Claude Desktop drops the MCP tool argument named `session_id` (only that token) from tools/call

Status Open
Maintainer reply None cached
Activity 4 comments · opened Jul 13, 2026

Summary

Claude Desktop omits the tool-call argument named session_id when invoking MCP tools over a Streamable-HTTP server (via the mcp-remote stdio bridge). The argument is dropped before it reaches the bridge — and only the token session_id is affected; sibling string arguments on the same server (generation_id, image_id, category_id, name) are delivered correctly. This looks like a collision between the tool argument name session_id and the Streamable-HTTP transport's own session identifier (Mcp-Session-Id).

Environment

  • Claude Desktop → mcp-remote 0.1.37 → stateless Streamable-HTTP MCP server
  • Server SDK: @modelcontextprotocol/sdk 1.26.0 via mcp-handler 1.1.0 (SSE disabled)
  • Observed: 2026-07-12

Actual behavior

A tool with a required session_id: z.string() receives undefined:

MCP error -32602: Input validation error: Invalid arguments for tool get_session:
[ { "code": "invalid_type", "expected": "string", "received": "undefined", "path": ["session_id"], "message": "Required" } ]

When session_id is optional, it is simply absent, so the tool falls back to its default (e.g. auto-creating a new session) — making it look like the passed id is silently ignored.

Expected behavior

The session_id argument the model supplies in a tools/call should reach the MCP server verbatim, like any other argument.

Proof it is the client, not the server or the bridge

The identical call succeeds through every non-Desktop client against the same live server:

| Client path | session_id delivered? |
|---|---|
| Raw MCP SDK Client, in-memory transport | ✅ |
| MCP SDK Client over real HTTP (StreamableHTTPClientTransport) | ✅ |
| mcp-remote 0.1.37 driven by a scripted stdio client | ✅ |
| Claude Desktop (same mcp-remote bridge) | ❌ dropped |

mcp-remote forwards the exact JSON-RPC it receives (verified via its [Local→Remote] tools/call logs — the arguments are intact when it is driven by a non-Desktop client), so the argument is dropped upstream of the bridge, inside Claude Desktop.

Minimal reproduction

Register two tools that differ only in argument name, each echoing its arguments:

server.registerTool("t_session",    { inputSchema: { session_id: z.string() } },    echoArgs);
server.registerTool("t_generation", { inputSchema: { generation_id: z.string() } }, echoArgs);

From Claude Desktop, ask it to call each tool with a value. t_generation receives { generation_id: "..." }; t_session receives {} (→ -32602 … received: undefined, path: session_id).

Impact

Any MCP tool whose input uses an argument literally named session_id is uncallable from Claude Desktop. session_id is an extremely common tool-parameter name, so this silently breaks many third-party MCP servers with no error surfaced to the user (for optional params it just does the wrong thing).

Suspected cause

The tool argument name session_id collides with the MCP Streamable-HTTP transport's protocol-level session identifier (Mcp-Session-Id header / sessionId). Related session-id handling issues: #41836, #48557, #99, #27425.

View original on GitHub ↗

3 Comments

jgilfoil · 3 months ago

Update: confirmed via client-side capture — it's the remote-devices bridge

A user reproduced this from Claude Desktop / Cowork and captured the client-side request/response, which pins down the strip precisely:

  • The connector is the remote-devices MCP bridge (tools are surfaced as mcp__remote-devices__imggen__*).
  • The client's own request panel showed the argument was sent:

``json
{ "session_id": "e3bdbf73-afa1-4c22-8eaf-16a35c967c4b" }
``

  • ...but the server received undefined and rejected it:

``
MCP error -32602: Input validation error: Invalid arguments for tool list_approved_images:
[ { "code": "invalid_type", "expected": "string", "received": "undefined", "path": ["session_id"], "message": "Required" } ]
``

  • In the same session, get_generation({generation_id}) and get_image({image_id}) reached the server fine (returned not_found for a probe id).

So the argument named session_id is present in the client's outbound call and absent by the time it reaches the server — the remote-devices bridge is dropping it, almost certainly a collision with the transport's own session id (Mcp-Session-Id). Renaming the tool parameter to anything else (session_name, gen_session_id) is delivered normally, which is the workaround we've shipped on our side — but it means every third-party server using the very common session_id parameter name is silently broken from Desktop.

nicotem · 1 month ago

Also for me: The remote-devices bridge strips any MCP tool argument named session_id before it reaches the server — the server receives the call with that key missing while sibling arguments arrive intact (verified differentially; the same calls work over direct stdio). Presumably the bridge reserves session_id for its own routing and pops it from tool arguments. This silently breaks third-party MCP servers that use that parameter name.

lukeeexd · 1 month ago

Seeing the same behaviour from a different client and a different transport chain,
which suggests this isn't specific to Claude Desktop or to mcp-remote.

Setup: a Claude Code / Cowork cloud agent session calling
CrowdStrike/falcon-mcp tools, proxied
through the Claude desktop bridge to a locally-hosted server — no mcp-remote, and
not a Streamable-HTTP server of my own. Server-side validation here is Pydantic
rather than Zod.

Same symptom. falcon_execute_rtr_read_only_command(session_id, base_command,
command_string)
:

1 validation error for execute_read_only_commandArguments
session_id
Field required [type=missing, input_value={'base_command': 'ls',
'command_string': '...'}, input_type=dict]

The sibling arguments arrive. session_id does not.

Cleanest evidence is falcon_delete_rtr_session, whose only parameter is
session_id:

1 validation error for delete_sessionArguments
session_id
Field required [type=missing, input_value={}, input_type=dict]

input_value={} — the arguments object arrives empty. Worth noting it's a missing
key rather than a null: Pydantic would report type=string_type if it received
None. That matches your Zod received: "undefined" from an entirely different
validation stack.

Parameters that pass fine against the same server, several of them on the same
tools:
ids (an array of RTR session IDs — so it's the exact key name that's
affected, not the concept), cloud_request_id, device_id, filter, limit,
sort, offset.

Practical impact: the schema marks session_id required and the transport
refuses to carry it, so no valid invocation of the tool exists. Because the failure
surfaces as a server-side validation error, the natural reading is that the server
is at fault — I spent a while suspecting falcon-mcp before isolating it to the client.
For this server it means Real Time Response command execution is unreachable from an
agent session, which is a meaningful gap in an incident-response workflow.

Given it reproduces outside Desktop, would you reconsider the invalid label?

Showing cached comments. Read the full discussion on GitHub ↗