Skip to content
Ironflow is pre-1.0: APIs can change between releases. Pin a version. What is ready →

MCP Server

Ironflow includes a built-in Model Context Protocol (MCP) server. AI assistants can retrieve API contracts and handler authoring guides, list runs, inspect step outputs, emit events, and query projections directly from the IDE.

Add to your project’s .mcp.json:

{
"mcpServers": {
"ironflow": {
"command": "ironflow",
"args": ["mcp", "--allow-writes"]
}
}
}

Add to your MCP settings (Settings → MCP):

{
"ironflow": {
"command": "ironflow",
"args": ["mcp", "--allow-writes"]
}
}

If the Ironflow server is running on a different port or host:

{
"mcpServers": {
"ironflow": {
"command": "ironflow",
"args": ["mcp", "--allow-writes", "--server-url", "http://localhost:9000"]
}
}
}

For servers with auth enabled, give the MCP server an API key through the IRONFLOW_API_KEY environment variable:

{
"mcpServers": {
"ironflow": {
"command": "ironflow",
"args": ["mcp", "--allow-writes"],
"env": { "IRONFLOW_API_KEY": "${IRONFLOW_API_KEY}" }
}
}
}

Claude Code expands ${IRONFLOW_API_KEY} from your shell, so the key does not go into the file. Do not put the key in args with --api-key: other users on the machine can read it in the process list, and .mcp.json is often committed.

By default, the MCP server starts in read-only mode with 27 tools for observation and documentation. Add --allow-writes to enable 16 tools that modify state (emitting events, invoking functions, writing to KV store, etc.), for 43 core tools. Pairing --allow-writes with --evidence-file also registers ironflow_await_reload, a cooperative gate that blocks until the dev server re-registers your functions past the version your last test event hit — call it after editing function code and before emitting a verification event. A plain --allow-writes session (no evidence file) does not expose it.

Read-only mode does not expose application tools. Their effects are defined by your application, so the server cannot promise that they only read.

The mode controls which tools the MCP server registers. It does not limit the API key. The engine authorizes every call by the key’s role, so give the agent a key whose role grants only the access it needs.

Six tools change data permanently: ironflow_delete_run, ironflow_delete_stream, ironflow_redact_event, ironflow_redact_run, ironflow_redact_step and ironflow_outbox_dlq_discard.

  • Your client supports form elicitation: the server asks you to confirm before it changes anything. The question names the operation and its target. The server ignores the confirm argument, because the model writes the arguments and content in its context can set them. If you decline, nothing changes.
  • Your client does not support elicitation: the call needs confirm: true. Agents set it only after they show you the target and you agree.

Over streamable HTTP, a client on protocol version 2026-07-28 or later can be asked. An earlier client cannot be asked over that transport, so it uses confirm: true. URL-only elicitation does not support this confirmation form and also uses the confirm: true fallback. An accepted form must contain the boolean confirm: true; a declined or cancelled form, an unticked checkbox or a string value never confirms.

On legacy stdio, a pending question waits for the response or request cancellation; the server adds no confirmation timeout. On protocol 2026-07-28 or later, the server returns an input request and the client answers in a retry. The answer is bound to the tool, target and server process, so changing the target or restarting the server requires a new confirmation.

ironflow_kv_put and ironflow_secret_set overwrite a value and are marked destructive, but the server does not ask about them. Your client’s own approval prompt, which follows destructiveHint, covers them.

Every tool has a title and the four behaviour hints: readOnlyHint, destructiveHint, idempotentHint and openWorldHint. Clients use the hints to decide when to ask you before a call. Read tools are read-only. Deletes, redactions and overwrites are destructive. Tools that run your code (emit, invoke, append, resume, rebuild) are destructive and open-world, because the engine cannot limit what your code does. Application tools keep the conservative defaults.

Every read tool that returns engine data (all of them except ironflow_get_docs) declares an outputSchema and returns structuredContent with its JSON text. When the engine returns a JSON array (projects, environments, secrets, circuit breakers), structuredContent wraps it as {"items": [...]}. The text block keeps the engine’s array. Tool text is compact JSON.

Results can contain data that other parties wrote: event payloads, step inputs and outputs, KV values and application tool descriptions. The server instructions and the tool descriptions tell the model to treat that content as data, not instructions.

Every request using protocol 2026-07-28 or later must include params._meta["io.modelcontextprotocol/clientCapabilities"]. Send {} when the client has no optional capabilities. Missing, null or malformed declarations return JSON-RPC -32602 (Invalid params) before the tool runs. Over HTTP, the response status is 400. Clients that previously handled -32021 for a missing declaration or -32020 for a malformed one must handle -32602 instead.

To receive confirmation forms, declare {"elicitation":{"form":{}}}. A bare {"elicitation":{}} declaration also supports forms for older clients. Over HTTP, the protocol version in _meta must match Mcp-Protocol-Version, and Mcp-Method and Mcp-Name must match the request. Earlier protocol versions keep their initialize-time capability negotiation.

Legacy HTTP handling rejects a nonempty MCP-Protocol-Version header naming an unsupported version with HTTP 400 and an “Unsupported protocol version” message before handling non-initialize POST, GET or DELETE requests. Missing headers remain allowed on that legacy path, and initialize requests still negotiate a version. Modern requests use their separate metadata and header validation.

When asking an agent to write a handler without an SDK, have it retrieve the push protocol and function authoring guide first. For REST calls to the engine, use the OpenAPI resource. The three resources and ironflow_get_docs are always available, including in read-only mode and with --static-only, over stdio or streamable HTTP.

Resource URIMIME typeironflow_get_docs topicSource
ironflow://docs/openapiapplication/jsonopenapiConfigured engine’s authenticated GET /api/v1/openapi.json
ironflow://docs/push-protocoltext/markdownpush-protocolBundled push wire protocol
ironflow://docs/function-authoringtext/markdownfunction-authoringBundled SDKless handler guide

The OpenAPI document describes REST only: it excludes ConnectRPC methods and push callbacks and is not the complete Ironflow API contract. Each read fetches the configured engine’s document using your API key. Fetch failures return an error. The Markdown guides ship with the MCP binary and work offline; their version may differ from your engine or the current documentation website.

A host that exposes resources can read one directly:

{"jsonrpc":"2.0","id":1,"method":"resources/read","params":{"uri":"ironflow://docs/push-protocol"}}

For hosts that expose only tools, call ironflow_get_docs. Its required topic accepts exactly openapi, push-protocol, or function-authoring:

{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"ironflow_get_docs","arguments":{"topic":"function-authoring"}}}

The tool returns a source note in its first text block and the full document in its second text block.

Five resource templates read one record each, in every mode. Each returns compact JSON (application/json), with the same data as the matching tool.

URI templateSame data as
ironflow://runs/{run_id}ironflow_get_run
ironflow://runs/{run_id}/stepsironflow_get_run_steps
ironflow://functions/{function_id}ironflow_get_function
ironflow://streams/{entity_id}ironflow_read_entity_stream
ironflow://projections/{name}ironflow_projection_status

Percent-encode a / in an ID, for example ironflow://streams/tenant%2F42.

The full tool list, by mode, is in the MCP tools reference.

A failed tool call returns isError: true and one JSON object as its text:

{"error": "run \"run-1\" not found", "code": "not_found", "retryable": false, "hint": "Check the ID. List the candidates first."}

code is the lowercase ConnectRPC code. Retry the same call only when retryable is true, after retry_after seconds when present. A missing or wrong-typed argument is invalid_argument. A destructive tool that was not confirmed is failed_precondition with reason confirmation_required, confirmation_declined or confirmation_mismatch. Errors from SDK-registered application tools keep their own [code] message text.

With the MCP server configured, you can ask your AI assistant to:

  1. “What functions are registered?” → calls ironflow_list_functions
  2. “Emit an order.placed event” → calls ironflow_emit_event
  3. “Show me what happened in that run” → calls ironflow_get_run + ironflow_get_run_steps
  4. “What’s the current state of the order-stats projection?” → calls ironflow_projection_status
  5. “Query the database for failed runs in the last hour” → calls ironflow_sql_query
You: "List recent failed runs"
AI: → ironflow_list_runs(status: "failed")
"Run run_abc123 failed at step 'charge-payment'"
You: "Show me what happened step by step"
AI: → ironflow_get_run_steps(run_id: "run_abc123")
"Step 1 (validate-order) succeeded with output {...}
Step 2 (charge-payment) failed: 'Payment gateway timeout'"
You: "What event triggered this?"
AI: → ironflow_list_events
"Event order.placed with data {orderId: 'ord-456', total: 99.99}"
Terminal window
ironflow mcp # Read-only mode
ironflow mcp --allow-writes # Enable write operations
ironflow mcp --server-url http://host:port # Custom server URL
IRONFLOW_API_KEY=ifkey_xxx ironflow mcp # API key authentication
IRONFLOW_MCP_BEARER_TOKEN=... ironflow mcp --transport=streamable-http --port=0 # Authenticated loopback HTTP
FlagDefaultDescription
--allow-writesfalseEnable write operations and application tools
--server-urlIRONFLOW_SERVER_URL, else http://localhost:9123Ironflow server URL
--api-key""API key. Prefer IRONFLOW_API_KEY: a flag value is visible in the process list
--transportstdiostdio or authenticated streamable-http
--host127.0.0.1Loopback HTTP bind host
--port0HTTP port (0 chooses an ephemeral port)
--port-file""Write the bound port as mode-0600 JSON
--evidence-file""Append a JSONL trail of every tool call and resource read to this file. See Evidence trail
--static-onlyfalseExclude SDK-registered application tools (read-only mode never registers them)
--terminal-bridge-url""Loopback URL of Ironflow Desktop’s terminal bridge. Adds ironflow_terminal_run and ironflow_terminal_read. Needs --allow-writes and IRONFLOW_TERMINAL_BRIDGE_TOKEN

--evidence-file appends one JSON line when a tool is called or a resource is read, and one when it returns. A shared id pairs them. The file is created with mode 0600; treat it as sensitive.

  • Credentials are masked. The ironflow_secret_set value is always masked. Credentials in well-known formats are masked wherever they appear, in arguments and in results: Ironflow API keys, Stripe, OpenAI, Anthropic, AWS, GitHub, Slack and Google keys, JWTs, Bearer tokens, PEM private keys and URL passwords. The prefix stays, for example sk_live_***. A credential in no known format, such as a plain password stored as a KV value, is not detected.
  • Confirmations are linked. A confirmed irreversible call writes two call records. The result of the first has "inputRequired": true. The second has "answers", the id of the first, and "inputResponses", the user’s answer, for example {"confirm": {"action": "accept", "content": {"confirm": true}}}.
  • Resource reads are recorded. A call record has "resource" (the URI) instead of "tool". Reads of the ironflow://docs documents record the size, not the content.

--transport=streamable-http is for local supervisors such as Ironflow Desktop:

  • It binds only to a loopback address and needs IRONFLOW_MCP_BEARER_TOKEN.
  • It refuses a request whose Host header is not a loopback name, which blocks DNS rebinding.
  • It refuses a request whose Origin header is not a loopback origin. Clients that send no Origin (CLIs, agent runtimes) are not affected.

The bearer token protects only the MCP endpoint. The server calls the engine with its own API key and never forwards the token.

Set IRONFLOW_MCP_EXIT_ON_STDIN_CLOSE=1 and the process exits when its stdin closes, so a supervisor that holds a stdin pipe takes it down when the supervisor dies.

A hosted agent with no local ironflow binary can reach the engine over MCP. Turn on spec.mcp.remote (or IRONFLOW_MCP_REMOTE=true) and the engine serves MCP at /api/v1/mcp. The agent sends its own API key, and every tool call runs with that key’s permissions. See Remote MCP.

The MCP server provides runtime tools (the agent’s “hands”), while AI Skills provide structured knowledge and workflows (the agent’s “brain”). The ops skill automatically uses MCP tools when available and falls back to CLI when not. For the best AI experience, use both.

With --allow-writes and without --static-only, the MCP server discovers and registers dynamic agent tools at startup. These are tools registered by SDK clients via the AgentToolsService/RegisterTool RPC on the running ironflow serve process.

  • The MCP CLI calls ListTools at startup to fetch SDK-registered tools
  • Each returned tool is registered as a dynamic MCP tool
  • Invocations are forwarded to the Ironflow server via InvokeTool
  • This is best-effort — if the server is unreachable, static tools still work
  • Restart the MCP CLI to pick up newly registered agent tools. The server does not send tools/list_changed, and it does not advertise that it can
  • Each description starts with [Application tool ...], because your application wrote it

This means your effective MCP tool count may be higher than the static tool set. The MCP tools reference lists 43 core tools (27 read, 16 write); ironflow_await_reload is the 44th and needs --evidence-file as well. Ironflow Desktop’s optional terminal bridge adds ironflow_terminal_run and ironflow_terminal_read separately from those counts.