Calling the endpoint
A custom client uses an agent key with the emk-
prefix. Create one from Manage agents & keys. Send JSON-RPC
requests to the HTTPS endpoint below. Use the method sequence for your protocol version.
# This legacy-compatible request returns the tool list. $ curl -X POST https://emisar.dev/api/mcp/rpc \ -H "Authorization: Bearer $EMISAR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
Use MCP tools from the shell
The MCP bridge also exposes all tools as a CLI. This lets any LLM use emisar in scripts too, without the usual limits of the MCP protocol.
Authenticate once — with emisar-mcp auth, or during the
bridge install
— and direct commands work with no environment exports. The credential is stored
in owner-only local state.
Run emisar-mcp
by itself to see one command reference grouped into Fleet, Actions, Runbooks,
Continuations, Accounts, and MCP client commands. list_tools
shows the live server catalog in the same operational groups. MCP clients start this
program automatically and send MCP requests through stdin.
$ emisar-mcp auth $ emisar-mcp list_tools $ emisar-mcp help find_actions $ emisar-mcp get_action --help
auth
opens the approval page in your browser. Choose an account there. The CLI stores a
dedicated key for that account and makes it current.
Use auth status
to inspect the current account and endpoint without contacting the control plane or
printing the key.
list_tools
fetches and groups the live catalog. help <tool>
shows the server-owned description and top-level arguments.
<tool> --help
is a shorthand for non-conflicting tool names. Add --json
to list_tools
or help
for exact descriptors and complete input schemas.
Authenticate each account once. accounts list
shows the accounts stored on this machine. It marks the current one with a star.
accounts use <slug-or-id>
changes the current account. A leading --account <slug-or-id>
uses another account for one command without changing the current one.
$ emisar-mcp auth $ emisar-mcp accounts list $ emisar-mcp accounts use immersive $ emisar-mcp --account blitz list_runners
--json
makes one logical tool invocation, follows no continuations, and prints the exact
structuredContent
object with no styling. The transport may resend that identical request once under the
same operation ID after a network failure. Put --json
last, pass -
to read one JSON object from stdin, and parse stdout as JSON. For conditional or
mutually exclusive arguments, use help <tool> --json
and treat that complete schema as authoritative.
Fleet#
These commands cover fleet inventory. Their default output is written for a person
scanning a terminal: runner records and compact pack summaries. Add --json
to receive the exact MCP structuredContent
object instead.
-
list_runnersshows connectivity, hostname, group, labels, packs, issues, and exactrunner_refvalues. JSON arguments can filter by status, runner name/group/host text, runner, pack, action, or issues. -
list_packsshows trusted versions, availability, action counts, issues, and exactpack_refvalues. The default includes executable packs; useavailability: "all"to diagnose trusted unavailable packs.
$ emisar-mcp list_runners $ emisar-mcp list_runners '{"statuses":["connected"]}' $ emisar-mcp list_packs $ emisar-mcp list_packs '{"availability":"all"}'
Actions#
find_actions
accepts a plain-text task and shows ranked matches. get_action
shows the selected action's side effects, trusted arguments, and compatible runner refs.
recent_runs
and get_operation
provide run history and mutation recovery.
Human run_action
prints the operation ID, then follows only exact wait_for_run
continuations tied to the returned runs. It waits on several runners concurrently and
shows approvals, terminal status, exit codes, and output. Ctrl-C stops waiting and exits
130; it does not cancel the action. The diagnostic prints a get_operation
command for later recovery.
$ emisar-mcp find_actions "postgres replication" $ emisar-mcp get_action '{"action_id":"postgres.status","pack_ref":"postgres@1.2.0/sha256:..."}' $ emisar-mcp recent_runs '{"scope":"own","limit":10}' $ emisar-mcp get_operation '{"operation_id":"op_..."}'
Runbooks#
list_runbooks
shows live releases, unpublished changes, and workflow size. get_runbook
shows inputs, stages, and action steps. Draft commands show the content digest, current
live release, and review link.
Human execute_runbook
follows only continuations tied to the returned execution, waits through approval or
expiry, and retrieves the available output for each action. It prints up to 16,384
characters per action and links to the run page when more remains. Ctrl-C stops
observation only. Add --json
when a script or LLM should receive the original execution response and decide whether
to call wait_for_run
itself.
Call an exact tool name with one JSON object. Omit the object when the tool accepts {}, or pass
-
to read it from stdin. Stdin keeps operational details out of the process list and works
cleanly with JSON-producing tools such as jq. Put --json
last when a script needs the exact structuredContent
object.
$ query='diagnose postgres replication' $ jq -cn --arg query "$query" '{query:$query}' \ | emisar-mcp find_actions - --json >actions.json $ jq -e '.candidates' actions.json
A successful tool call exits 0 and writes readable text, or exact JSON when
--json
is present. Tool, MCP, authentication, and post-send transport failures exit 1. Invalid
commands or JSON exit 2. In JSON mode, when the CLI cannot be sure whether the server received a
mutation, stdout includes data.operation_id. Recover it with
get_operation
before sending new work. Human run_action
exits 1 for any terminal status except success. Human
execute_runbook
exits 1 for halted
or cancelled. Policy, approvals, signed
dispatch, and audit still apply.
Tool and MCP error objects stay on stdout in the selected format, so a script can read
recovery data such as data.operation_id. A safe local diagnostic may also
appear on stderr. Configuration and usage failures write their diagnostic to stderr.
Color appears only in a terminal; pipes and redirected output stay plain. Set
NO_COLOR
to turn terminal color off. --json
output on stdout never contains styling.
When both authentication environment variables are absent, direct commands use the
current account. A leading --account <slug-or-id>
selects another stored account for that command. Use accounts list --json
to see immutable account IDs. Setting EMISAR_URL
and EMISAR_API_KEY
together overrides it for that command. Setting only one is an error.
Do not combine the pair with --account.
Stdio MCP clients never use direct-CLI account storage. Run emisar-mcp auth
and choose the account again if its credential expires or is revoked. Revoke connected keys from AI agents.
The local auth, accounts, help, and
list_tools
commands do not hide server tools with the same names. Prefix an exact tool call with --, for example emisar-mcp -- help '{}'.
MCP clients#
connect
detects the AI clients installed on this machine and writes the emisar server into
each one you choose; an already-connected client is left alone. disconnect
removes that entry and does not change anything else in those files.
-
--client <id>names one client; repeatable.--alltakes every detected client without asking. -
--auto-permit(connect only) also turns off the client's own "allow this tool?" prompt for the emisar server alone — emisar still decides every call server-side. Claude Code, Gemini CLI, Codex CLI, and Grok CLI support this per-server setting. -
--forget(disconnect only) also deletes the stored direct-CLI accounts. A key removed from a config keeps working until you revoke it under AI agents.
$ emisar-mcp connect --all --auto-permit $ emisar-mcp disconnect --client cursor
Protocol methods
emisar serves three protocol revisions on one stateless endpoint — it does not issue an
Mcp-Session-Id
or require sticky routing.
| Revision | Start | Other methods | Cancellation |
|---|---|---|---|
2026-07-28 |
server/discover |
tools/list, tools/call
|
No protocol cancellation method. |
2025-11-252025-06-18
|
initialize |
ping, tools/list, tools/call
|
notifications/cancelled
stops observation and does not send a response.
|
A 2026-07-28
request declares its version in _meta. It also sends
MCP-Protocol-Version
and Mcp-Method
headers. A tools/call
request also sends Mcp-Name. Modern results include resultType
and cache hints.
Request IDs are strings or integers and are returned with the same JSON type. A frame without an ID is a notification, and a mutation sent as one never runs — a mutation needs a request ID for safe recovery.
Thirteen fixed tools
Actions are data, not MCP tool descriptors. Runner and pack changes appear in discovery results. Clients never need a hand-maintained action allowlist and never load hundreds of action schemas during setup.
| Tool | Purpose |
|---|---|
| list_packs | List currently trusted in-scope packs with bounded action summaries and deployment issues. |
| list_runners | Inspect runner connectivity, compatibility, and exact runner refs. |
| find_actions | Find a bounded set of actions by task or exact identifiers. |
| get_action | Read one immutable action contract and its compatible runners. |
| run_action | Dispatch one exact action to one or more selected runners. |
| get_operation | Recover one exact mutation after an ambiguous response. |
| wait_for_run | Wait for one run to change or reach a terminal state. |
| recent_runs | Inspect scoped run history with exact identity filters. |
| list_runbooks | List every runbook with its live release and any unpublished change. |
| get_runbook | Read one runbook's live release, or its unpublished change. |
| execute_runbook | Execute the live release, or an explicitly allowed unpublished change. |
| create_runbook_draft | Start a new runbook as an unpublished change. |
| update_runbook_draft | Replace a runbook's unpublished change without changing its slug. |
Discovery
Start broad only when the task needs it. list_packs
gives a bounded overview. find_actions
ranks candidates. get_action
returns the exact argument schema and the current compatible runner refs. Use
list_runners
for fleet
diagnosis or target selection. Pack and runner pages default to 15 items. Continue a list
by passing the previous response's next_cursor
(find_actions returns a ready-to-send next
call), and a cursor is bound to the identical query — a mismatched one is rejected.
availability: "executable"
is the normal path and omits capabilities that
cannot run anywhere in scope. availability: "all"
is diagnostic: it preserves
trusted but unavailable packs and actions together with deployment issues such as no
connected runner, a missing executable, or a descriptor mismatch. Pending, rejected,
revoked, and retirement-blocked pack refs are operator-only facts that discovery
omits — when an expected ref is absent, review it in Packs.
A pack_ref
has the immutable form pack@version/sha256:hash. A runner_ref
has the identity-bound form name~sha256-prefix. Copy both exactly from
discovery: a reconnect or reinstall that reuses the same external ID keeps the ref,
while a new external ID or a pack change can make a selection stale.
Running an action
run_action accepts one explicit execution intent:
| Field | Requirement | Meaning |
|---|---|---|
| action_id | Required | Exact action returned by get_action. |
| pack_ref | Required | Immutable pack generation returned by discovery. |
| runner_refs | Required | One to sixteen distinct, explicit targets. |
| args | Required | One JSON object validated against the trusted action schema. |
| reason | Required | Operator-facing audit reason for this execution. |
| evidence | Optional | What you already observed that makes the action necessary — prior findings or run ids. |
| expected | Optional | The outcome you expect if it works. It is stored with the run and shown to approvers. |
| wait | Optional | Duration string: "0", "1s"–"60s", or "1ms"–"60000ms". The default and maximum are 60s. |
Dispatch preflight re-resolves the complete target set, pack trust, descriptor, schema,
policy, certificate window, and signature requirement, and creates the complete
fan-out or nothing — it never silently drops a stale target. Policy can still
produce a mix of allowed, pending-approval, and denied runs inside the accepted
operation. A parked run is not an error: it comes back in a successful response with
run status pending_approval, an approval URL, and a
wait_for_run
continuation — wait on it rather than treating it as a failure.
Operations and recovery
Every run_action, execute_runbook, create_runbook_draft, and
update_runbook_draft
receives a private operation_id. The stdio bridge derives it from its private
process nonce and request sequence. For native HTTP clients, the portal derives it from
the exact request and credential lineage. Automatic rotation successors share that
lineage. The portal reserves the operation ID in the same transaction as the mutation.
Repeating the same request and facts returns the original resource; changed facts
conflict. Models never create operation identifiers.
A successful mutation returns its operation ID. If the bridge loses an HTTP response, its
correlated JSON-RPC error includes that same ID. After a mutation, pass it to
get_operation
before deciding whether to issue new work. Read calls retry normally. Run history
is for investigation — it is not a substitute for exact operation recovery.
Waits and cancellation
run_action
waits up to 60 seconds. wait_for_run
waits up to 60 seconds and returns the current state when its deadline expires. One
credential lineage can hold eight waits per portal node; the stdio bridge keeps up
to eight requests in flight on a 90-second HTTP deadline, and other calls stay
responsive during a wait. Legacy clients can also use ping.
Cancellation stops observation only — it does not undo committed infrastructure
work.
Runbooks and signing
A runbook is one slug carrying a live release, slug@N, and at most one
unpublished change. Discovery returns the live release by default. To edit an existing
runbook, read it, then call update_runbook_draft
with the definition_sha256
you read. The slug never changes, the edit lands as the runbook's one unpublished
change, and a concurrent write answers draft_changed
instead of being overwritten.
Execution runs the live release. An older slug@N
answers not_live
rather than silently running current content. An unpublished change never enters ordinary
discovery or default execution. Read it with status: "draft", then pass the
slug to execute_runbook
with allow_draft: true
and the exact definition_sha256
you consent to run. A draft test uses the same target, trust, policy, approval,
scheduler, runner, and audit path as a live execution — it can cause the actions'
real effects, and it publishes nothing: publication remains a human action in Runbooks.
A definition can contain 32 inputs, 16 stages, and 16 target refs per step within 64 KiB.
A resolved execution can contain 256 items. Execution returns after the durable execution
and its initial stage state commit. The response groups exact items under stages and shows
extracted output and condition evidence, wait state, stable blocking reason, and only the
latest physical attempt. Follow its wait_for_run
continuation while it remains active. Use recent_runs
for paginated attempt history.
Customer-CA signing currently protects only run_action. A runbook that targets
a signature-enforcing runner fails closed with signed_runbook_unsupported.
Errors
| Code | Meaning | Next step |
|---|---|---|
| target_contract_changed | The selected action, pack, or runner generation is stale. | Call get_action again and make a fresh selection. |
| action_unavailable | No currently visible exact action contract is executable. | Follow the returned list_runners continuation to see why no runner can execute it. |
| operation_conflict | One operation ID was reused with different facts. | Treat it as a security error. Do not retry. |
| signed_runbook_unsupported | A runbook includes signature-enforcing targets. | Use individually signed run_action calls. |
These codes are the contract. When you have a symptom instead of a code — a client that cannot connect, an outdated bridge, a refused tool call — start from troubleshooting, which routes back here once it identifies the problem.
Approval gates, denials, decisions, and terminal results are recorded. See the audit trail and policies & approvals.
MCP methods and tool inputs are v1 compatibility surfaces. Read Compatibility and deprecation.