- AI & Agents
- MCP Server
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.
Claude Code
Section titled “Claude Code”Add to your project’s .mcp.json:
{ "mcpServers": { "ironflow": { "command": "ironflow", "args": ["mcp", "--allow-writes"] } }}Cursor
Section titled “Cursor”Add to your MCP settings (Settings → MCP):
{ "ironflow": { "command": "ironflow", "args": ["mcp", "--allow-writes"] }}Custom Server URL
Section titled “Custom Server URL”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"] } }}Authentication
Section titled “Authentication”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.
Read-Only vs Read-Write Mode
Section titled “Read-Only vs Read-Write Mode”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.
Confirming irreversible operations
Section titled “Confirming irreversible operations”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
confirmargument, 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.
Tool metadata
Section titled “Tool metadata”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.
Protocol 2026-07-28 client capabilities
Section titled “Protocol 2026-07-28 client capabilities”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.
API contracts and authoring guides
Section titled “API contracts and authoring guides”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 URI | MIME type | ironflow_get_docs topic | Source |
|---|---|---|---|
ironflow://docs/openapi | application/json | openapi | Configured engine’s authenticated GET /api/v1/openapi.json |
ironflow://docs/push-protocol | text/markdown | push-protocol | Bundled push wire protocol |
ironflow://docs/function-authoring | text/markdown | function-authoring | Bundled 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.
Records as resources
Section titled “Records as resources”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 template | Same data as |
|---|---|
ironflow://runs/{run_id} | ironflow_get_run |
ironflow://runs/{run_id}/steps | ironflow_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.
Available Tools
Section titled “Available Tools”The full tool list, by mode, is in the MCP tools reference.
Errors
Section titled “Errors”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.
Example Workflows
Section titled “Example Workflows”Building an App with AI
Section titled “Building an App with AI”With the MCP server configured, you can ask your AI assistant to:
- “What functions are registered?” → calls
ironflow_list_functions - “Emit an order.placed event” → calls
ironflow_emit_event - “Show me what happened in that run” → calls
ironflow_get_run+ironflow_get_run_steps - “What’s the current state of the order-stats projection?” → calls
ironflow_projection_status - “Query the database for failed runs in the last hour” → calls
ironflow_sql_query
Debugging a Failed Run
Section titled “Debugging a Failed Run”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}"CLI Reference
Section titled “CLI Reference”ironflow mcp # Read-only modeironflow mcp --allow-writes # Enable write operationsironflow mcp --server-url http://host:port # Custom server URLIRONFLOW_API_KEY=ifkey_xxx ironflow mcp # API key authenticationIRONFLOW_MCP_BEARER_TOKEN=... ironflow mcp --transport=streamable-http --port=0 # Authenticated loopback HTTP| Flag | Default | Description |
|---|---|---|
--allow-writes | false | Enable write operations and application tools |
--server-url | IRONFLOW_SERVER_URL, else http://localhost:9123 | Ironflow server URL |
--api-key | "" | API key. Prefer IRONFLOW_API_KEY: a flag value is visible in the process list |
--transport | stdio | stdio or authenticated streamable-http |
--host | 127.0.0.1 | Loopback HTTP bind host |
--port | 0 | HTTP 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-only | false | Exclude 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 trail
Section titled “Evidence trail”--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_setvalue 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,Bearertokens, PEM private keys and URL passwords. The prefix stays, for examplesk_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", theidof 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 theironflow://docsdocuments record the size, not the content.
HTTP transport security
Section titled “HTTP transport security”--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
Hostheader is not a loopback name, which blocks DNS rebinding. - It refuses a request whose
Originheader is not a loopback origin. Clients that send noOrigin(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.
Remote MCP
Section titled “Remote MCP”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.
Pair with AI Skills
Section titled “Pair with AI Skills”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.
Dynamic Agent Tools
Section titled “Dynamic Agent Tools”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
ListToolsat 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.