Telegram channel plugin: bun child receives SIGTERM every few minutes, auto-respawn doesn't re-register MCP tools

Resolved 💬 3 comments Opened May 11, 2026 by 0xKMG Closed May 14, 2026

Summary

The Telegram channel plugin (plugin:telegram@claude-plugins-official) runs an
MCP server as a bun server.ts child process. During an interactive session
that uses --channels plugin:telegram@claude-plugins-official, the bun child
receives SIGTERM at irregular intervals (sometimes within seconds of being
spawned, sometimes after several minutes) and exits. Claude Code respawns a
new bun child within ~1 second, but the parent session's tool registry stays
stale — mcp__plugin_telegram_telegram__* tools become unavailable until the
user manually runs /reload-plugins in the REPL.

The bun child is not crashing. The plugin's own error handling (bot.catch(),
async retry loop with exponential backoff, orphan watchdog) is fine — none of
those code paths are firing. The kill is coming from the parent process.

Environment

  • Claude Code: 2.1.138
  • OS: macOS 26.3.1 (Darwin 25.3.0, arm64)
  • Plugin: claude-plugins-official/telegram install dir 0.0.6, package.json version: 0.0.1
  • Runtime: bun (whatever version ships with the user's machine)
  • Launch: claude --channels plugin:telegram@claude-plugins-official

Reproduction

  1. Launch Claude Code with --channels plugin:telegram@claude-plugins-official.
  2. Leave the session idle (or actively use it — both reproduce).
  3. Within minutes, mcp__plugin_telegram_telegram__reply (and the other 3

telegram MCP tools) stop being callable; a system reminder appears saying
"MCP server disconnected".

  1. /reload-plugins restores them temporarily.
  2. They die again within minutes. Cycle repeats indefinitely.

Evidence

I patched a single instrumented line into the plugin's shutdown() function
in server.ts (see ~/.claude/plugins/cache/claude-plugins-official/telegram/0.0.6/server.ts),
recording the death reason and parent PID state. Each call site of
shutdown() passes a distinct reason string (stdin.end, stdin.close,
SIGTERM, SIGINT, SIGHUP, or an orphan ... string with stdin/ppid
diagnostics).

After running the patched session for ~10 minutes, every single death
entry was reason=SIGTERM with ppid equal to bootPpid (the parent
recorded at process start). The orphan watchdog never fired. stdin EOF
never fired. Only SIGTERM from the unchanged parent.

2026-05-11T07:38:55.606Z pid=69805 ppid=69803 bootPpid=69803 reason=SIGTERM
2026-05-11T07:42:12.277Z pid=70957 ppid=70955 bootPpid=70955 reason=SIGTERM
2026-05-11T07:43:34.565Z pid=81067 ppid=81065 bootPpid=81065 reason=SIGTERM
2026-05-11T07:43:56.198Z pid=85322 ppid=85320 bootPpid=85320 reason=SIGTERM   ← only 22s after previous respawn
2026-05-11T07:47:13.541Z pid=86518 ppid=86516 bootPpid=86516 reason=SIGTERM

The 22-second interval (entry 4) is notable: the previous bun child had
just spawned and barely begun polling Telegram before being killed again.
This makes "long-poll hang triggers an unresponsive timeout" unlikely as
a root cause, since there was effectively no long poll to hang.

Expected vs actual

  • Expected: MCP server stays alive for the lifetime of the parent Claude

session, or — if the harness has a reason to recycle it — the new child is
re-attached transparently and the tools stay available.

  • Actual: Child is killed every few minutes; respawn happens but tools

don't re-register until /reload-plugins.

Impact

For sessions that depend on the Telegram channel (notifications, scheduled
agents posting to chat, inbound message handlers), every Telegram-bound
operation silently fails between deaths and the next manual /reload-plugins.
There is no error surfaced to the user — the tool simply isn't there.

Workaround in place

External launchd agent watching the death log, injecting /reload-plugins
via screen -X stuff into the cron-host's screen session. Functional but
clearly a band-aid.

Asks

  1. Documentation or source pointer to **what condition causes Claude Code to

send SIGTERM** to a stdio MCP server while the session is otherwise
healthy. (Idle timer? Health-check failure? Memory pressure heuristic?
Plugin manifest field I'm missing?)

  1. If this is intentional, an option to auto-re-register tools after the

child respawns, so /reload-plugins isn't required.

  1. If this is a bug, a fix to stop killing channel plugins that have

declared themselves long-running.

Happy to share the shutdown(reason) patch as a small reproducer if useful.

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗