Skip to content

One deeply nested MCP tool argument closes the entire stdio session #18

@Regis-RCR

Description

@Regis-RCR

TL;DR: A single tools/call whose arguments object is nested deeply enough does not just fail that one call: the whole memtrace mcp stdio session closes. The offending call gets no response, and every later request on the same connection also goes unanswered until the client reconnects. The boundary is sharp and reproducible: nesting depth 125 returns a normal error and the session keeps working, depth 126 ends it.

Environment

  • memtrace 0.6.11, macOS arm64
  • memtrace mcp stdio server, one client connection

Repro

After initialize, send a tools/call whose arguments nests N levels deep, then send any normal follow-up call as a liveness probe:

1. tools/call  name=find_symbol  arguments={"x":{"x":{"x": ... }}}   (N levels deep)
2. tools/call  name=list_indexed_repositories                        (liveness sentinel)

  N     bomb call        sentinel        session
  124   normal error     answered        ALIVE
  125   normal error     answered        ALIVE     <- last surviving depth
  126   no response      no response     CLOSED
  127   no response      no response     CLOSED

Server stderr at depth 126:
  ERROR Error reading from stream: serde error recursion limit exceeded at line 1 column 715

Below the boundary the offending call returns a clean error and the session keeps serving. At depth 126 and beyond the connection is gone and the sentinel never gets a reply.

Expected

Return a JSON-RPC error for the offending call and keep the session open, the way depth 125 already does, instead of dropping the connection.

Actual

The session closes. The follow-up sentinel call, and any in-flight work on the same connection, gets no response. The process keeps running (no panic, no crash), so the cost is availability on that one connection: one malformed argument ends that client's session, with only a line of stderr to explain it.

Why it matters

A local stdio client is trusted, so the blast radius of the observed repro is small. The reason to fix it is robustness: a buggy client that accidentally sends a deeply nested argument loses its whole session and every queued request. If a non-stdio transport is ever exposed, the same input could have the same effect from a less trusted caller, which would make it a cheap denial-of-service. Worth bounding either way.

Ask

Keep the session alive when an argument is over-nested: return a JSON-RPC error for that one call instead of closing the connection. Depth 125 already returns a clean error, so reaching the same outcome a couple of levels deeper would settle it. Happy to send a PR.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions