Telegram channel plugin: bun child receives SIGTERM every few minutes, auto-respawn doesn't re-register MCP tools
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/telegraminstall dir0.0.6, package.jsonversion: 0.0.1 - Runtime:
bun(whatever version ships with the user's machine) - Launch:
claude --channels plugin:telegram@claude-plugins-official
Reproduction
- Launch Claude Code with
--channels plugin:telegram@claude-plugins-official. - Leave the session idle (or actively use it — both reproduce).
- 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".
/reload-pluginsrestores them temporarily.- 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 ofshutdown() 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
- 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?)
- If this is intentional, an option to auto-re-register tools after the
child respawns, so /reload-plugins isn't required.
- 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.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗