◉CLAUDE LABEN
●2.1.301 — vim の % モーション、VS Code パネルでの LaTeX 数式表示、使用量上限待ちのリトライ監視の上限が入りました(10/15)●2.1.299 — 200K トークン超で約54分放置した会話はアイドル圧縮。MCP の許可ルールは名指ししたサーバーだけに一致します●SONNET 4.5 — Claude API での提供終了は 11/30、残り45日。移行先は Sonnet 5.5●WINDOWS — 日本語の環境でだけフックが黙って壊れる報告があり、文字コードの確認が呼びかけられています●FAST MODE — Opus 5.5 の Fast mode がリサーチプレビューを終え、ベータヘッダーが不要になりました(10/15)●NEW — Cowork に渡したフォルダの先頭に、十行の約束ファイルを置いてみました●2.1.301 — vim の % モーション、VS Code パネルでの LaTeX 数式表示、使用量上限待ちのリトライ監視の上限が入りました(10/15)●2.1.299 — 200K トークン超で約54分放置した会話はアイドル圧縮。MCP の許可ルールは名指ししたサーバーだけに一致します●SONNET 4.5 — Claude API での提供終了は 11/30、残り45日。移行先は Sonnet 5.5●WINDOWS — 日本語の環境でだけフックが黙って壊れる報告があり、文字コードの確認が呼びかけられています●FAST MODE — Opus 5.5 の Fast mode がリサーチプレビューを終え、ベータヘッダーが不要になりました(10/15)●NEW — Cowork に渡したフォルダの先頭に、十行の約束ファイルを置いてみました
記事一覧/Claude Code
⟐ Claude Code/2026-09-01上級

MCPサーバが十数個を超えてから、起動直後の一覧を信用しなくなりました

起動直後に「接続できません」と並ぶサーバのうち、こちらから手を打てるものは一部だけです。応答なし・起動失敗・プロトコル拒否を分けて返すプローブを実装し、14台構成で逐次22.03秒が並列5.09秒まで縮むところ、待ち時間を詰めると健全なサーバが失敗に化けるところを実測で残しました。

MCP54Claude Code265起動時診断並行処理3運用設計28

✦ プレミアム記事

朝いちばん、いつもの作業を始めようとして手が止まりました。前日まで使っていたサーバが、一覧のどこにもありません。

代わりに並んでいたのは「接続に失敗しました」という行が七つでした。設定は触っていません。ネットワークも生きています。首をかしげながら別の調べ物を五分ほどして、もう一度一覧を開いたら、七つのうち四つが何事もなかったように戻っていました。

壊れていたのではなく、まだ握手が終わっていなかっただけでした。

この日から、起動直後の一覧をそのまま信じるのをやめました。信じる代わりに、自分で握手して結果を分類するようにしました。今日はその実装と、十四台まで増やして測った数字を残しておきます。

「使えません」の中に、性質の違うものが混ざっていました

一覧に出る文言は一種類でも、その裏側で起きていることは同じではありません。手元で拾ったものを並べると、少なくとも四つに分かれていました。

結末裏で起きていることこちらの取れる手
応答なしプロセスは生きているが initialize への返事が期限内に来ない待つ・並列度を上げる。設定をいじっても直らない
起動失敗プロセスがすぐ落ちる。標準エラーに理由が出ているその場で直せる。資格情報・パス・実行権限
プロトコル拒否握手は成立したが、サーバがこちらの申告を受け付けないその場で直せる。バージョン整合・クライアント名
正常initialize が結果を返した何もしない

肝心なのは、この四つに割り当てるべき行動が全部違うという点です。応答なしのサーバに資格情報を入れ直しても徒労ですし、起動失敗しているサーバを待っても永遠に戻ってきません。一覧が「接続に失敗しました」の一語でまとめてしまうと、この使い分けができないまま総当たりで時間が溶けます。

もうひとつ、ここに含まれない層があります。握手も tools/list も成功しているのに、ツールが一件も生えていない状態です。あれは接続の層ではなく権限の層の話なので、MCPサーバーは繋がっているのに、ツールが0件で返る の方で扱いました。今日は握手が終わる手前だけを見ます。

分類して返すプローブを書く

やることは単純です。サーバを起動し、initialize を一通だけ投げ、期限つきで一行読みます。返ってきたものと、返ってこなかった理由で場合分けします。それだけです。

一点だけ、素直に書くと必ず踏む落とし穴があります。readline() には期限を渡せません。標準入出力の読み取りは、相手が黙り込んだらそのまま返ってこないので、timeout を書ける場所がないのです。ここは読み取りを別スレッドへ追い出し、スレッドの join(timeout) を期限として使います。

# probe.py — MCPサーバの起動時状態を四つに分類する
import json, os, subprocess, sys, threading, time
 
READY, TIMEOUT, EXITED, PROTOCOL_ERROR = "READY", "TIMEOUT", "EXITED", "PROTOCOL_ERROR"
 
def probe(name, command, env_overrides, timeout):
    """1台のMCPサーバへ initialize を投げ、(名前, 結末, 経過秒, 詳細) を返す。"""
    env = dict(os.environ)
    env.update(env_overrides)
    t0 = time.monotonic()
 
    try:
        proc = subprocess.Popen(
            command,
            stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
            text=True, env=env,
        )
    except OSError as ex:
        # 実行ファイルが無い・権限が無い。ここは握手以前の失敗
        return (name, EXITED, round(time.monotonic() - t0, 3), str(ex))
 
    request = {
        "jsonrpc": "2.0", "id": 1, "method": "initialize",
        "params": {
            "protocolVersion": "2025-06-18",
            "capabilities": {},
            "clientInfo": {"name": "startup-probe", "version": "1.0"},
        },
    }
 
    box = {}
    def read_one_line():
        box["line"] = proc.stdout.readline()   # 期限を渡せないのでスレッドへ隔離する
 
    reader = threading.Thread(target=read_one_line, daemon=True)
    try:
        proc.stdin.write(json.dumps(request) + "\n")
        proc.stdin.flush()
    except (BrokenPipeError, OSError):
        pass    # 既に落ちている。下の EXITED 判定で理由を拾う
 
    reader.start()
    reader.join(timeout)
    elapsed = round(time.monotonic() - t0, 3)
 
    if reader.is_alive():
        proc.kill()
        return (name, TIMEOUT, elapsed, f"initialize への応答が {timeout}s 以内に無し")
 
    line = box.get("line") or ""
    if not line:
        proc.kill()
        stderr_tail = (proc.stderr.read() or "").strip().splitlines()
        detail = stderr_tail[-1] if stderr_tail else "応答せずに標準出力を閉じた"
        return (name, EXITED, elapsed, detail)
 
    message = json.loads(line)
    proc.kill()
    if "error" in message:
        return (name, PROTOCOL_ERROR, elapsed, message["error"]["message"])
    return (name, READY, elapsed, message["result"]["serverInfo"]["name"])

この形は一点だけ足りません。後述の「診断ログの多いサーバ」の節で stderr の扱いを補うので、実際に回す前にそちらまで目を通していただければと思います。

proc.kill() を各分岐に置いているのは、握手だけ確かめて用済みにするからです。プローブが起動したプロセスを残したまま本体がもう一度起動すると、同じサーバが二重に立ち上がります。片付けを忘れると、調べるために作った仕組みが調べたい対象を汚します。

✦

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

この記事の続きを読む

この先には、実装コードやベンチマーク結果など、実務でお役に立てる内容をご用意しています。このサイトは広告を掲載しておらず、サーバーや開発にかかる費用はメンバーの皆様のご支援で成り立っています。もしお役に立てていましたら、ご支援いただけますと大変ありがたいです。

この記事で得られること
✦動かないサーバを応答なし・起動失敗・プロトコル拒否の三つに切り分けて、自分の手で直せるものだけに時間を使えるようになります
✦健全なのに応答が遅いだけのサーバを失敗として切り捨てる設定を、本番の起動処理へ入れてしまう前に避けられるようになります
✦14台構成で逐次22.03秒が並列5.09秒へ縮んだ実測をもとに、自分の台数でプローブの並列度と待ち時間を決められるようになります
Stripe による安全な決済 · いつでもキャンセル可能
✦

この記事を購入する

この先の内容をすべてお読みいただけます。一度のご購入で、いつでも何度でもアクセスできます。このサイトは広告を掲載しておらず、皆さまのご支援がサーバー費用などの運営を支えています。

または
メンバーシップなら全記事が読み放題 →
シェア

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

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

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

関連記事

⟐ Claude Code2026-07-29
MCPサーバーは繋がっているのに、ツールが0件で返る — 無人実行の権限劣化をツール表の差分で止める
資格情報が抜けてもMCPのハンドシェイクは成功し、tools/list は空配列を返します。例外が飛ばないまま実行が完走する構造と、期待ツール表との差分で無人実行を止めるpreflightの実装を、実測値つきで記録しました。
⟐ Claude Code2026-09-04
~/.claude.json の巻き戻しは v2.1.259 で直りました。消えた設定は自分で戻します
同時に走らせたセッションが ~/.claude.json を互いに巻き戻す不具合が v2.1.259 で修正されました。消えた信頼設定と MCP を洗い出して戻すまでを、キー集合の差分を取る小さなスクリプトとあわせて書き残します。
⟐ Claude Code2026-08-19
一度キャッシュに載った500ページは、直しても消えません
修正をデプロイしたのに壊れたページが出続けたことがあります。原因はコードではなくエッジキャッシュでした。ステータスコードだけを見る保存判定が、200で返る壊れたHTMLを通していたのです。本文の完全性で保存可否を決めるガードの実装と、運用で調整した誤検知の線引きを、実際に踏んだ経緯とともに書きました。