Skip to content

Bug: Batched MCP tool calls corrupt parameters when using SSE transport #43311

Closed as not planned

Description

@darkhipo

Description

When opencode batches multiple MCP tool calls in a single message and the MCP server uses SSE transport (port 4201), the second+ tool calls fail with "JSON Parse error: Unexpected EOF". The first call succeeds, but subsequent calls receive corrupted parameters.

Reproduction

  1. Configure an MCP server with SSE transport (not stdio)
  2. Have the LLM produce a response with 2+ tool_use blocks targeting that server
  3. Opencode dispatches the tool calls
  4. First call succeeds, second call fails with corrupted parameters

Error Pattern

Invalid input for tool register_update: JSON parsing failed: 
Text: {"entry_id": {"tool": "register_update", "error": "Invalid input 
for tool register_update: JSON parsing failed: Text: {"entry_id": . 
Error message: JSON Parse error: Unexpected EOF

The error shows the first tool call's error response text concatenated into the second tool call's parameters string. The parameters should be a JSON object but arrive as a corrupted string containing both valid JSON and error text from a previous call.

Root Cause Analysis

The streaming parser (Vercel AI SDK Anthropic provider) correctly separates input_json_delta buffers per content block index — there is no cross-block buffer leak at the streaming level.

The corruption happens in the MCP SSE transport layer during concurrent tool call dispatch:

  1. Opencode sends tool call N as an HTTP POST to the SSE-transport MCP server
  2. The server returns an error response (e.g., validation failure)
  3. The error response text is somehow fed back into the shared parameter buffer for the next tool call
  4. Tool call N+1 is sent with parameters = valid_json + error_text_from_N
  5. The server receives corrupted JSON and rejects it

This is a client-side routing/buffering issue — the MCP client does not properly isolate request construction from response handling when dispatching concurrent tool calls over SSE transport.

Affected Transport

  • SSE transport (port 4201): Bug present
  • stdio transport: Not affected (serialized connection)
  • Streamable HTTP (port 4200): Possibly affected (same HTTP POST mechanism)

Environment

  • opencode: v1.18.15 (Bun-compiled TypeScript)
  • MCP server: Python MCP SDK (mcp package)
  • Transport: SSE via Starlette
  • OS: macOS arm64

Suggested Fix

Ensure each tools/call JSON-RPC request has its own isolated parameter buffer. Error responses from one call must not be concatenated into parameter buffers for subsequent calls. Consider:

  1. Using separate HTTP connections for concurrent tool calls to the same SSE server
  2. Implementing strict request/response correlation with unique JSON-RPC IDs
  3. Ensuring parameter buffers are constructed fresh for each call, not shared

Workaround

Our MCP server now has a server-side sanitizer that detects and repairs corrupted parameters by extracting valid JSON from the corruption. This is a safety net, not a proper fix — the fix needs to be in the opencode client.

Activity

  1. Bohdan-Tym commented on Sep 10, 2026

    @Bohdan-Tym

    Confirmed resolved in v1.18.30. After upgrading from 1.18.29 and restarting, batched calls to a legacy (SSE-transport) MCP server no longer corrupt parameters — 3 concurrent DevMind tool calls returned clean results. Consistent with PR #39373 rolling the client back to MCP SDK 1.29.0. This issue can be closed.

  2. jairomello commented on Oct 2, 2026

    @jairomello

    Additional evidence from a duplicate report (v1.18.34, Linux, local stdio MCP server, TypeScript @modelcontextprotocol/sdk):

    This bug is not SSE-specific — it reproduces with type: "local" stdio transport too, and the corruption source is broader than error-response feedback:

    1. Built-in tool args leak into MCP tool params. A parallel MCP list_tasks + built-in bash call delivered to list_tasks:

      {"fields": {"command": "grep -rn \"foo\\|bar\" apps/", "workdir": "/repo"}}
      

      (fields received the bash arguments object — no error text involved, pure cross-call argument merge.) → JSON Parse error: Expected '}'

    2. Pure truncation (no merge, no error text): parallel MCP + built-in call delivered {"includeDescriptions": → Unexpected EOF.

    3. Serial calls never failed in a ~2h session; parallel MCP+MCP worked. Corruption concentrated on MCP tool + built-in tool in the same assistant message.

    Conclusion: the shared-buffer leak exists at the tool-call dispatch/argument-assembly level (cross-tool, including non-MCP tools), not only in the SSE transport response handling. The "stdio: not affected" note in the description should be revised.

  3. rekram1-node commented on Oct 4, 2026

    @rekram1-node
    Collaborator

    plz share sessions this happened in? run this command in your terminal: opencode export > session.json and then send the json file that the piped output was written to to me.

    I dont think this has anything to do w/ opencode? I think its a smaller model just failing to call tools well..

  4. thdxr commented on Oct 11, 2026

    @thdxr
    Member

    Thanks for the report! OpenCode 2.0 is now out and development is focused on V2, so we're closing issues filed against OpenCode 1. Please upgrade (opencode upgrade --major, details: https://anoma.ly/notes/opencode-2-0), and if this is still relevant on 2.0, reopen this issue or open a new one with your 2.0 version.

    — from 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊

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

Metadata

Metadata

Assignees

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