MCP tool-call parser appends `</payload></invoke>` to string parameter values (100% repro within affected session)
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
textblocks in the same transcript are clean — the leak is confined to MCPtool_use.inputstring 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
payloadstring 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.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗