IronFlowDocs

mcp_client

Stateful client for the stable MCP 2025-11-25 contract over stdio or Streamable HTTP.

initialize performs the complete MCP handshake atomically: it sends the initialize request, validates the negotiated protocol and capabilities, and sends notifications/initialized. It returns an opaque IronFlow session handle in {output_key}_session. Pass that handle to later list_tools, call_tool, and close actions. The server's HTTP session ID and JSON-RPC envelopes are kept inside the client and are not written to workflow context.

Parameters

Common parameters

actionstringdefault "initialize"
One of initialize, list_tools, call_tool, or close.
output_keystringdefault "mcp"
Prefix for output context keys.
timeoutnumberdefault 30
Positive per-action timeout in seconds. A shorter enclosing step timeout wins.
sessionstringfor non-initialize actionsdefault none
Opaque {output_key}_session returned by initialize.

Initialization parameters

Initialization reads the following parameters. The params.name and params.arguments fallbacks described under tool calls are the only later use of params; transport configuration is initialization-only.

ParameterTypeRequiredDefaultDescription
transportstringno"stdio"stdio or streamable_http. http is accepted as an alias; the obsolete sse name is rejected.
protocol_versionstringno"2025-11-25"Must be 2025-11-25, the stable revision supported by this build.
client_namestringno"ironflow"Client implementation name sent during initialization.
client_versionstringnocrate versionClient implementation version sent during initialization.
paramsobjectno{}Optional initialization fields. protocolVersion, capabilities, and clientInfo are validated against the supported MCP model.
commandstringstdio onlynoneExecutable used to start the MCP server.
argsstring arrayno[]Arguments passed to the stdio server.
envstring mapno{}Environment variables passed to the stdio server.
cwdstringnocurrent directoryWorking directory for the stdio server.
urlstringStreamable HTTP onlynoneMCP Streamable HTTP endpoint.
headersstring mapno{}Additional HTTP headers, such as Authorization. Transport-managed headers cannot be overridden.

IronFlow rejects custom Accept, Content-Type, MCP-Session-Id, and MCP-Protocol-Version headers. The transport owns these fields so callers cannot weaken content negotiation, corrupt response correlation, or substitute another server session.

Tool-call parameters

ParameterTypeRequiredDefaultDescription
tool_namestringyes*noneTool to invoke. params.name is accepted as a fallback.
argumentsobjectno{}Tool arguments. Non-object values are rejected. params.arguments is accepted as a fallback.

*Required for action = "call_tool".

list_tools and call_tool also require the server to have negotiated the MCP tools capability.

Session lifecycle

  • initialize creates one isolated session and returns its opaque handle.
  • A stdio session keeps the same child process and pipes for the entire MCP lifecycle. JSON-RPC frames are newline-delimited, strictly correlated, and bounded by IRONFLOW_MAX_SHELL_OUTPUT_BYTES in each direction. Server stderr remains attached as logging and is not interpreted as a protocol response.
  • Partial stdio frames remain buffered while IronFlow writes outgoing messages, including replies to server pings. The input size limit applies to the whole accumulated frame, including its newline delimiter, not each read fragment. Writing an outgoing message does not itself cancel the MCP operation or invalidate its session. EOF before the delimiter or an input error fails the transport and invalidates the affected operation's session.
  • A Streamable HTTP session retains its negotiated version, transport-managed headers, and server-issued session ID internally. Request responses may be application/json or text/event-stream as defined by MCP 2025-11-25.
  • Sessions are process-local. A handle persisted in run context cannot restore a session after IronFlow restarts.
  • Call close after the final operation. For stdio, shutdown closes stdin, waits for the server, and escalates process termination if necessary. The session handle is invalid after close.
  • A timed-out, failed, or cancelled operation invalidates its session and cleans up the owned transport. A completed external tool side effect cannot be rolled back.
  • IRONFLOW_MCP_SESSION_CACHE_SIZE and IRONFLOW_MCP_SESSION_TTL_SECS bound abandoned sessions as a safety net. Idle expiry is swept when another session is inserted or leased; it is not a replacement for close.

Context output

Every successful action writes:

  • {output_key}_transport — "stdio" or "streamable_http".
  • {output_key}_action — resolved action.
  • {output_key}_session — opaque IronFlow session handle. The close result reports the handle that was closed; it is no longer usable.
  • {output_key}_result — typed action result, without a raw JSON-RPC envelope.
  • {output_key}_success — true.

Action-specific output:

  • initialize
    • {output_key}_protocol_version
    • {output_key}_capabilities
    • {output_key}_server_info — the server's implementation identity, or null when the server does not supply one (the MCP handshake treats it as optional)
  • list_tools
    • {output_key}_tools
    • {output_key}_tool_names
    • {output_key}_tool_count
  • call_tool
    • {output_key}_tool_name
    • {output_key}_tool_result
    • {output_key}_tool_content
    • {output_key}_tool_text — text content blocks joined with newlines, or null when no text block is present.
  • close
    • {output_key}_closed — true.

Raw JSON-RPC request IDs, request and response envelopes, and server-issued HTTP session IDs are intentionally not exposed.

Example: persistent stdio session

local flow = Flow.new("mcp_stdio_demo")

flow:step("initialize", nodes.mcp_client({
    transport = "stdio",
    command = "python3",
    args = { "examples/17-mcp/mcp_stdio_mock.py", "--mode", "fragmented-ping" },
    action = "initialize",
    output_key = "mcp_init"
}))

flow:step("list_tools", nodes.mcp_client({
    action = "list_tools",
    session = "${ctx.mcp_init_session}",
    output_key = "mcp_tools"
})):depends_on("initialize")

flow:step("call_tool", nodes.mcp_client({
    action = "call_tool",
    session = "${ctx.mcp_init_session}",
    tool_name = "search",
    arguments = { query = "IronFlow MCP integration" },
    output_key = "mcp_call"
})):depends_on("list_tools")

flow:step("close", nodes.mcp_client({
    action = "close",
    session = "${ctx.mcp_init_session}",
    output_key = "mcp_close"
})):depends_on("call_tool")

return flow

Example: Streamable HTTP

local flow = Flow.new("mcp_streamable_http_demo")

flow:step("initialize", nodes.mcp_client({
    transport = "streamable_http",
    url = env("MCP_STREAMABLE_HTTP_URL"),
    action = "initialize",
    headers = {
        Authorization = "Bearer " .. env("MCP_TOKEN")
    },
    output_key = "mcp_remote_init"
}))

flow:step("list_tools", nodes.mcp_client({
    action = "list_tools",
    session = "${ctx.mcp_remote_init_session}",
    output_key = "mcp_remote_tools"
})):depends_on("initialize")

flow:step("close", nodes.mcp_client({
    action = "close",
    session = "${ctx.mcp_remote_init_session}",
    output_key = "mcp_remote_close"
})):depends_on("list_tools")

return flow

IronFlow documentation

Find the next step

Type a keyword to search the documentation.

↑ ↓ to move · Enter to open · Esc to close