MCP stdio サーバが Windows で「接続済み」のまま死んでいるのを見抜く
MCPサーバ一覧が緑でもツール呼び出しだけ spawn ENOENT で死ぬ故障の3層構造と、initialize/tools-list/tools-call を層ごとに実測して証明する手順。npx形式が作る初回だけの接続タイムアウト、設定スキーマ移行による静かな認証無効化、自動修復コードが鍵を空文字で潰す罠も含む。
約5.3万トークンの節約 (API料金換算で約80円分)。 要件定義・技術調査・試行錯誤ぶんのトークンがまるごと不要になります。※ 出品者申告とレビューに基づく推定値。モデル・タスク内容により変動します。
この巻物について
「MCP stdio サーバが Windows で「接続済み」のまま死んでいるのを見抜く」は、開発プロセスカテゴリのAI指示書(MDファイル)です。MCPサーバ一覧が緑でもツール呼び出しだけ spawn ENOENT で死ぬ故障の3層構造と、initialize/tools-list/tools-call を層ごとに実測して証明する手順。npx形式が作る初回だけの接続タイムアウト、設定スキーマ移行による静かな認証無効化、自動修復コードが鍵を空文字で潰す罠も含む。この巻物をAIに読み込ませると、ゼロから設計・調査する場合に比べて 約5.3万トークン(API料金換算で約80円)・85%のトークンを節約できます。
- カテゴリ
- 開発プロセス
- 対応AI
- claude-code、cursor、codex-cli
- ライセンス
- 商用利用可 (再販不可)
- 価格
- 無料
- ゼロから開発時
- 約6.2万トークン
- この巻物使用時
- 約9,000トークン
- 節約量
- 約5.3万トークン (約80円)
- 更新日
- 2026-08-30
使い方 (AIに渡す3つの方法)
いちばん簡単なのはワンライナー。Claude Code のターミナルに貼るだけです。
claude "https://makimono-md.vercel.app/api/v1/files/mcp-stdio-windows/raw を読み込んで、この指示書どおりに実装して"
中身
MCP stdio サーバが Windows で「接続済み」のまま死んでいるのを見抜く
AI コーディングエージェント(MCP クライアント)に stdio 型の MCP サーバを登録したのに、 サーバ一覧では緑の「Connected」なのに、ツールを呼んだ瞬間だけ失敗する——という故障がある。 接続表示を信じたせいで原因の切り分けが数セッションにわたって空転しやすい。
この指示書は、その故障の3つの層と、どこまで通ったかを層ごとに証明する手順を与える。
前提
- MCP サーバを stdio(子プロセスを spawn して JSON-RPC を標準入出力で話す)で登録している
- 実行環境が Windows
- 登録は
{ "type": "stdio", "command": "...", "args": [...], "env": {...} }の形
1. なぜ「Connected」が嘘をつくのか
クライアントが Connected と表示する条件は MCP サーバ本体との initialize ハンドシェイクが成立したことだけ。
そのサーバが内部でさらに別の CLI を子プロセスとして起動するラッパー型(よくある形)の場合、
ラッパー自身は正常に起動するので Connected になる。中の CLI の起動失敗は、ツールを呼ぶまで表面化しない。
したがって故障は独立した3層に分かれる。
| 層 | 症状 | 一覧表示 |
|---|---|---|
| ① クライアント → MCP サーバの起動 | CONNECT_TIMEOUT / CONNECTION_CLOSED | 赤 |
| ② MCP サーバの初期化・ツール登録 | ツール一覧が空 | 緑 |
| ③ MCP サーバ → 内部 CLI の spawn | ツール呼び出しだけ spawn <cli> ENOENT | 緑 |
**①②が緑でも③で死ぬ。**③まで実際に叩かない限り「直った」と言ってはいけない。
2. Windows 固有の真因:spawn("cli") は .cmd を起動できない
Node.js のラッパーが spawn("mytool") を shell なしで呼んでいると、Windows では必ず ENOENT になる。
グローバルインストールされた npm パッケージの実体は
%APPDATA%\npm\mytool.cmd(バッチ)%APPDATA%\npm\mytool(sh 用 shim。Windows からは実行できない)
の2つで、拡張子なしの mytool という実行可能ファイルは存在しない。
Windows の CreateProcess は PATHEXT 解決をしないので、spawn("mytool") は解決に失敗する。
派生: spawn("npx", ...) も同じ理由で死ぬ(npx.cmd しか無い)。
「--allow-npx を付ければ回避できる」は成立しない。
回避の選択肢
.cmdを解決するラッパーに乗り換える(メンテされている代替パッケージを探す。これが最短)- 自前の最小 MCP サーバを書く。Windows なら
cmd.exe /c mytool.cmd、それ以外は素のmytoolを spawn する。 JSON-RPC は改行区切り JSON なので外部依存ゼロで書ける spawn(cmd, args, { shell: true })にする(引数のエスケープが自前になる点に注意)
更新が止まったラッパーパッケージを使い続けない。 最終更新が1年以上前なら、 Windows 対応の修正はもう入らないと考えて乗り換えるほうが速い。
3. 副次の罠:起動コマンドの形が接続タイムアウトを作る
登録を command: "npx", args: ["-y", "<package>"] にすると、
そのマシンで初回だけ npx がパッケージをダウンロードする。
MCP クライアントの接続タイムアウトは短い(30秒程度)ので、
初回起動だけ CONNECT_TIMEOUT になり、2回目以降はキャッシュが効いて通る。
- 「昨日は繋がらなかったのに今日は繋がる」はこれ。設定を直したからではない
- 検証機がキャッシュ済みだと再現しないので「直った」と誤判定する
- 恒久対策は
command: "node", args: ["<絶対パス>"](ローカル実体を直に起動)
配布する自動修復コードが登録を無条件で npx 形式へ書き戻していないか確認する。 1台で直しても、配布側が npx 形式を強制していれば、次回起動で全台が元に戻る。
4. 認証設定のスキーマ移行で静かに無効化される
CLI 側の設定ファイルは、メジャー更新でキーの位置が変わることがある。 古いキーはエラーにならず単に読まれないので、「認証方式が未設定です」とだけ言われて原因が見えない。
- 旧:
{ "selectedAuthType": "..." }(トップレベル) - 新:
{ "security": { "auth": { "selectedType": "..." } } }(ネスト)
症状が「設定したはずの値が効いていない」なら、まず CLI の現行バージョンの設定スキーマを確認する。
また、ヘッドレス実行では「信頼されたディレクトリではない」で止まる CLI があるため、
--skip-trust 相当のフラグか、それに対応する環境変数を登録の env に入れる。
5. 層ごとに証明する手順(これをやる)
手順 A: 層②を証明する — initialize + tools/list を直に叩く
登録とまったく同じ command / args / env で子プロセスを起こし、時間を測る。
// probe-mcp.mjs — 使い方: node probe-mcp.mjs <command> <args...>
import { spawn } from 'node:child_process';
const [, , cmd, ...args] = process.argv;
const t0 = Date.now();
const env = { ...process.env, /* 登録の env をここに再現する */ };
const p = spawn(cmd, args, { stdio: ['pipe', 'pipe', 'pipe'], env, shell: process.platform === 'win32' });
let buf = '', err = '';
p.stderr.on('data', (d) => { err += d; });
const send = (o) => p.stdin.write(JSON.stringify(o) + '\n');
p.stdout.on('data', (d) => {
buf += d;
for (const line of buf.split('\n').slice(0, -1)) {
if (!line.trim()) continue;
let m; try { m = JSON.parse(line); } catch { continue; }
if (m.id === 1) {
send({ jsonrpc: '2.0', method: 'notifications/initialized' });
send({ jsonrpc: '2.0', id: 2, method: 'tools/list', params: {} });
}
if (m.id === 2) {
console.log(`OK in ${Date.now() - t0}ms tools=[${(m.result?.tools || []).map((t) => t.name).join(', ')}]`);
p.kill(); process.exit(0);
}
}
buf = buf.slice(buf.lastIndexOf('\n') + 1);
});
send({ jsonrpc: '2.0', id: 1, method: 'initialize', params: { protocolVersion: '2024-11-05', capabilities: {}, clientInfo: { name: 'probe', version: '0' } } });
setTimeout(() => { console.log(`TIMEOUT ${Date.now() - t0}ms`); console.log('stderr:', err.slice(0, 800)); p.kill(); process.exit(1); }, 90000);
**所要ミリ秒を必ず記録する。**クライアントの接続タイムアウトと比較するための数字であって、 「通った / 通らない」の二値では §3 の初回ダウンロード問題を見逃す。
手順 B: 層③を証明する — tools/call を1回だけ実行する
**ここを飛ばすと検証にならない。**手順 A のスクリプトの tools/list を差し替える。
send({ jsonrpc: '2.0', id: 3, method: 'tools/call',
params: { name: '<ツール名>', arguments: { /* 最小の引数 */ } } });
判定はレスポンス本文で行う。
spawn <cli> ENOENT→ §2 の未解決。配線の問題- 上流 API のエラー(クォータ超過・レート制限・認証失敗)→ 配線は生きている。 子プロセスの起動・認証・ネットワーク到達まで全部通った証拠なので、これは合格として扱う
- 正常応答 → 合格
**「上流のクォータ超過」を故障と読み違えないこと。**それは到達できた証明であって、設定の不具合ではない。
手順 C: 変更したら読み戻す
登録ファイルを書き換えたら、書いた直後に読み直して command / args / 必須の環境変数キーの有無を表示する。秘匿値は長さだけ出す(値を出力しない)。
6. 自動修復コードのレビュー観点
登録を自動で書き戻す仕組みを配っている場合、次の2つを必ず確認する。
- 秘密を無条件に上書きしていないか。
env: { API_KEY: keyReadFromSomewhere }と書くと、読み取りに失敗した環境で 空文字が既存の有効な鍵を上書きして潰す。既存値を引き継ぐか、読めなければ書き換えを中止する - 「互換」の判定が形の一致になっていないか。
argsに特定の文字列が含まれるかで互換性を判定すると、 より良い形(絶対パス直起動など)に手で直した端末を毎回壊しに行く
7. チェックリスト
- 一覧の Connected を根拠にしていない
- 手順 A で initialize + tools/list を実測し、所要ミリ秒を記録した
- 手順 B で
tools/callを1回実行し、ENOENT が出ないことを確認した - 上流のクォータ/レート上限エラーを「配線は生きている」と正しく解釈した
- 起動コマンドが初回ダウンロードを伴う形になっていない(または許容できると確認した)
- CLI 側の設定スキーマが現行バージョンのものになっている
- 自動修復コードが鍵を空文字で潰さない/手で直した登録を壊さない
- 書き換え後に読み戻して確認した(秘匿値は長さのみ表示)
よくある質問
+「MCP stdio サーバが Windows で「接続済み」のまま死んでいるのを見抜く」とは何ですか?
MCPサーバ一覧が緑でもツール呼び出しだけ spawn ENOENT で死ぬ故障の3層構造と、initialize/tools-list/tools-call を層ごとに実測して証明する手順。npx形式が作る初回だけの接続タイムアウト、設定スキーマ移行による静かな認証無効化、自動修復コードが鍵を空文字で潰す罠も含む。
+どれくらいトークン(費用)を節約できますか?
ゼロから開発すると約6.2万トークンかかりますが、この巻物を使えば約9,000トークンで済みます。差し引き約5.3万トークン(API料金換算で約80円)・85%の節約です。
+どうやって使いますか?
無料です。MDファイルを Claude Code などのAIに読み込ませるだけ。ワンライナーをターミナルに貼れば実装が始まります。要件定義や技術調査を省いて実装だけにトークンを使えます。
+どのAIツールに対応していますか?
claude-code、cursor、codex-cli に対応しています。
+商用利用できますか?
ライセンスは「商用利用可 (再販不可)」です。
🤝 自分でAIを動かすのは、まだ不安…という方へ
この巻物の内容を、AIを使うプロに丸ごと任せることもできます。姉妹サービスAI代行堂なら「LINEで頼むだけで、仕事が完成」。
関連する巻物
ドキュメント駆動開発プロセス CLAUDE.md — 作るものを固めてから書かせる
「AIが暴走して意図と違うものを作る」を根絶する開発プロセス指示書。UI仕様→機能設計→実装の順をAIに強制し、1ファイルごとに承認ゲートを挟む。受託開発・チーム開発向け。
AIに指示書マーケットを自動参照させ、終了時に自動出品させるMD
開発依頼を受けた瞬間にマーケットの完成済み指示書を検索してAIに読ませ、セッション終了時には汎用ノウハウを自動出品させる仕組みの作り方。全台配布・秘密情報スキャン・実際に踏んだ配布バグ3つの回避込み。
「そのPCにしか直せない障害」をAIに自分で気付かせて着手させる
特定の1台にしかリポジトリが無い機能は、修正手順を書いても誰にも実行されず放置される。SessionStart hook で当該PCのAIだけに指示を出し、完了後は指示書へ状態を書き戻して再実装事故を防ぐ型。走査の時間予算とセッション跨ぎの再開、メール一致だけの自動承認がなりすまされる理由と署名キー方式、状態問い合わせAPI、鍵の自動配布、no-op通知の抑止まで、実際に94件の滞留を解消した実例に基づく手順。
この巻物、誰かのトークンも救えます
𝕏 で節約レシートをシェア