MCPサーバーに接続できないときは、設定を一度に直さず、①プロセスが起動するか、②ホストと同じPATH・作業ディレクトリ・環境変数で動くか、③stdioまたはHTTPで通信できるか、④クライアントとサーバーのプロトコルの仕様リビジョンが合うか、の順に切り分けます。最初にログを保存し、同じコマンドを単体実行してからMCP Inspectorへ進むと、原因の層を狭められます。
この記事はローカルのstdioサーバーとStreamable HTTPサーバーの接続復旧を、症状から5層へ振り分ける診断記事です。Inspectorの正常・異常ケース、機能種別の単体テスト、ホスト差分検査の詳しい手順はMCP Inspectorでサーバーをデバッグする方法へ譲ります。ツール固有の処理失敗、OAuthの実装、個別ホストごとの完全な設定例も扱いません。
情報確認日:2026年8月17日(日本時間)
2026年版の注意:MCP 2026-07-28仕様では、従来のinitialize / initialized ハンドシェイクとMcp-Session-Idが廃止されました。一方、旧リビジョンと旧仕様のSDK・ホストも残っています。ログにinitializeがないことだけで障害と決めず、クライアントとサーバーがどの仕様リビジョン・互換モードを使うかを先に確認してください。
結論:接続エラーを5つの層へ分ける
上から順に確認
- 設定:JSON、コマンド、引数、URLがホストに読み込まれているか
- プロセス:実行ファイル、実行環境、権限、作業ディレクトリ、環境変数がそろうか
- トランスポート:stdioを汚していないか、HTTPエンドポイントへ到達できるか
- プロトコル:仕様リビジョンと機能宣言の扱いがクライアント・サーバーで一致するか
- 機能:接続後に
tools/listと代表ツールを呼べるか
「サーバーが見えない」と「サーバーは見えるがツールが失敗する」は別の障害です。まず接続完了を、サーバープロセスが生存し、プロトコル上の最初の交換が終わり、tools/listなどの一覧取得に成功する状態と定義します。
症状から最初に見る場所を決める
| 症状 | 最初に疑う層 | 最初の確認 |
|---|---|---|
| サーバーが一覧に出ない | 設定 | 設定ファイルの場所、JSON構文、ホスト再起動 |
| 起動直後に切断される | プロセス | コマンド単体実行、終了コード、標準エラー出力 |
command not found |
PATH | 絶対パス、ホストが継承する環境変数 |
| 端末では動くがホストでは失敗 | 作業ディレクトリ・環境変数 | 相対パス、必要なキー、実行環境の実体 |
| JSON解析エラー | stdio | 標準出力の起動メッセージ・デバッグログ |
| 404 / 405 | HTTPエンドポイント | URLパス、メソッド、プロキシルート |
| 400 / プロトコルエラー | ヘッダー・仕様リビジョン | Accept、MCP-Protocol-Version、リクエスト本文 |
| バージョン交渉失敗 | プロトコル | 2026型・旧仕様型・フォールバック設定 |
| 接続済みだがツールがない | 機能宣言・登録 | 一覧結果、サーバー側のツール登録時点 |
この表は原因を断定するものではなく、最初の観測点を決める診断表です。上の層が失敗している間は、下のツール実装を直しても接続は戻りません。
手順1:変更する前に失敗条件を保存する
最初に「いつ、どのホストから、どのサーバーを、どのトランスポートで起動し、何が表示されたか」を残します。設定を何度も変えると、直った理由も壊れた理由も追えなくなるためです。
connection incident
- observed_at: 2026-08-17T10:30:00+09:00
- host: product name and version
- server package/version: ...
- transport: stdio | streamable-http
- command or endpoint: secretを除いた値
- expected: tools/list succeeds
- actual: process exits after 1.2s
- exit code / HTTP status: ...
- stderr or server log: secretを除いた抜粋
- last known working version: ...
APIキー、Cookie、Authorizationヘッダー、個人情報はログから除きます。共有するときはパスに含まれる利用者名やプロジェクト名も必要に応じて伏せてください。
手順2:stdioサーバーが単体で起動するか確認する
ホストと同じコマンドをターミナルで実行する
stdioではホストがサーバーを子プロセスとして起動します。まずホスト設定のcommandとargsを、同じ順序でターミナルから実行します。
command -v node
node --version
test -f /absolute/path/to/server/dist/index.js
node /absolute/path/to/server/dist/index.js
サーバーが入力待ちになれば、少なくとも実行環境と起動ファイルは解決しています。すぐ終了する場合は標準エラー出力と終了コードを確認します。TypeScriptソースコードを直接指定しているのに実行環境がtsxを持たない、ビルド後のdistを指定していない、実行権限がない、といった問題を先に直します。
コマンドとファイルは絶対パスでそろえる
GUIから起動したホストは、ログインシェルと同じPATH・作業ディレクトリを持つとは限りません。公式デバッグガイドも、ホストから起動するstdioサーバーでは絶対パスを使うよう案内しています。
{
"mcpServers": {
"reports": {
"command": "/absolute/path/to/node",
"args": ["/absolute/path/to/reports/dist/index.js"],
"env": {
"REPORTS_API_BASE": "https://api.example.test"
}
}
}
}
上はフィールドの役割を示す例です。設定ファイル名とスキーマはホストの現行公式資料に合わせてください。認証情報を設定ファイルへ直接置く必要がある場合は、ファイル権限とホストの機密情報管理機能を確認します。
作業ディレクトリ依存を外す
./config.jsonや./dataのような相対パスは、ホストの作業ディレクトリが変わると別の場所を指します。サーバー自身のモジュールURLから基準ディレクトリを決めるか、設定で絶対パスを渡します。
import { fileURLToPath } from "node:url";
import path from "node:path";
const moduleDir = path.dirname(fileURLToPath(import.meta.url));
const configPath = path.resolve(moduleDir, "../config/default.json");
作業ディレクトリを固定するだけでも直せますが、ホストごとに起動場所を合わせ続ける負担が残ります。配布サーバーでは、起動ファイルを基準にするか、必要パスを明示設定にするほうが再現しやすくなります。
手順3:標準出力をプロトコル専用にする
stdioトランスポートでは、stdinと標準出力がMCPメッセージの通り道です。公式仕様では、サーバーが標準出力へ有効なMCPメッセージ以外を書いてはいけません。起動メッセージ、console.log、進捗表示が1行入るだけでも、クライアント側ではJSON解析エラーや予期しない切断に見えます。
| 出力先 | 置くもの | 置かないもの |
|---|---|---|
| 標準出力 | MCPのJSON-RPCメッセージ | 起動文、デバッグ、進捗、スタックトレース |
| 標準エラー出力 | 診断ログ、起動失敗、警告 | 機密情報、個人情報、巨大なレスポンス全文 |
// stdio serverの診断はstderrへ
console.error(JSON.stringify({
level: "info",
event: "server_starting",
runtime: process.version
}));
ライブラリが自動で標準出力へログを出す場合は、そのロガー設定も確認します。ログを消すのではなく標準エラー出力へ移し、発生時刻、イベント名、リクエストID、所要時間を残すと再発時に追いやすくなります。
手順4:Inspectorでサーバー側かホスト側かだけを分ける
サーバー単体が起動したら、Inspectorを一度使い、同じコマンドへホストの外から接続できるかだけを確認します。
npx @modelcontextprotocol/inspector \
node /absolute/path/to/server/dist/index.js
Inspectorでサーバーへ接続できるのにホストでは失敗するなら、サーバー本体よりホスト設定、ホストが渡す環境変数、対応仕様リビジョン、再起動方法へ原因を絞れます。Inspectorでも失敗するならサーバー・トランスポート側を直します。
ここでは接続可否の二分だけに使います。tools/list、代表ツール、不正入力、リソース・プロンプト、終了処理まで含む正常/異常ケース表は、重複を避けてMCP Inspectorの単体検査記事で確認してください。Inspectorはホスト固有の権限・承認UIを再現しないため、合格してもホスト統合の完成証明にはなりません。
手順5:プロトコルの仕様リビジョンの不一致を確認する
2026-07-28型と旧仕様型を分ける
2026-07-28仕様では、リクエストごとにプロトコルバージョン、クライアント情報、機能宣言が渡され、必要ならserver/discoverでサーバーの対応を調べます。2025年系では、クライアントがinitializeを送り、バージョンと機能宣言を交換してからinitializedを送る方式です。
| 確認点 | 2026-07-28型 | 2025年系 / 旧仕様型 |
|---|---|---|
| 接続前の確認 | server/discoverは任意 |
initializeが最初 |
| プロトコル情報 | リクエストごとに送る | ハンドシェイクで交換 |
| セッション | プロトコルレベルのセッションなし | HTTPではセッションIDを使う場合あり |
| 互換確認 | 新仕様を明示または自動確認 | 旧仕様へのフォールバックの有無 |
公式TypeScript SDK v2でも、旧仕様のハンドシェイクを既定にする構成と、server/discoverで新仕様を確認して旧仕様へフォールバックする構成があります。「SDKをv2へ上げたから自動で新仕様になった」と決めつけず、バージョン交渉の設定と実際の通信ログを確認します。
機能宣言は宣言された範囲だけ使う
接続自体は成功しても、相手が対応していない機能を呼ぶとプロトコルエラーになります。旧仕様ではハンドシェイクの機能宣言、2026-07-28型ではリクエストメタデータや拡張機能の交渉を確認します。ツール登録漏れと機能宣言不一致を分けるため、まず一覧結果を保存してください。
Streamable HTTPで接続できない場合は何を見る?
まずHTTPとして到達できるか確認する
Streamable HTTPは独立プロセスとして動きます。DNS、TLS、リバースプロキシ、ルート、認証をMCPより先に確認します。404はパス、405はメソッド、401/403は認証・権限、5xxはサーバーまたはプロキシのログへ戻る目印になります。
curl -i https://mcp.example.test/mcp
# credentialをshell historyや共有logへ残さない
# 実際のPOST body・headerは対応revisionの公式仕様に合わせる
GETを提供しないサーバーは405を返す場合があるため、GETの結果だけでMCPエンドポイントが壊れているとは判断できません。サーバーの対応仕様リビジョンに合うPOSTをInspectorまたは公式SDKから送り、HTTPステータスとレスポンスのContent-Typeを確認します。
プロキシでヘッダーとストリームを失わない
リバースプロキシがMCP-Protocol-Version、Accept、認証ヘッダーを落としていないかを確認します。旧仕様のセッション型ならMcp-Session-Idも対象です。SSEを使う構成ではバッファリングやタイムアウトがストリームを途中で閉じていないかも調べます。
ローカルHTTPサーバーは原則として127.0.0.1へバインドし、外部公開が必要な場合はOriginの検証と認証を実装します。接続確認のために0.0.0.0へ広げると、別のセキュリティ問題を作ります。
症状別の診断フロー
MCP serverが見えない
├─ Hostが設定を読み込んだか
│ ├─ No → JSON・file場所・Host完全再起動
│ └─ Yes
├─ processは起動するか
│ ├─ No → command・absolute path・runtime・permission
│ └─ Yes
├─ Inspectorで接続できるか
│ ├─ No → stdout汚染・transport・protocol revision
│ └─ Yes
├─ Hostだけ失敗するか
│ ├─ Yes → HostのPATH・cwd・env・対応revision・権限
│ └─ No
└─ tools/list後だけ失敗するか
├─ Yes → capability・Tool登録・input schema
└─ No → 接続復旧。代表Toolと終了処理を確認
1回の修正では1つの層だけを変え、同じ再現手順で結果を比較します。複数の設定を同時に変えると、復旧しても原因を特定できません。
診断コマンド表
| 目的 | コマンド例 | 分かること |
|---|---|---|
| 実行環境解決 | command -v node |
ホスト設定へ渡す実体パス |
| 実行環境版 | node --version |
パッケージの対応バージョン条件との一致 |
| 起動ファイル | test -f /absolute/path/to/index.js |
ビルド先・パスの存在 |
| 実行権限 | test -x /absolute/path/to/command |
CLIを直接起動できるか |
| HTTP到達 | curl -i https://host.example/mcp |
DNS・TLS・ルート・ステータス |
| stdio検査 | npx @modelcontextprotocol/inspector <command> |
ホスト外でトランスポート・一覧・呼び出しを確認 |
ログ共有前の確認:env全体、Authorizationヘッダー、Cookie、リクエスト本文全文をそのまま貼らないでください。必要な変数は「設定されている/いない」と値の長さだけで確認できる場合があります。
接続が戻ったかをどう確認する?
UIに緑色の印が出ただけではなく、次の一連の動作を同じバージョンで確認します。
- ホストを完全終了して再起動する
- サーバープロセスが1つだけ起動する
- バージョン交渉または対応仕様リビジョンの最初のリクエストが成功する
tools/listの結果がサーバー実装と一致する- 読み取り専用の代表ツールを最小入力で呼ぶ
- 不正入力が安全なエラーになり、サーバーが落ちない
- ホスト終了後に子プロセス・ポートが残らない
ここまで通れば接続層は復旧です。特定ツールだけが失敗する場合は、接続設定ではなく入力スキーマ、後段のAPI、権限、タイムアウトの診断へ切り替えます。
よくある質問
ターミナルでは動くのにホストから接続できないのはなぜ?
ホストのPATH、作業ディレクトリ、環境変数、実行環境がターミナルと違う可能性があります。コマンドと起動ファイルを絶対パスにし、必要な環境変数をホストの正規設定で渡して比較してください。
console.logは使ってはいけませんか?
stdioサーバーでは標準出力がプロトコル専用なので、診断ログは標準エラー出力へ出します。Streamable HTTPではサーバー側のログ基盤やOpenTelemetryを使い、機密情報を除外します。
initializeが来ないのは接続エラーですか?
2026-07-28仕様ではinitializeハンドシェイクが廃止されています。旧リビジョンでは必要です。クライアントとサーバーが使う仕様リビジョンと互換モードを確認してから判断します。
Inspectorで動けば本番ホストでも動きますか?
サーバー・トランスポート側の有力な合格材料ですが、ホスト固有の環境変数、権限、対応仕様リビジョン、承認UIは別に確認が必要です。
HTTP接続確認のため0.0.0.0へバインドしてよいですか?
ローカル用途では避け、127.0.0.1を使います。外部公開が必要なら、Originの検証、認証、TLS、レート制限、ログを設計してから公開します。
まとめ
MCPサーバーへ接続できないときは、設定、プロセス、トランスポート、プロトコル、ツール機能の順に層を分けます。stdioでは絶対パス・作業ディレクトリ・環境変数と標準出力汚染を確認し、MCP Inspectorでホストの外から再現します。Streamable HTTPでは、MCPより先にDNS、TLS、ルート、メソッド、ヘッダー、認証を確認します。
2026-07-28仕様と旧仕様では接続開始の形が異なります。サーバーとクライアントの対応バージョンを記録し、一度に1項目だけ変え、tools/listと代表ツールまで通して復旧を確認してください。