stuckgo について
stuckgoは、goroutineダンプから停止状態を調べるGo開発者向けのMCPサーバー兼コマンドラインツールです。スタックを分類し、一般的なアイドル状態を除外して原因候補を順位付けします。サービスに接続せず、ダンプを比較してリークも調査できます。
主な機能
- 01Go goroutineダンプのレビュー
- 02一致するスタックトレースの分類
- 03一般的なアイドルgoroutineの除外
- 04ブロック原因候補の順位付け
- 052つのダンプを比較して増加を確認
- 06JSON出力または重大度に応じたCI失敗
活用シーン
- 01ダンプから停止したGoサービスを調査
- 02ダンプを比較してgoroutineリークを確認
- 03ロックやデータベース接続を待つgoroutineの特定
MCP 対応
MCP サーバーは、テキストまたはファイルから1つのgoroutineダンプをレビューするツールと、同じプログラムの2つのダンプを比較するツールを提供します。ダンプデータを解析し、サービスには接続しません。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
ツール
2掲載時のツール一覧 · 2 週間前に更新
| パラメーター | 型 | 必須 | 説明 |
|---|---|---|---|
| after | string | いいえ | 後の時点で取得した goroutine ダンプのテキスト |
| before | string | いいえ | 前の時点で取得した goroutine ダンプのテキスト |
| after_path | string | いいえ | 後の時点で取得したダンプを含むファイルのパス |
| before_path | string | いいえ | 前の時点で取得したダンプを含むファイルのパス |
詳細
stuckgo:この Go プログラムはなぜ停止しているのでしょうか?
Go のゴルーチンダンプを読み取り、プログラムが何を待っているのかを知らせる MCP サーバーおよび CLI です。ゴルーチンリーク、ロックの滞留、データベース接続プールの枯渇、タイムアウトのない HTTP 呼び出しを見つけます。ダンプのテキストだけを使うため、サービスに接続することはありません。
停止したサービスには数千のゴルーチンが存在することがありますが、そのほとんどは正常です。リスナー、読み取りループ、アイドル状態のワーカーなどです。stuckgo は同一のスタックをまとめ、通常アイドル状態のものを除外し、残ったものを優先度順に並べます。40 個のゴルーチンがミューテックスを待っている場合は、それを保持している可能性のあるものも特定します。
エージェントに質問できること
- 「Go サービスが停止しています。ゴルーチンダンプはこちらです。」
- 「ゴルーチンリークはありますか? 10 分間隔で取得したダンプが 2 つあります。」
- 「このサービスでデータベース接続が足りなくなっているのはなぜですか?」
- 「このミューテックスを保持しているのは何ですか?」
インストール
Claude Code プラグインとして:
claude plugin marketplace add GillMacedo/stuckgo
claude plugin install stuckgo@stuckgo-marketplace
任意のクライアント用の MCP サーバーとして:
{"mcpServers": {"stuckgo": {"command": "npx", "args": ["-y", "[email protected]", "serve"]}}}
Node 18 以降を使う CLI として:
npx stuckgo review dump.txt
または、Go 1.27 以降を使う場合:go install github.com/GillMacedo/stuckgo@latest。
使い方
net/http/pprof をインポートしているサービスからダンプを取得します。プログラムは停止しません。
curl 'http://localhost:6060/debug/pprof/goroutine?debug=2' > dump.txt
kill -QUIT <pid> でもダンプを出力できますが、プロセスは終了します。クラッシュ時の出力も使えます。また、GOTRACEBACK=all を設定すると、失敗したゴルーチンだけでなく、すべてのゴルーチンが表示されます。
以下は、このリポジトリをクローンして実行した、examples/ にある架空のサービスの実際のダンプです。
$ go run . review examples/dumps/mutex-late.txt
43 goroutines, 1 normally idle
Groups:
40 sync.Mutex.Lock, up to 2 min main.(*store).Get (app/main.go:109)
1 chan receive, up to 2 min main.mutex (app/main.go:130)
1 select, up to 2 min main.(*store).Refresh (app/main.go:118)
HIGH SG107 main.(*store).Get (app/main.go:109)
40 goroutines waiting for a mutex, waiting up to 2 minutes. Possible holder, not proven: main.(*store).Refresh (app/main.go:118), in select.
Try: Look at what the holder does while it holds the lock. Don't hold a lock across network calls, channel operations or slow work.
リークを確認するには、時間を空けて取得した 2 つのダンプを比較します。同じゴルーチン ID が同じ場所に残ったままスタックが増えている場合は、単一のダンプよりはるかに強い証拠になります。
$ go run . compare examples/dumps/leak-early.txt examples/dumps/leak-late.txt
Goroutines: 344 -> 637 (+293)
Growing:
+293 342 -> 635 chan send main.lookupPrice.func1 (app/main.go:79)
Same goroutine IDs in the same place in both dumps:
342 chan send, up to 2 min main.lookupPrice.func1 (app/main.go:79)
Go 1.27 以降では、Go 自体にチェックさせることもできます:/debug/pprof/goroutineleak?debug=2。stuckgo はその出力も読み取ります。
JSON を出力するには --json を追加します。CI で終了コード 1 を返すには --fail-on high を追加します。標準入力から読み取るには - を使います。
検出内容
| ルール | 検出内容 |
|---|---|
| SG101 | Go 自身が検出する「すべての goroutine がスリープ中」のデッドロック |
| SG102 | パニックまたは致命的エラーと、その発生箇所 |
| SG103 | Go のリークプロファイルでリークとして報告される goroutine |
| SG104 | nil チャネルへの送信または受信。処理が戻ることはありません |
| SG105 | 空き database/sql 接続を待機している goroutine |
| SG106 | 長時間待機している送信 HTTP 呼び出し |
| SG107 | mutex で待機している goroutine の滞留と、ロックを保持している可能性のある goroutine |
| SG108 | 完了していない WaitGroup と、その開始元 |
| SG109 | チャネルへの送信で停止している goroutine。一般的なリークです |
| SG110 | チャネル受信または select を待機している多数の goroutine |
| SG111 | ネットワーク I/O の長時間待機 |
| SG112 | システムコールの長時間実行 |
| SG113 | sync.Cond.Wait での長時間待機 |
| SG114 | 同じ行でスリープしている多数の goroutine |
| SG115 | 複数のスタックにまたがって、ほとんどの goroutine を開始した関数 |
| SG116 | 実行中、または CPU を待機中の多数の goroutine |
| SG117 | debug=1 のダンプで同じ行に多数の goroutine が停止している状態。停止理由は示されません |
深刻度は、関与する goroutine の数と待機時間によって決まります。これらのしきい値は判断に基づくものです。
MCP ツール
| ツール | 機能 |
|---|---|
go_goroutine_review | テキストまたはファイルから、単一のダンプを確認します |
go_goroutine_compare | 1 つのプログラムの 2 つのダンプを比較します |
制限事項
- 検出結果は手がかりです。 ダンプはある瞬間の状態です。各検出結果をコードと照らし合わせてください。
- ロックの保持者を特定することはできません。 Go のダンプには記録されません。「保持している可能性のある goroutine」とは、同じ型のメソッド内にいる別の goroutine です。
- Go 自身のリークプロファイルを除き、単一のダンプだけではリークを証明できません。 アイドル状態のワーカープールもリークと同じように見えるため、2 つのダンプを比較してください。
debug=1で得られる情報は少なくなります。 件数はわかりますが、待機時間や goroutine ID はなく、goroutine が何を待っているかもほとんど表示されません。可能であればdebug=2を使ってください。- 待機時間はおおよその値です。 Go が待機時間を出力するのは 1 分以上の場合に限られ、計測はガベージコレクション時に始まるため、時間が表示されていなくても待機時間が短いとは限りません。
- CPU 使用率は確認できません。 停止中の goroutine がないのにプログラムが遅い場合は、CPU プロファイルを取得してください。
- 通常はアイドル状態となるスタックの一覧は限られています。 Go ランタイム、
net/http、database/sql、gRPC、golang.org/x/net/http2です。その他のライブラリのバックグラウンドループはグループとして表示されますが、検出結果になることはほとんどありません。 - ダンプには、ファイルパスやプログラムの値が含まれることがあります。
stuckgoはローカルマシン上で動作しますが、MCP クライアントはツールの結果をモデルに送信します。
よくある質問
Go の goroutine リークを見つけるにはどうすればよいですか? 時間を空けて 2 つのダンプを取得し、stuckgo compare を実行してください。goroutine ID が変わらないままスタックが増えている場合は、確認が必要です。
実行中の Go プログラムから goroutine ダンプを取得するにはどうすればよいですか? net/http/pprof をインポートしてから、/debug/pprof/goroutine?debug=2 を取得してください。このエンドポイントは非公開にしてください。
goroutine ダンプの [chan send, 5 minutes] は何を意味しますか? その goroutine は、受信する goroutine がいないチャネルへの送信で約 5 分間ブロックされています。SG109 がこれを検出します。
Go サービスでデータベース接続が不足するのはなぜですか? database/sql.(*DB).conn で待機している goroutine がある場合、接続プールが満杯です。SG105 がこれを検出します。クエリの遅延や、クローズされていない Rows など、接続を保持している原因を調べてください。
panicparse や goroutine-inspect とは何が違いますか? panicparse と goroutine-inspect は、人がダンプを読み解いて調査するのに役立ちます。stuckgo はエージェント向けに優先度を付けた検出結果を提示し、2 つのダンプを比較でき、通常はアイドル状態のものを除外します。
テスト済み
examples/programs/内の実際のプログラム: SG101 から SG109 までのすべてのルールに加え、SG111、SG114、SG117 が、実際に停止したプログラムのダンプ(examples/dumps/内)で検出されました。ほとんどは Go 1.22 でも実行されました。- 実際のサービス:
examples/dumps/real/内のアイドル状態の Prometheus 3.7.3 と etcd 3.6.5 では、低より上の重要度の検出はありませんでした。Prometheus では低重要度のWaitGroupの検出が2件あり、これは通常の動作です。 - 記述例のみ: SG110、SG112、SG113、SG115、SG116。
- MCP ツールの呼び出しは MCP Inspector を経由しました。
本番環境のインシデントでまだ使用されておらず、新しい Claude セッションでも試されていません。
コントリビューション
個人の方も、AI エージェントと協業している方も、コントリビューションを歓迎します。CONTRIBUTING.md をご覧ください。初めての方に適した Issue はこちらに一覧があります。
プルリクエストの流れ
- PR を作成します。 リポジトリをフォークしてブランチで作業し、変更は1つに絞ってください。変更内容がユーザーにどう影響するかを示すタイトルを付けてください。タイトルは変更履歴の項目になるため、
feat:は Added に、fix:は Fixed に分類されます。 - 自動チェック。 コードの変更にはテストが必要で、
make testが成功しなければなりません。30行を超える変更には、リンクされたオープンな Issue(Closes #<number>)が必要で、PR の変更行数は最大800行です。不足がある場合は、修正内容をコメントでお知らせします。 - レビュー。 Claude が各 PR を自動的にレビューします。コードの PR では、セキュリティレビューも行われ、サンドボックスでテストされます。その後、レビューにより PR がマージされるか、変更が求められます。CI、エージェント設定、npm または Python の依存関係、ベンダーコード、ビルドファイルを変更する PR はこの方法ではマージできず、その旨をコメントでお知らせします。セキュリティレビューで承認されなかったものも同様です。
- リリース。 ユーザーがインストールするものに変更があるマージは、約2時間以内に新しいバージョンとしてリリースされ、GitHub Release と、プロジェクトに存在する場合は npm パッケージも公開されます。ドキュメントと CI の変更は次のリリースまで保留されます。
AI による生成について
このプロジェクトは主に Claude Code で生成され、メンテナーによるレビューを受けています。
自己紹介
私は Giulliano Macedo です。GoTech Solutions で開発者向けツールを開発しています。最新情報をチェックしたり、お気軽にご連絡ください。
- GitHub:GillMacedo
- LinkedIn:giulliano
- X:@giumacedo
stuckgo がプログラムの停止原因を見つけた場合は、スターを付けていただけると、ほかの人にも見つけてもらいやすくなります。
Giulliano Macedo 作、GoTech Solutions · LinkedIn · X
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。