MCP channel notifications silently dropped when session is idle
Summary
MCP channel notifications (notifications/claude/channel) are silently dropped when a Claude Code session is waiting for user input. This makes bidirectional channel plugins (Telegram, Slack, etc.) effectively one-way — the agent can send messages out, but cannot receive inbound messages unless the session happens to be in an active conversation turn.
Steps to Reproduce
- Configure the official Telegram plugin (
telegram@claude-plugins-official) - Pair with a Telegram user (allowlist mode)
- Confirm outbound works: use the
replytool to send a message — succeeds - Let the session go idle (waiting for user input at the prompt)
- Send a Telegram message to the bot from the paired user
- The bot receives the message, calls
mcp.notification()withmethod: 'notifications/claude/channel' - The notification is never delivered to the session. The user's message is silently lost.
Messages only arrive as <channel> tags when the session is actively processing a conversation turn. If the agent is idle, the notification has nowhere to go.
Impact
This makes channel plugins unreliable for real-world use. The primary use case — "message my agent when I'm away and have it respond" — doesn't work. Users send messages expecting a response and get silence.
Workaround
We implemented a two-part workaround that restores full bidirectional messaging:
1. Spool file patch (plugin side):
In the Telegram plugin's handleInbound function, after the existing mcp.notification() call, also write the message to a local spool file:
// Write inbound messages to spool for external delivery
try {
const spoolDir = join(STATE_DIR, 'spool')
mkdirSync(spoolDir, { recursive: true })
const entry = JSON.stringify({
ts: new Date().toISOString(),
chat_id, message_id: msgId ? String(msgId) : undefined,
user: from.username ?? String(from.id),
text,
}) + '\n'
appendFileSync(join(spoolDir, 'incoming.jsonl'), entry)
} catch {}
2. Nudge daemon (session side):
A lightweight bash script polls the spool file every 2 seconds. When a new message appears, it uses tmux send-keys to inject the message text directly into the Claude Code session's terminal input:
tmux send-keys -t "$TARGET_SESSION" "[Telegram from $USER]: $TEXT" Enter
This works because tmux send-keys writes to the terminal's stdin, which Claude Code reads as user input — even when idle. The message appears as a user prompt and the agent processes it normally.
The daemon also supports @mention routing (e.g., @architect routes to a different tmux session), enabling multi-agent Telegram dispatch from a single bot.
Proposed Fix
The proper fix should be in Claude Code's MCP notification handler. When a notifications/claude/channel notification arrives and the session is idle, it should be queued and delivered as a synthetic user prompt (similar to how our tmux send-keys workaround works). This would make all channel plugins work bidirectionally without external daemons.
Environment
- Claude Code (latest as of 2026-03-21)
- Plugin:
telegram@claude-plugins-official - Platform: Linux (Ubuntu 24.04)
- Session type: tmux
This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗