MCP tool-call parser appends `</payload></invoke>` to string parameter values (100% repro within affected session)

Resolved 💬 3 comments Opened Apr 15, 2026 by Baggydawg Closed Jun 9, 2026

Summary

In a recent Claude Code session, every call to an MCP tool that accepted a string parameter containing multi-paragraph markdown had the literal suffix </payload>\n</invoke> appended to the parameter value. The tags shipped verbatim to the downstream service (in this case, a Slack MCP server that forwards the string to Slack), making them visible to message recipients.

Evidence

Repro rate: 6 of 6 sends in the affected session (100%).

Exact trailing suffix — byte-identical across all 6 calls:

</payload>\n</invoke>

(\n is a real newline character, not the two-character literal.)

Examples (last ~60 characters of the payload parameter, verbatim from the session JSONL):

  • ...paste me your config file (redact tokens) and I'll have a look.</payload>\n</invoke>
  • ` ...isn't exported in your .zshrc/.bashrc.</payload>\n</invoke> `
  • ...That path's 100% reliable.</payload>\n</invoke>

Where the leak originated

The trailing tags appear inside the JSON-encoded input.payload string of the tool_use object stored in the session JSONL (~/.claude/projects/<slug>/<session-id>.jsonl). This means the tags were inside the parameter value by the time the harness dispatched the MCP call — upstream of the MCP server, which is a passive pipe that just forwards whatever string it receives.

The corresponding tool_result echo confirms the server received and shipped the full string including the trailing tags.

Observed pattern

  • 100% repro rate within the affected session (6/6 calls).
  • Byte-identical trailing suffix — zero variance in form.
  • Always terminal, never mid-string.
  • No mismatched tags, no duplicates, no mid-content occurrences.
  • All affected payloads were multi-paragraph markdown (numbered lists, inline code, bold).
  • Earlier Claude-authored messages in the same downstream Slack DM (from a prior session) show no leak, so the bug is session-scoped — not universal to the tool or the markdown content.
  • Short, non-tool-authored messages in the same window are also clean (they bypass the tool-call path entirely).
  • The assistant's human-facing text blocks in the same transcript are clean — the leak is confined to MCP tool_use.input string values.

Hypothesis

The deterministic, byte-identical signature suggests a parser over-capture rather than a model-emission issue. A model hallucinating structural tags would typically produce variance (mismatches, duplicates, malformed tags). A parser whose terminator matches the outer </invoke> instead of the innermost </payload> (or equivalent parameter close) would produce exactly this signature.

Environment

  • Claude Code terminal CLI
  • macOS (darwin 25.3.0)
  • Session occurred within the week ending 2026-04-15
  • MCP server was a passthrough — it just forwards the payload string it receives

Impact

Visible to end recipients of any MCP tool that treats the leaked parameter as user-visible content (Slack, email, webhook bodies, etc.). Silent on the sender's side unless they grep their transcript — the human-facing text stream is clean, so the model doesn't know its tool call shipped garbage.

Repro artefacts

Happy to share the session JSONL privately if useful for root-causing. The relevant file lives at ~/.claude/projects/<slug>/<session-id>.jsonl and shows both the input.payload with the trailing tags and the tool_result echoing them back.

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗