Telegram Channels: inbound messages never reach the session (outbound works)

Status Open
Reported on v2.1.209
Maintainer reply None cached
Activity 1 comment · opened Jul 20, 2026

Summary

With the Telegram channel configured and paired correctly, outbound messages work (the reply MCP tool successfully delivers to Telegram), but inbound messages from Telegram never reach the session as a <channel source="telegram"> turn — even though the plugin's own server.ts confirms the sender is allowlisted and calls mcp.notification({method: 'notifications/claude/channel', ...}) with the message content.

Environment

  • Claude Code: 2.1.209, running via the macOS desktop app (Claude.app)
  • macOS: 15.5 (24F74)
  • Telegram plugin: directory version 0.0.6 (claude-plugins-official/telegram/0.0.6), but package.json inside reports "version": "0.0.1" — noting the mismatch in case it's relevant
  • Bun: 1.3.14
  • Plugin confirmed already at the latest available version via /plugin update

Steps to reproduce

  1. Install the Telegram plugin: /plugin marketplace add anthropics/claude-plugins-official, /plugin install telegram@claude-plugins-official, /reload-plugins.
  2. Configure a bot token via /telegram:configure.
  3. Ensure the session is running with the channel active (in this case the plugin was auto-loaded by the desktop app rather than via an explicit --channels CLI flag).
  4. Pair: DM the bot from Telegram, receive a 6-character pairing code, approve it with /telegram:access pair <code>.
  5. DM the bot again with a plain text message (e.g. "hi").

Expected

The message should surface in the session as documented in the plugin's own MCP server instructions:

"Messages from Telegram arrive as <channel source="telegram" chat_id="..." message_id="..." user="..." ts="...">"

...allowing a reply via the reply tool.

Actual

Telegram shows a "typing…" indicator (confirming the bot process received the message and gate() ran), but no message ever appears in the session. This was reproduced consistently across multiple send attempts and a full pairing reset.

Diagnosis already ruled out

Before filing, we went through the following to isolate the cause (happy to share the full transcript if useful):

  • Duplicate poller: found and killed a second bun server.ts process from a separate, unrelated session that was also holding the same bot token (Telegram's getUpdates only allows one consumer). Confirmed via ps/lsof that only one poller remained afterward, holding the live ESTABLISHED connection to Telegram's API (149.154.166.110:https).
  • Access config: confirmed ~/.claude/channels/telegram/access.json correctly has the sender's ID in allowFrom after pairing.
  • Server source review: read server.ts directly. gate() returns { action: 'deliver', access } for an allowlisted DM sender as expected. handleInbound() then calls mcp.notification({ method: 'notifications/claude/channel', params: { content: text, meta: {...} } }) — this is the mechanism that should become a visible turn.
  • Outbound sanity check: called the reply MCP tool directly with the known chat_id from access.json (no inbound message required, since Telegram DM chat_id == user_id). It succeeded and the message was received in Telegram — confirming the MCP tool-call direction and the bot's Telegram API connection both work fine.
  • Full pairing reset: cleared access.json back to a fresh pairing state, had the sender re-DM the bot for a new code, re-ran /telegram:access pair <code>. Same result — outbound works, inbound never surfaces.
  • Plugin already latest: /plugin update telegram@claude-plugins-official reported no update available.

Given the plugin declares 'claude/channel': {} as an experimental MCP capability, and everything on the plugin's own side of the pipeline (gating, notification dispatch) checks out, this looks like a gap in how this specific harness build (2.1.209, desktop app) receives/surfaces that experimental notification type — not a config or plugin-version issue.

Additional notes

  • The Telegram plugin was auto-loaded into this session by the desktop app without an explicit --channels CLI invocation, which may be relevant if --channels triggers additional wiring that auto-load doesn't.
  • server.ts has its own stale-poller self-healing (kills a PID recorded in bot.pid on startup if still alive) — worth knowing this exists if others hit the duplicate-poller symptom first.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗