ANTIGRAVITY LABEN
記事一覧/連携・プラグイン
連携・プラグイン/2026-09-18中級

MCP が欠けたまま始まったセッションを、どこで止めるか

MCP サーバーの初期化に失敗したまま始まったセッションで、エージェントは黙って別の手段に落ちます。ツールが本当に見えているかを手元で確かめる小さなスクリプトと、欠けていたときに取る三つの分岐をあらかじめ決めておく話です。

MCP25Antigravity372運用11判断基準Node.js7

朝いちばんに Antigravity を開いて、前日の続きを頼みました。返ってきた要約はそれらしく、読んでいて引っかかるところもありませんでした。

手が止まったのは、しばらく経ってログを遡ったときです。繋いだはずの MCP サーバーのツール呼び出しが、どこにも見当たりませんでした。起動時に初期化が失敗していて、私はそれに気づかないまま作業を進めていたのです。

同じ状態はフォーラムにも上がっています。起動のたびに MCP の初期化に失敗するという報告では、手動で更新をかけたあとで初めて接続が作られ、サーバー自体は単体なら正常に動く、という切り分けまで済んでいます。

厄介なのは直し方ではありません。揃っていない状態で始まってしまったことに、いつ、どうやって気づくかのほうです。

欠けていることを、エージェントは教えてくれません

MCP のツールが読み込まれていないとき、エージェントは止まりません。手元にある別の手段——web 検索、ファイル走査、記憶している一般論——へ黙って移り、それらしい答えを返してきます。

出力は一見まともです。だからこちらも疑いません。私も最初のうち、結果が返っていれば繋がっているものと見なしておりました。結果は芳しくありませんでした。半日ぶんの作業を、参照していないはずの情報を根拠にして進めていたことが、あとから分かったのです。

欠落が見えるのは、ツールが「失敗した」ときだけです。**黙って代替へ落ちた場合、失敗という出来事そのものが発生していません。**ログにエラーが無いのは、正常だからではなく、何も試みられていないからでした。私が見張るべきだったのは失敗の数ではなく、試みの数だったのだと気づいたのは、そのあと出所を辿り直しているときでした。

ここで設定ファイルを開いても、たいてい何も見つかりません。文法は正しく、サーバーは手で叩けば動きます。設定ファイルの静的な検査は起動前の 40 行で足りますが、今回の症状はその先——実際にハンドシェイクが成立したかどうか——にあります。

手元で tools/list を叩いて、名前を目で見る

確かめ方はひとつです。エディタの表示ではなく、自分で MCP サーバーへ接続して、返ってくるツール名を読むことです。

MCP は標準入出力の上を流れる JSON-RPC ですので、initialize を送り、notifications/initialized を返し、tools/list を投げれば、見えるはずのツールが名前の配列で戻ってきます。次のスクリプトは設定ファイルに並んだサーバー全部にこれを行い、必須のツールが揃っていなければ終了コード 1 で落ちます。

// mcp-readiness.mjs — 設定のサーバーへ実際に接続し、見えているツール名を出します
// 使い方: node mcp-readiness.mjs <mcp_config.json> [必須ツール名 ...]
import { spawn } from 'node:child_process';
import { readFileSync } from 'node:fs';
 
const [configPath, ...required] = process.argv.slice(2);
const config = JSON.parse(readFileSync(configPath, 'utf8'));
const servers = config.mcpServers ?? config.servers ?? {};
 
const PROTOCOL_VERSION = '2025-06-18';
const TIMEOUT_MS = 8000;
 
function listTools(name, spec) {
  return new Promise((resolve) => {
    const child = spawn(spec.command, spec.args ?? [], {
      env: { ...process.env, ...(spec.env ?? {}) },
      stdio: ['pipe', 'pipe', 'pipe'],
    });
 
    let buffer = '';
    let settled = false;
    const done = (result) => {
      if (settled) return;
      settled = true;
      child.kill();
      resolve({ name, ...result });
    };
 
    const timer = setTimeout(
      () => done({ ok: false, reason: `${TIMEOUT_MS}ms 以内に応答がありません` }),
      TIMEOUT_MS,
    );
    child.on('error', (e) => { clearTimeout(timer); done({ ok: false, reason: e.message }); });
 
    const send = (msg) => child.stdin.write(JSON.stringify(msg) + '\n');
 
    child.stdout.on('data', (chunk) => {
      buffer += chunk;
      let index;
      while ((index = buffer.indexOf('\n')) >= 0) {
        const line = buffer.slice(0, index).trim();
        buffer = buffer.slice(index + 1);
        if (!line) continue;
        let msg;
        try { msg = JSON.parse(line); } catch { continue; } // ログ行が混ざることがあります
        if (msg.id === 1) {
          send({ jsonrpc: '2.0', method: 'notifications/initialized' });
          send({ jsonrpc: '2.0', id: 2, method: 'tools/list', params: {} });
        }
        if (msg.id === 2) {
          clearTimeout(timer);
          done({ ok: true, tools: (msg.result?.tools ?? []).map((t) => t.name) });
        }
      }
    });
 
    send({
      jsonrpc: '2.0',
      id: 1,
      method: 'initialize',
      params: {
        protocolVersion: PROTOCOL_VERSION,
        capabilities: {},
        clientInfo: { name: 'mcp-readiness', version: '1.0.0' },
      },
    });
  });
}
 
const results = await Promise.all(
  Object.entries(servers).map(([name, spec]) => listTools(name, spec)),
);
 
const seen = new Set();
for (const r of results) {
  if (r.ok) {
    r.tools.forEach((t) => seen.add(t));
    console.log(`OK   ${r.name}  ${r.tools.length} tools: ${r.tools.join(', ')}`);
  } else {
    console.log(`NG   ${r.name}  ${r.reason}`);
  }
}
 
const missing = required.filter((t) => !seen.has(t));
if (missing.length > 0) {
  console.log(`\n欠けている必須ツール: ${missing.join(', ')}`);
  process.exit(1);
}
console.log('\n必須ツールは揃っています');

こう書いたのには理由があります。数ではなく名前を出すようにしたのは、サーバーが起動していても公開されるツールが版によって減ることがあったからです。「3 本見えている」では足りず、「その 3 本のうちに必要な 1 本があるか」まで確かめないと、同じ取り違えを繰り返します。

標準出力の JSON 解析で catch { continue; } としているのも実地の都合です。起動直後に人間向けのログを混ぜて吐くサーバーがあり、行ごとに読み飛ばせないと最初の 1 行で落ちてしまいます。

タイムアウトを 8 秒に置いたのは、初回に依存を取りに行くサーバーで 3 秒では足りず、逆に 30 秒にすると「立ち上がらない」ことに気づくのが遅れたためです。

欠けていたときの三つの分岐

確かめる手段ができても、その先を毎回考えていては手が止まります。分岐は先に決めておきます。

状況取る手払うもの
手動の再読み込みで名前が揃ったそのまま続けます。ただし揃ったことをもう一度名前で確認してからにします数十秒
再読み込みしても 1 本だけ立たないその 1 本を前提にした頼み方をやめ、必要なものは自分で取得して貼り付けます手間は増えますが、参照元は確実になります
書き込み系・権限を伴うサーバーが立たないそのセッションでは着手しません。設定の側を直してから改めますその日の予定が 1 件ずれます

分け方の軸は、そのツールが取りに行くもの書き込むものかです。

直感に反しますが、危ないのは取りに行く側でした。書き込み系が欠けていれば、変更が起きないのですぐ分かります。取りに行く側が欠けたときは、それらしい代替が返り、そのまま成果物に混ざります。あとから出所を辿り直す時間のほうが、はるかに高くつきました。順序が逆であれば、もう少し早く手を止められたかもしれません。

いまは、読み取り系が 1 本でも欠けていたらその作業を止める、という線引きにしています。エージェントの能力の問題ではなく、私が結果を信じてよい根拠が無い、というだけの話です。

止める合図は、自分の手順の側に置く

この一件で決めたのは、確認をエージェントに依頼しないことでした。「MCP は使えていますか」と尋ねても、返事は当人の見えている範囲に依存します。見えていないことは、見えていないままです。

揃っているかどうかは、エージェントにではなく自分の手順に聞く。 この線引きだけは、急いでいる日でも崩さないようにしています。

やっていることは素朴です。私は Lab のサイト群を毎日の自動更新で回しておりますが、そこでも同じ形にしています——参照するデータが空だったら、必ず 1 行ログに残して、そこで止めます。無言で先へ進んだ結果は、あとで必ず自分に返ってくるからです。MCP も同じで、mcp-readiness.mjs が終了コード 1 で落ちたら、その日はそのサーバーを当てにしない、と決めておくだけで迷いが消えました。

渡すツールの範囲そのものを絞っておくと、確認する対象も短くなります。エージェントに渡す MCP ツールを絞る設計と併せると、朝の確認は 30 秒で終わります。

明日の朝、最初にすること

まずは手元の設定ファイルに対して、上のスクリプトを一度だけ走らせてみてください。必須ツール名を引数に渡さず、名前の一覧を眺めるところから始めれば十分です。

想定していた名前が並んでいれば、それは今日のセッションを信じてよい根拠になります。並んでいなければ、今日はその作業を持ち越す日でした——それが分かっただけで、半日は守られます。

お読みいただきありがとうございました。同じところで手を止めた方の、朝の 30 秒が軽くなれば幸いです。

シェア

お読みいただきありがとうございます

Antigravity Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥2,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

連携・プラグイン2026-06-01
Antigravity の MCP サーバーが spawn npx ENOENT で起動しないときの原因と対処
Antigravity に MCP サーバーを追加した直後、ログに spawn npx ENOENT と出てサーバーが起動しない症状の原因と直し方をまとめます。設定 JSON は正しいのに GUI からだけ失敗する PATH 非継承の問題に絞った診断手順です。
連携・プラグイン2026-08-20
MCP サーバーが1本も起動しない原因は、起動前の 40 行で特定できます
設定ファイルの1件の書き間違いで MCP サーバーが揃って起動しなくなる状況を、起動前に検査する小さなスクリプトで潰す手順です。実際に壊した設定へ通した出力と、JSON の同名キーが黙って消える落とし穴まで扱います。
連携・プラグイン2026-08-03
サーバーごとに秒数を書く前に測ったこと — MCP の接続と呼び出しは 486 倍違っていました
2.4.3 で MCP サーバーごとにタイムアウトの秒数を指定できるようになりました。何秒が妥当かを決めるために stdio サーバーを自作して境界別に実測し、単一の値では覆えない理由と、いま設定に書いている値の決め方をまとめます。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます