Skip to content

sam-node: tools/call succeeds on the backend but consumer sees EOF (producer->consumer relay drops the response) #375

Description

@fer-marino

Environment

  • SAM v0.1.0-alpha.9 (Windows x86_64 binaries)
  • Windows 11, local sam-one standalone mesh (control plane + router, sqlite backend)
  • Two sam-node instances joined via bootstrap token, --allow-loopback
  • Producer node's sam-node.yaml registers a type: mcp service via command (stdio subprocess)

What's broken

A tools/call (and tools/list) issued from a consumer node via call_remote_tool / find_remote_tools against a service on a different node consistently fails with:

failed after 3 retries: failed to call tool mcp://<service>/<tool>: calling "tools/call": EOF

This reproduces with two independent clients (the bundled mcp-client.exe, and Python's langchain-mcp-adapters talking to the consumer node's /mcp endpoint) - not a client bug.

Root-caused to the producer→consumer response relay, not the backend

Instrumented the command-spawned subprocess (a stdio<->HTTP relay to a warm backend) to log every raw line in/out with timestamps. Evidence:

  1. The subprocess produces the correct tools/call response, twice (once per retry), matched to the request:
    IN  {"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"...","arguments":{...}}}
    OUT ok {"jsonrpc":"2.0","id":3,"result":{"content":[{"text":"...correct payload..."}],"isError":false,...}}
    
  2. The producer's own log shows a clean write-back to the consumer at the exact moment of that success, no error:
    2026-09-09T09:17:09.770+0200  Stream Accounting  {"target": "mcp://<service>", "bytes_read": 1218, "bytes_written": 834}
    
  3. The consumer logs EOF 1ms later, for the same call:
    2026-09-09T09:17:09.771+0200  [MCP] Tool call failed, retrying in 2s: ... calling "tools/call": EOF
    

Repeats identically on the retry 2s later. The producer believes it wrote a complete response; the consumer sees EOF at essentially the same instant. The backend, the subprocess relay, and the client are demonstrated correct by direct log evidence - the gap is specifically in the producer→consumer P2P response relay (or the consumer's read of it).

Positive control (rules out "your backend is unusual")

Tried the same pattern fronting @modelcontextprotocol/server-everything (the reference server sam-node skill show itself references, mcp://everything/get-sum) instead of our own backend:

  • Direct command: [npx, -y, "@modelcontextprotocol/server-everything"] (no relay): failed registration outright - npx takes ~4s to start even warm, exceeding the backend health-check deadline. (Separately worth noting: this deadline appears to have no configurable flag, and is tight enough that even SAM's own reference example can't reliably meet it on Windows.)
  • Via the same relay pattern, fronting a warm server-everything instance: different failure signature during health-check - unmarshaling jsonrpc message: EOF on the first attempt, then HTTP Error 400: Bad Request on retries.

Two different backends, two different but both-broken symptoms around retries/session handling for command-spawned services.

Ask

Would appreciate a pointer to whether this is a known issue (didn't find an obvious match in the open issues) and, if useful, I'm happy to share the full wire-level debug log and minimal repro scripts (a ~50-line stdlib-only stdio<->HTTP relay + sam-node.yaml).

Tested on Windows only - haven't yet confirmed whether this reproduces on Linux.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions