[BUG] Telegram MCP plugin: inbound notifications/claude/channel never delivered to session (outbound works)
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
Environment:
- macOS Darwin 25.4.0
- Claude Code (Opus 4.6, 1M context)
- Telegram plugin v0.0.1 (~/.claude/plugins/cache/claude-plugins-official/telegram/0.0.1)
- Runtime: Bun
What happens:
- User sends a DM to the bot from Telegram
- Bot receives the message — sendChatAction('typing') fires, visible in Telegram as "typing..."
- gate() returns deliver (user ID 767**** is in access.json allowlist)
- mcp.notification({ method: 'notifications/claude/channel', params: {...} }) is called (server.ts line 578)
- Notification never arrives in the Claude Code session — no <channel> tag appears in the conversation
What works:
- Outbound reply tool works perfectly — mcp__plugin_telegram_telegram__reply sends messages to Telegram successfully (confirmed: message ID 26 delivered)
- Bot token is valid, bot is polling (ps aux shows bun process running)
- Access config is correct (dmPolicy: "allowlist", user ID in allowFrom)
What was tried:
- Restarting Claude Code sessions (multiple times)
- Reconfiguring via /telegram:configure
- Verifying access.json is correct
- Confirming MCP server process is running (PID visible in ps aux)
Diagnosis:
The MCP stdio transport between the Telegram server and Claude Code is functional for tool calls (request/response pattern) but mcp.notification() (server-initiated push) is silently
dropped or not processed by Claude Code's MCP client. The void fire-and-forget on line 578 of server.ts means any notification delivery failure is swallowed without logging.
Relevant files:
- ~/.claude/plugins/cache/claude-plugins-official/telegram/0.0.1/server.ts — line 578: void mcp.notification(...)
- ~/.claude/channels/telegram/access.json — allowlist config
- ~/.claude/channels/telegram/.env — bot token
What Should Happen?
Expected behavior: Inbound Telegram messages should appear in the Claude Code conversation as <channel source="telegram" ...> events, allowing Claude to read and respond.
Error Messages/Logs
{"debug":"Starting connection with timeout of 30000ms","timestamp":"2026-03-19T23:47:32.549Z"}
{"debug":"Successfully connected to undefined server in 228ms","timestamp":"2026-03-19T23:47:32.776Z"}
{"debug":"Connection established with capabilities: {\"hasTools\":true,\"hasPrompts\":false,\"hasResources\":false,\"hasResourceSubscribe\":false,\"serverVersion\":{\"name\":\"telegram\"
,\"version\":\"1.0.0\"}}","timestamp":"2026-03-19T23:47:32.776Z"}
{"debug":"Channel notifications skipped: server plugin:telegram:telegram not in --channels list for this session","timestamp":"2026-03-19T23:47:32.780Z"}
{"debug":"Channel notifications skipped: server plugin:telegram:telegram not in --channels list for this session","timestamp":"2026-03-19T23:47:32.780Z"}
{"debug":"Calling MCP tool: reply","timestamp":"2026-03-19T23:50:05.506Z"}
{"debug":"Tool 'reply' completed successfully in 332ms","timestamp":"2026-03-19T23:50:05.838Z"}
And from a previous session where it did work (2026-03-19T23-33-20-145Z.jsonl):
{"debug":"Channel notifications registered","timestamp":"2026-03-19T23:33:21.324Z"}
{"debug":"notifications/claude/channel: yo!","timestamp":"2026-03-19T23:35:03.717Z"}
{"debug":"Tool 'reply' completed successfully in 401ms","timestamp":"2026-03-19T23:35:18.476Z"}
Steps to Reproduce
Steps to reproduce:
- Install the Telegram plugin (plugin:telegram:telegram)
- Configure bot token in ~/.claude/channels/telegram/.env
- Add a user to the allowlist in ~/.claude/channels/telegram/access.json
- Start a Claude Code session — send a Telegram message to the bot
- Sometimes it works (Channel notifications registered), sometimes it doesn't (Channel notifications skipped: server plugin:telegram:telegram not in --channels list for this session)
- Restart Claude Code and repeat — the result is non-deterministic
Expected: Channel notifications registered on every session start
Actual: Intermittent — some sessions register, others skip with not in --channels list
How to verify the state: Check the latest .jsonl file in:
~/Library/Caches/claude-cli-nodejs/<project-path>/mcp-logs-plugin-telegram-telegram/
Line 4 will say either:
- "Channel notifications registered" (working)
- "Channel notifications skipped: server plugin:telegram:telegram not in --channels list for this session" (broken)
Workaround found: None. Restarting sessions sometimes fixes it, sometimes doesn't. The --channels flag that controls this appears to be set internally by Claude Code and is not
user-configurable.
Claude Model
Opus
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
Claude Code v2.1.80
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Warp
Additional Information
_No response_
20 Comments
Same issue here.
Bot receives DMs, sends typing indicator, passes the gate (pairing/allowlist all working), and outbound
replytool works fine — butnotifications/claude/channelnever surfaces in the session. Reloading plugins and restarting the session didn't help.+1 — experiencing the exact same issue on macOS Darwin 25.3.0, Claude Code 2.1.81, Telegram plugin v0.0.1.
Confirmed via MCP logs:
replytool works fine (messages delivered to Telegram)getUpdatesreturning empty)access.jsonis correctly configured (allowlist policy, user ID present)/reload-pluginsdid not resolve~/Library/Caches/claude-cli-nodejs/-Users-Zissou/mcp-logs-plugin-telegram-telegram/2026-03-20T23-02-46-128Z.jsonlWould love to see a fix or a user-facing
--channelsflag to force registration.Also experiencing the same issue -- https://github.com/anthropics/claude-code/issues/36898#issuecomment-4101797957
Same here, MacBook Pro
Ran into this same pattern - outbound replies work fine but inbound channel notifications never surface. A few things that fixed it:
claude plugins marketplace updatebefore anything else. The cache doesn't auto-refresh, so you might be running an older plugin version even after reinstalling.ps aux | grep "bun.*server"and kill any zombies.--channelsflag is required at launch. Having the server in.mcp.jsonloads the MCP tools (so outbound works), but it doesn't activate channel event delivery. You need:``
``claude --channels plugin:telegram@claude-plugins-official
tengu_harbor: truein your GrowthBook config:``
bash
`cat ~/.claude.json | python3 -c "import json,sys; print(json.load(sys.stdin).get('cachedGrowthBookFeatures',{}).get('tengu_harbor'))"
True`.Should print
I wrote up the full debugging process and permission relay setup here: https://github.com/cversek/cc-telegram-channel-tutorial
Confirming this is the same issue on Claude Code 2.1.81 (macOS).
Symptoms:
Debug logs consistently show:
"Channel notifications skipped: server plugin:telegram:telegram not in --channels list for this session"
Important detail:
claude --helpshows no usable--channelsflagclaude updateconfirms I am already on the latest version (2.1.81)From the user side, there appears to be no way to enable the required channel subscription, even though the Telegram plugin expects it.
This looks like a CLI/plugin mismatch rather than a configuration issue.
Happy to provide full debug logs if needed.
Description:
Inbound Telegram messages are intermittently not delivered to the Claude Code session. The user must send the same message 2+ times for it to be received. Outbound replies (via the reply tool) work correctly every time. Sometimes it works on the first try and sometimes I have to send the message 4 times.
Behavior:
Environment:
Suspected cause:
Likely related to the zombie process / polling conflict described in the linked issues — when multiple processes compete for the Telegram polling slot, messages are randomly split between them and the active Claude session misses some.
Edit: I tried to kill all the processes and start them again. But nothing changed
Impact:
User loses confidence that messages are being received. Workaround is to send each message twice, which is not acceptable UX.
Same bug here.... I can't get telegram to reliably work Claude team please fix
+1, I can reproduce what looks like the same channel allowlist/routing problem, but with a locally installed development plugin channel rather than Telegram.
In my case:
I am using this for local developmentBut channel registration still fails with:
So the UI confirmation appears to be accepted, but the runtime still behaves as if the plugin channel is not allowlisted.
Filed separately here for the local-plugin repro: #43064
I dug into this a bit and wanted to share a hypothesis that might help pinpoint the issue quickly.
Observed behavior
Likely root cause
It looks like channel matching may be relying only on the plugin/server name segment extracted from values like:
and comparing that directly against the
--channelsentries.This can fail when:
--channelsname@marketplace, which may already be available on the connection/config object) isn’t used during matchingIn that case, even though the correct channel is configured, the lookup fails and the notification gets dropped at the gating stage.
Why this fits the symptoms
Suggested direction
It may help to:
Debugging improvement
The current error message doesn’t show what channels are actually registered, which makes this harder to diagnose.
Including that would make issues like this immediately obvious, e.g.:
or:
This would help distinguish between:
Claude got it working! Posting what actually fixed it in case it helps.
What was happening
The bot was receiving messages fine (typing indicator visible in Telegram, outbound reply tool working), but inbound messages never surfaced in the session. MCP logs showed:
Two separate problems
1. Zombie bun process
A previous session's bun server was still running in the background (check with
ps aux | grep "bun.*server"). Telegram delivers each update to exactly one active poller, so the zombie was consuming messages before the current session's process could see them. Kill it withkill -9 <PID>and verify only one bun process remains.Hat tip to @cversek for calling this pattern out explicitly.
2. The
--channelsflagEven after clearing the zombie, channel notifications were still skipped. The
--channelsflag has to be passed at session launch to tell the MCP client to routenotifications/claude/channelevents into the conversation. Without it, the server fires the notification but the client drops it regardless.The working invocation:
Again, @cversek's comment named this directly — that was the unlock.
A note on running multiple sessions
If you run multiple Claude sessions simultaneously, each one spawns its own bun process polling the same bot token. Telegram splits updates across all active pollers, so messages land randomly across sessions. The pattern that works cleanly: one dedicated session launched with
--channels, all others without. A shell function makes this easy:Root cause note for Anthropic
@uditya-kumar's comment gave the clearest technical framing here: the
--channelscheck fails because session-level channel registration never happens for GUI-launched sessions. The--channelsflag is CLI-only with no settings.json equivalent, so Claude Desktop sessions never receive it and channel notifications are permanently skipped in that context.The
tengu_harborfeature flag being enabled andaccess.jsonbeing correctly configured are both necessary but not sufficient — the session-level registration is the missing piece. A fix that automatically registers channels when a plugin declares theexperimental['claude/channel']capability (which the telegram plugin already does in server.ts) would make this work without requiring users to discover and manually pass the flag.Seems Claude Code session that loads the Telegram plugin spawns a bun poller but doesn't kill it on exit 🤔
Same issue here — adding environment details.
Environment:
Symptoms:
replytool works finemcp.notification({ method: 'notifications/claude/channel', ... })is called but never surfaces in the Claude Code session~/.claude/channels/telegram/messages/since March 25getUpdatesfrom curl returnsok: truewith empty result (no 409), suggesting the bot consumes updates but the notification is silently droppedTried:
/mcpreconnectionsdeleteWebhook+drop_pending_updatesThis has been persistent since late March 2026. Would appreciate any workaround or fix.
Different approach: persistent poller outside the Claude session
The fixes above work but are fragile — messages that arrive during a Claude restart are still lost, because the bun poller dies with the session and the Bot API cursor has already advanced.
We split it into two processes:
telegram_bot.py— persistent Python, runs independently of Claude:getUpdatesforever, writes every message to SQLite (WAL mode)flock— no zombie/split-update problem by constructionserver.ts(modified) — no longer polls Telegram:--channelsis still required for MCP routing, but accumulation happens regardless — restart with--channelsand you get everything that arrived while Claude was down.When things go wrong
We ship a
telegram_debug.py --doctorthat checks the whole chain in one pass: bot.py PID alive, Unix socket accepting, inbound.db row counts, server.ts process count, source/plugin hash drift. Common failure modes it catches:server.tsprocesses — doctor flags it, fix iskill <pids>then/reload-pluginscpredeploynohup telegram_bot.py ... &Liveness signal: bot.py stamps 👀 on receive, server.ts stamps 🫡 on delivery. Both glyphs on a message = full pipeline ran.
Full design doc (SQLite WAL rationale, singleton semantics, 409 retry, the
pkill-by-name trap we hit): harden-telegram/design.mdTelegram plugin: inbound DMs never injected into conversation (outbound works)
claude-plugins-official/telegramv0.0.6claudeCLI — same behaviorSummary
The Telegram plugin's MCP server connects fine and outbound replies work, but inbound Telegram DMs are never injected into the Claude Code conversation as
<channel source="telegram">blocks. Pairing-mode replies from the server (ctx.reply("Pairing required …")) also do not reach the user, suggesting the issue may extend beyond just notification injection.What works
~/.claude/channels/telegram/bot.pidis populated;bun server.tschild is alive; manual launch logstelegram channel: polling as @<bot>./mcpreportstelegramas connected in both VS Code and CLI.handleInboundruns (bot.api.sendChatAction(chat_id, "typing")at server.ts:946).mcp__plugin_telegram_telegram__replywith the user''schat_idsuccessfully delivers a message (verified — message ID returned, user received it)./starttriggers the welcome reply frombot.command("start")at server.ts:684.What doesn''t work
<channel source="telegram">blocks. Themcp.notification({ method: "notifications/claude/channel", params: {...} })call at server.ts:963 fires (it''s downstream of the typing indicator we observed), but Claude Code never surfaces the message in the user''s conversation context.dmPolicy: "pairing", when a non-allowlisted sender DMs,handleInboundshould callctx.reply("Pairing required — run in Claude Code: /telegram:access pair <code>")at server.ts:912. The user only sees the bot''s typing indicator vanish; no pairing-code message arrives.y xxxxx/n xxxxx) similarly produce no observable effect.Steps to reproduce
Setup:
powershell -c "irm bun.sh/install.ps1 | iex"bun --version~/.claude/plugins/cache/claude-plugins-official/telegram/0.0.6/)/telegram:configure <BotFather token>/mcpshowstelegramas connected/telegram:access policy allowlistand/telegram:access allow <your-telegram-user-id>(get ID from@userinfobot)Reproduce:
hello)did my message arrive?)<channel source="telegram" chat_id="..." message_id="..." user="..." ts="...">hello</channel><channel>block ever appearsConfirm outbound (rules out the bot):
mcp__plugin_telegram_telegram__replywithchat_id= user''s Telegram ID and anytext— user receives the message; tool returns a message ID. Outbound is fully functional.Diagnostic data
Get-CimInstance Win32_Process -Filter "Name=''bun.exe''"shows two healthy bun processes:bun run --cwd ... --shell=bun --silent start)bun.exe server.ts) — PID matches~/.claude/channels/telegram/bot.pid~/.claude/channels/telegram/inbox/is empty (expected for text-only DMs — server only writes inbox files for media)/mcpreconnect did not change behaviorclaudeCLI session started from the bun-aware PowerShellHypothesis
Claude Code 2.1.126 is not registering / not handling the custom MCP notification methods used by this plugin:
notifications/claude/channel(inbound DM forwarding, server.ts:963)notifications/claude/channel/permission(permission-reply intercept, server.ts:929)The MCP transport itself is healthy —
tools/listandtools/callboth work, as proven by outbound replies. The notification handler on the Claude Code side appears to be dropping or not subscribing to these custom methods.Workaround
None within the plugin. Outbound replies still function (Claude can push messages to the user''s Telegram), but the user cannot reach Claude through Telegram.
'@
For anyone hitting the silent inbound notification bug (v2.1.x), I've documented a production-tested blueprint/workaround that ensures session persistence and reliable message delivery on Linux/Raspberry Pi.
It bypasses the MCP notification drops by using a file-based inbox and tmux send-keys for input injection, maintaining a "sane" session state even after the first reply. Tested on RPi 4 / Debian.
Complete Blueprint & Code: https://github.com/LozzKappa/claude-code-telegram-bot
Reposting with fresh evidence as of 2026-06-01.
We're hitting the same polling degradation on claude 2.1.114, the version we pinned specifically to avoid the 2.1.126 regression. Different timeline -- it manifests after 24-48 hours of session age rather than immediately -- but the same end state.
Repro:
Start a claude session with --channels plugin:telegram@claude-plugins-official on a pinned 2.1.114.
Inbound Telegram delivery works normally for the first 24h.
Leave the session running ~48 hours.
Plugin status line continues showing "Listening for channel messages". No error, no disconnect indicator.
Inbound Telegram messages stop being delivered to the session.
External monitoring (Telegram API reachability, tmux liveness, plugin status string) all report green.
Only working remediation is tmux kill-session to force a fresh claude process. The new session polls correctly for another 24-48h, then the cycle repeats.
Behavior is consistent across two separate bot instances on the same machine (each with its own TELEGRAM_STATE_DIR and bot token), so it isn't bot-specific.
Note: my variant may be subtly different from the main thread here. The reports here describe the plugin polling Telegram successfully but inbound messages not reaching the session. What I'm seeing on 2.1.114 looks like the plugin reports "connected" but never actually polls the Bot API at all -- no outbound TCP to api.telegram.org, getUpdates accumulates unconsumed messages. Could be the same root cause surfacing two ways, could be distinct. Flagging in case it helps maintainers triage.
Workaround in place is a scheduled nightly kill-session. Would appreciate confirmation this is on someone's radar, or pointers to a config flag we're missing.
Workaround:
claude -pper-message bridgeThe root cause is confirmed: CC host does not declare
experimental.claude/channelin MCP client capabilities, so all inbound notifications are silently dropped (#64470).Working solution: Independent bridge script using
claude -p(Architecture A), bypassing the broken MCP channel entirely.Quick start
Key code pattern
Limitations
--resume <session-id>Related repos
Tested on Windows 11, Claude Code 2.1.161.
Confirming on Claude Max (macOS, Bun runtime, Claude Code 2.1.190 and 2.1.191). Outbound works (the reply tool sends fine); inbound
notifications/claude/channelnever reach the session. This matches thetengu_harborroot-cause report consolidated here from #46299, and the non-deterministic registration described above.Two extra data points from this setup:
claude mcp list; only remote plugin servers such as vercel appear). So the plugin channel is unusable here, in addition to the notification-delivery problem.server:entry and launching with--dangerously-load-development-channelsdoes start it, but inbound then lands in the TUI prompt buffer instead of starting a turn. That specific failure is tracked separately in #61010.One clean instance of "worked for months, broke with no changes": the channel registered fine until a reboot, after which fresh sessions consistently log
Channel notifications skipped.Thank you for taking the time to report this.
We're currently triaging open MCP issues, and this one is being closed in favor of tracking it in #36431, which covers the host failing to negotiate the
experimental.claude/channelcapability during initialization, resulting in channel notifications never being delivered to the plugin runtime. Please follow that issue for updates and feel free to add any additional context there.