anthropics / anthropics/claude-plugins-official

telegram plugin: MCP server doesn't exit on stdin EOF — orphaned bun processes accumulate

オープン 初心者向け
#3,702 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る
主要言語
Python
スター
36.3k
フォーク
4.1k
平均マージ
2日 14時間
マージ済み PR(30日)
539

説明

## Summary

`external_plugins/telegram/server.ts` (v0.0.6) doesn't exit when its parent Claude Code process closes stdin. On a machine that opens Claude Code sessions daily over multiple weeks, orphan `bun server.ts` processes accumulate (35 on my machine after 38 days of uptime), each keeping the event loop hot via `grammy`'s long-polling loop and consuming 50–80% CPU.

Filed diagnosis + repro at anthropics/claude-code#73814. This issue is the targeted plugin-side fix that was recommended in that thread.

## Root cause

MCP servers using `StdioServerTransport` rely on the stdin-EOF convention for shutdown — when the parent (Claude Code) exits, the child's stdin reaches EOF, and a well-behaved server treats that as "my transport is gone, exit."

`grammy`'s `Bot.start()` starts a long-polling loop that keeps timers/sockets pending. Node/Bun only auto-exit when the event loop is empty — grammy guarantees it never is. So even after stdin closes, the process survives, gets reparented to `init` (`ppid=1`), and polls Telegram forever.

## Fix

Add stdin-EOF guards in `server.ts` after the transport is wired:

```ts
process.stdin.on('close', () => {
void bot.stop().finally(() => process.exit(0));
});
// Belt-and-suspenders — some hosts close the fd rather than end the stream:
process.stdin.on('end', () => process.exit(0));
```

`bot.stop()` cleanly halts grammy's polling loop before exit, so we don't drop a mid-flight update on the floor.

## Cleanup on install/update (nice-to-have)

Existing users have accumulated orphans that a code fix won't touch. Consider a small "kill orphaned instances before starting" step in the plugin's `start` script:

```bash
# in scripts.start, before `bun install && bun server.ts`
for pid in $(pgrep -f "bun server.ts" 2>/dev/null); do
[ "$(ps -o ppid= -p "$pid" | tr -d ' ')" = "1" ] || continue
cwd=$(lsof -p "$pid" 2>/dev/null | awk '/cwd/{print $NF; exit}')
[[ "$cwd" == *"claude-plugins-official/telegram"* ]] && kill "$pid"
done
```

Filters on `ppid=1` so it never touches a server attached to a live Claude Code session.

## Verified on

- macOS 26.5 · Mac Studio M1 Max
- Claude Code 2.1.199
- bun 1.3.11
- `claude-plugins-official/telegram` v0.0.6
- `@modelcontextprotocol/sdk` ^1.0.0, `grammy` ^1.21.0

## Impact

Aggregate 700%+ CPU across 35 orphans, ~4 GB RAM, load average 100+ on a 10-core machine.

Happy to open a PR with the `server.ts` change if that's welcome.

コントリビューションガイド

このリポジトリのコントリビューションガイドは索引されていません

調査の方向性

The target is external_plugins/telegram/server.ts; start by reading where StdioServerTransport is wired and how grammy polling is started. Reproduce stdin closure with the Telegram plugin, then verify that EOF stops polling and no orphan bun server.ts process remains. The install/update cleanup is explicitly a nice-to-have.

索引モデルが issue の本文から書いたものです。

評価

技術スタック
bun, typescript
領域
backend
issue の種類
バグ
難易度
2/5
見積もり時間
1〜3時間
活発さ
静か
明瞭さ
明確に書かれている
初心者へのやさしさ
78/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。