MCP notifications silently dropped when delivered to idle session via --channels
Summary
When an MCP server pushes a notification via notifications/<custom_method> (Claude Code's --channels mechanism), the delivery sometimes never surfaces in the target session — not in the pane, not in the transcript, not as a tool result. The server-side delivery completes successfully (acknowledged, logged sub-second) but the LLM never sees the message.
Environment
- Claude Code: v2.1.148+ (observed on multiple recent versions)
- Launch flags:
--channelsenabled,--strict-mcp-configwith a custom MCP config - Session state at drop time: idle at the
❯prompt (no pending turn, no tool calls in flight) - Platform: macOS 25.5.0 (Darwin), tmux-attached sessions
Symptom
A custom MCP server sends mcp.notification({ method: "notifications/claude/channel", params: {...} }). The MCP transport confirms the notification was written; our broker logs the deliver and the recipient session's MCP connection acks.
But the receiving session's pane never paints the notification, the LLM transcript does not include it, and crosstalk reply from the other side never arrives. The receiving session continues idling at ❯ as if nothing happened.
A subsequent unrelated UserPromptSubmit (the operator typing) is processed normally — the MCP connection isn't broken, just that particular notification was dropped.
Frequency
Observed several times during multi-hour sessions across a fleet of ~30 concurrent Claude Code instances. Not deterministically reproducible — looks like a race or a state-condition where the channel notification doesn't reach the LLM-context attach point.
What we've ruled out
- Not server-side: our broker logs the notification was written and acknowledged within milliseconds. The MCP framing is byte-clean and identical to messages that DID deliver moments earlier on the same session.
- Not the MCP child crashing: the MCP server stays alive; subsequent notifications can deliver successfully.
- Not version-specific: observed on at least v2.1.148, v2.1.150, v2.1.156.
- Not a transport restart: no MCP connect/disconnect events in our logs around the drop.
Workaround we've built
We have implemented a TCP-style retransmit handshake at the MCP-broker layer:
- Each notification carries a sequence number + monotonic timestamp.
- The broker watches the recipient's tool-call activity (via Stop hook) to passively detect acknowledgment.
- If no ack arrives within N seconds, the broker resends the notification with an explicit
retransmit: Nflag in the meta payload AND a[RETRANSMIT N — original sent T-Xs ago]prefix in the body, so the LLM can recognize the resend (LLMs don't dedupe via sequence numbers like network stacks do — the explicit flag is necessary).
The workaround works but is a lot of complexity to compensate for what we believe should be reliable in-process delivery.
Repro complexity
A clean minimal repro would require:
- Custom MCP server emitting periodic notifications.
- Claude Code launched with
--channelsand the MCP config. - Session sitting idle at
❯for many minutes. - Variable: some notifications surface in transcript, others don't, with no observable difference.
We're filing this even with the imperfect repro because we expect Anthropic may have telemetry that confirms the symptom from the other side, or guidance on what we might be doing wrong on the MCP server side.
Questions
- Is there a known race in the notification-attach path when the session is idle (no in-progress turn)?
- Is there a way for the MCP server to query "did the LLM see notification ID X" so retransmit doesn't depend on transcript scanning?
- Are channel notifications expected to surface in the transcript at all when no turn is in progress, or only at the start of the next turn? If the latter, that explains the "drop" — but the LLM never picks them up on the next turn either.
References
- Our internal tracker: https://github.com/dwinter3/claude-mux/issues/462 (TCP-handshake design)
- Bridge sidecar implementation: a separate workaround for the same family of issues on non-
--channelssessions
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗