MCP channel notifications (notifications/claude/channel) not surfaced to conversation
Description
MCP server-to-client notifications using the notifications/claude/channel method are successfully sent by the MCP server but never surfaced as <channel> tags in the conversation.
Environment
- macOS (Darwin 25.3.0)
- Claude Code (latest as of 2026-03-21)
- Telegram channel plugin (
claude-plugins-official/telegramv0.0.1)
Steps to Reproduce
- Install and configure the Telegram channel plugin
- Pair a Telegram user via
/telegram:access pair <code> - Send a message from the paired Telegram user to the bot
Expected Behavior
The message should appear in the Claude Code conversation as a <channel source="telegram" ...> tag, allowing the assistant to see and respond to it.
Actual Behavior
The message never appears. The assistant receives no <channel> tag.
Debugging Performed
- MCP tool calls work fine —
reply,react,edit_messagetools all function correctly (request/response path is healthy) - Bot is polling Telegram — confirmed via active TCP connections to Telegram API servers and
pending_update_count: 0 - Gate check passes — sender is in
allowFrom, gate returnsdeliver mcp.notification()succeeds — added debug logging to server.ts confirming:
````
2026-03-21T06:18:05.507Z handleInbound called: text="Hi" from=126616686
2026-03-21T06:18:05.508Z gate result: deliver
2026-03-21T06:18:05.509Z sending mcp.notification for chat_id=126616686
2026-03-21T06:18:05.510Z mcp.notification sent successfully
- MCP stdio pipes are connected — verified via
lsofshowing unix socket FDs for stdin/stdout/stderr between Claude Code and the bun server process - The experimental capability is advertised — server declares
experimental: { 'claude/channel': {} }in capabilities
Conclusion
The MCP server correctly sends notifications/claude/channel notifications over stdio. Claude Code receives them (no error thrown) but does not inject them as <channel> tags into the conversation. This was tested across multiple Claude Code restarts and a full plugin reinstall.
🤖 Generated with Claude Code
10 Comments
Also affected by this bug. Linux (Ubuntu) aarch64, Claude Code v2.1.83. Confirmed the plugin emits a well-formed JSON-RPC notification on stdout, but Claude Code never surfaces it as a
<channel>tag. Outbound tools work fine. Closed my duplicate #38926 in favor of this one. 👍Confirmed with the official Discord plugin as well (
discord@claude-plugins-official). In my case, the MCP tools work normally (reply, etc.), the bot is online, and when I run the plugin server manually I can confirm it emits a well-formednotifications/claude/channelnotification for inbound DMs. However, in hosted Claude Code sessions, that notification is never surfaced as a<channel source="discord" ...>block. This matches the same core issue: standard MCP tools work, but experimental channel notifications are not injected into the conversation.Is there an ETA for resolution?
Also affected. Adding data from a non-Bun environment:
Environment
--channels plugin:telegram@claude-plugins-officialnpx tsxinsteadPatches applied to plugin v0.0.4 (
~/.claude/plugins/cache/claude-plugins-official/telegram/0.0.4/)server.tsshebang:#!/usr/bin/env bun→#!/usr/bin/env npx tsxpackage.jsonscripts.start:bun server.ts→npm install --silent && npx tsx server.ts.mcp.jsoncommand:bun→npx --yes tsxtsxandzodto dependencies, installed vianpm installBehavior
telegram__replyoutbound tool works correctly — messages arrive in Telegramnotifications/claude/channelnever surfaces in the conversationConfirms: This is NOT a bun-specific issue. The same behavior occurs with Node.js + tsx. The root cause is client-side — Claude Code does not process
notifications/claude/channelfrom MCP channel servers.Previously reported this as #37633 (closed as duplicate of this issue).
Also affected. Windows 11, Claude Code v2.1.90, Discord plugin (
discord@claude-plugins-official).Confirmed behavior:
Listening for channel messagesdisplayedgateway connected as Klaude Code Channels#6759)claude/channelandclaude/channel/permissionnotifications/claude/channelnever surfaced to conversationAdditional finding: During troubleshooting, discovered that plugin cache directory was silently emptied after a Claude Code update (plugin files deleted but
installed_plugins.jsonstill references the path). This may be a separate plugin cache migration bug on Windows. Had to manually restore files from marketplace source to get the plugin running at all.Verified not a plugin issue: Running the plugin manually with MCP init over stdin confirms it emits well-formed
notifications/claude/channelJSON-RPC notifications. The problem is entirely client-side — Claude Code receives but does not inject them.This has been open for 2 weeks with no response from Anthropic. Multiple users across Mac, Linux, and Windows are affected. Please prioritize.
Full workaround: standalone polling daemon with auto-reply (Linux)
Since this bug is in Claude Code's core notification handler and can't be fixed externally, I built a standalone replacement that completely bypasses the broken plugin. Handles inbound (polling + inbox files) and outbound (auto-reply via
claude -p). Running in production on Gentoo Linux.Architecture
Instead of relying on the plugin's
notifications/claude/channel(silently dropped by Claude Code), this uses:getUpdates(every 3s).txtfor text,.jpgfor photos, documents by name) to~/.claude/channels/telegram/inbox/claude -p --model haiku— response sent back as a Telegram replyklaus-send "text") for manual messagesThe polling script
Outbound helper
OpenRC service
For systemd:
Type=simple,User=youruser,ExecStart=/usr/local/bin/klaus-telegram,Restart=always.What this replaces
| Feature | Broken Plugin | This Workaround |
|---------|--------------|-----------------|
| Inbound text | silently dropped | saved to inbox + auto-reply |
| Inbound photos | dropped | downloaded to inbox |
| Outbound messages | via MCP tools | via
sendMessagehelper || Auto-reply | never triggered | via
claude -p|| Typing indicator | no | yes |
| Allowlist | via access.json | via sender ID check |
Important
--channels plugin:telegram@claude-plugins-official.curl,jq,claudeCLI.See also #37933 for more reports and discussion of this issue.
Environment: Gentoo Linux, Claude Code 2.1.101, bash, curl, jq.
Follow-up to my 2026-04-11 workaround comment — the framing was off
In my earlier comment here I attributed the dropped
notifications/claude/channelto a host-side gap in the Channel framework and shipped a standalone polling daemon as workaround. Today I ran the plugin end-to-end on two different setups and the root cause in my specific failure mode is simpler than I thought — at least for me.The host delivers notifications correctly. In my case the plugin's MCP server was never spawning in the first place.
What I tested today
Same plugin (
telegram@claude-plugins-officialv0.0.6), same bot, two hosts:bun install -g, native Claude Code binary,claude --channels plugin:telegram@claude-plugins-official. Paired on the first try, DM surfaced as a← telegram · …prompt,replytool call round-tripped. Plugin works out of the box as documented.bunis not installable here — both the regular build andbun-linux-x64-baselineSIGILL because baseline still effectively needs x86-64-v2 (SSE4.2 + POPCNT) and Penryn only has SSE4.1. I shadowed the plugin's.mcp.jsonwith a tinynode --import tsx/esm server.tswrapper (deps vianpm installin the plugin dir). The MCP log then showed:``
``Starting connection with timeout of 30000ms
Successfully connected (transport: stdio) in 6868ms
Connection established with capabilities: {"hasTools":true, … ,"serverVersion":{"name":"telegram","version":"1.0.0"}}
Channel notifications registered
notifications/claude/channel: Test2
notifications/claude/channel: Hallo!
Calling MCP tool: reply
Tool 'reply' completed successfully in 105ms
Both DMs surfaced in the Claude session,
replylanded back to the user on Telegram. So the Node-hosted Claude Code processesnotifications/claude/channelcorrectly — when the server actually runs.Where the silent failure comes from
The plugin's
.mcp.jsonhard-requiresbun:If
bunisn't on PATH, or SIGILLs on the CPU (same population affected by #37065), the MCP server never starts. The host's connection attempt fails, but on my side the only user-visible effect was "DMs vanish into thin air" — easy to mis-diagnose as a framework issue, which is exactly what I did back in April.First-check that pins this down for anyone else hitting my symptom: look for
ERR_MODULE_NOT_FOUND/command not found/ exit-on-connect in~/.cache/claude-cli-nodejs/<project>/mcp-logs-plugin-telegram-telegram/*.jsonl. If the very first log for a session isConnection failed after Xms: MCP error -32000: Connection closedwith nothing in between, the server just isn't spawning.Workaround if
bunis off the tableSmall wrapper in the plugin cache — survives until the next plugin update:
And
.mcp.json:A cleaner upstream fix would be a fallback runtime in
.mcp.json(trybun, fall back tonode --import tsx/esm) or shipping a precompiledserver.mjsalongsideserver.ts.Caveats
This only explains my failure mode. If your host has a working
bunand you still see DMs disappear — e.g. #36503 (macOS, bun almost certainly present) or @Lucasafaria1's WhatsApp-plugin-with-groups case — then this observation doesn't apply and the original framework-bug hypothesis may well still hold. Worth checking the same MCP log first, though.The standalone polling daemon from my earlier comment is still useful as a permanent Telegram bridge that survives session restarts (the plugin only lives while a
--channelssession is open), but framing it as the fix for a framework bug was wrong on my part.Resolved on macOS — confirms @hibbes' 2026-04-23 follow-up, with a separate parsing bug
Confirming the framework-bug framing is wrong on macOS as well. The "notifications never surface" symptom traces back to the same root cause @hibbes identified —
--channelsopt-in not being passed at startup — but reaches it via a different path than their bun/SIGILL case. Posting in case anyone else is still stuck on this thread.Environment
imessage@claude-plugins-officialv0.1.0,discord@claude-plugins-officialv0.0.4buninstalled and working — so the bun-SIGILL / missing-bun path from the 2026-04-23 comment doesn't applyDiagnosis
Plain
claudestartup. MCP log at~/Library/Caches/claude-cli-nodejs/<project>/mcp-logs-plugin-imessage-imessage/*.jsonl:\
\\\Successfully connected (transport: stdio) in 213ms
Connection established with capabilities: {"hasTools":true,...,"serverVersion":{"name":"imessage","version":"1.0.0"}}
Channel notifications skipped: server plugin:imessage:imessage not in --channels list for this session
\
\The plugin's MCP server is healthy. Outbound tools (
replyetc.) work fine. Channel notifications are deliberately skipped because--channelswas not passed at startup. Same for the Discord plugin in the parallel log directory.Resolution
Start Claude Code with explicit
--channelsopt-in for each plugin you want inbound notifications from:\
\\bash\claude --channels plugin:imessage@claude-plugins-official --channels plugin:discord@claude-plugins-official
\
\After this, the startup banner shows:
\
\\\Listening for channel messages from: plugin:imessage@claude-plugins-official, plugin:discord@claude-plugins-official
\
\<channel source="imessage" ...>/<channel source="discord" ...>blocks now surface in the session for inbound DMs, and theChannel notifications skippedline is gone from the MCP log. Verified end-to-end on both plugins.Secondary bug — comma-separated
--channelsentries silently failThe first form I tried — comma-joining the entries the way
--allowedToolsetc. accept — is broken:\
\\bash\claude --channels plugin:imessage@claude-plugins-official,plugin:discord@claude-plugins-official
\
\Claude Code prints:
\
\\\Listening for channel messages from: plugin:imessage@claude-plugins-official,plugin:discord@claude-plugins-official
plugin:imessage@claude-plugins-official,plugin:discord@claude-plugins-official · plugin not installed
plugin:imessage@claude-plugins-official,plugin:discord@claude-plugins-official · not on the approved channels allowlist
you asked for plugin:imessage@claude-plugins-official,plugin:discord@claude-plugins-official but the installed imessage plugin is from claude-plugins-official
\
\i.e. the comma-joined string is treated as a single plugin ID rather than being split into entries. Repeating
--channelsper entry is the only working form I found. Worth noting the inconsistency: the validation error for an untagged entry does say \"--channels entries must be tagged\" (plural \"entries\") and joins them with commas in its own output, so the parser intent looks split-on-comma somewhere internally.Doc gap
claude --helpon v2.1.121 does not list--channelsat all. Users who don't follow this issue thread or read MCP source won't know the flag exists. The failure mode is hard to debug without going intomcp-logs-*/*.jsonl— would help to surface in--helpand/orclaude doctor.Thanks @hibbes for the 2026-04-23 follow-up — pointed me at the right log file.
Thanks for the report. This is the same issue as #36431 (
notifications/claude/channelfrom stdio MCP servers is acknowledged but never delivered into the conversation). Consolidating tracking there. Please follow #36431 for updates.This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.