MCPサーバーをAIクライアントへ接続する方法の要点は、画面の「接続済み」表示ではなく、想定した機能だけを発見し、正常入力を実行でき、異常入力と権限不足を拒否できるところまで確かめることです。本稿では、ローカルstdioとリモートStreamable HTTPを分け、Node.js環境の準備からSDKでの独立検証、AIクライアント設定、受入試験、停止判断までを順に進めます。
接続前にそろえる環境と完成条件
Node.jsはCurrentではなくLTS系を使います。Node.js公式のリリース一覧では、2026年7月時点でv24がLTS、v26がCurrentです。公式ページは本番用途にActive LTSまたはMaintenance LTSを推奨しています[1]。新機能の確認だけならv26でも構いませんが、AIクライアントが内蔵するNodeやSDKの対応範囲とずれる可能性があるため、本稿の基準はNode 24です。
MCP TypeScript SDKは2026年7月27日にv2.0.0の安定版パッケージ群が公開され、クライアントは`@modelcontextprotocol/client`へ分割されました[2]。v1の`@modelcontextprotocol/sdk/client/…`を混ぜると、インポート先と世代交渉の挙動が食い違います。作業ディレクトリで`npm ls @modelcontextprotocol/client`を実行し、意図した版が一つだけ入っていることを先に確認します。
| 項目 | 指定値 | 確認方法 |
|---|---|---|
| OS | Windows、macOS、Linuxのいずれか | パス区切りと実行権限をOSに合わせる |
| Node.js | 24 LTS、検証記録はパッチ版まで残す | `node –version` |
| npm | 依存ロックを生成できる版 | `npm –version` |
| MCP SDK | `@modelcontextprotocol/client@2.0.0` | `npm ls @modelcontextprotocol/client` |
| 対象サーバー | 仕様版、Transport、起動方法が既知 | 所有者が発行した接続票 |
| 業務合格 | 期待Toolだけを列挙し、SKU A-104を照会 | JSON結果と監査IDを保存 |
Node 20以下、EOL版、未固定の`latest`依存は検証対象外にします。また、本番データへ最初から接続しません。テストアカウントには読み取りscopeだけを付け、架空SKUを10件、存在しないSKUを2件用意します。接続作業の最中に更新Toolが見えた場合は、その時点で中止し、サーバー側の公開範囲を修正します。
サーバー情報を六項目の台帳にする
「サーバー名とURLだけ」を渡されても、安全な接続はできません。最低限、識別名、Transport、起動点、仕様世代、認証、期待する機能を一組にします。stdioなら実行ファイルと引数、作業ディレクトリ、渡してよい環境変数が必要です。Streamable HTTPならMCPエンドポイント、認可サーバー、必要scope、証明書、許可Originが必要です。
| 欄 | stdio検証 | リモート検証 |
|---|---|---|
| 識別名 | `inventory-local-readonly` | `inventory-remote-readonly` |
| 接続点 | `node C:\mcp\inventory\server.mjs` | `https://mcp.example.invalid/inventory` |
| 仕様世代 | 2026-07-28を優先、2025系も許可 | 2026-07-28のみ |
| 資格情報 | `INVENTORY_FIXTURE`だけを子プロセスへ渡す | OAuth scope `inventory:read` |
| 期待機能 | `inventory_lookup` 1件 | 同じTool 1件と方針Resource 1件 |
| 所有者 | 商品基盤チーム、平日9時から18時 | API基盤当番、重大障害は24時間受付 |
`example.invalid`はIANA予約の例示用ドメインで、実在エンドポイントではありません。実際のURLを台帳へ入れるときは、チャットや記事から推測せず、サーバー所有者が署名または承認した値を使います。stdioの`command`も、同名実行ファイルをPATHから拾うより絶対パスまたは管理されたランタイムを指定した方が、別プログラム起動のリスクを下げられます。
接続票の目的は入力を増やすことではなく、障害時に「どのプロセス、どのURL、どの資格情報、どの所有者を確認するか」を一分で特定することです。
SDK v2の検証用クライアントを準備する
AIクライアントへ直接設定する前に、独立した小さな検証クライアントでサーバー側の問題と製品UIの問題を分けます。新しい空ディレクトリで次のコマンドを実行します。既存アプリの依存関係へ混ぜると、SDK v1や古い`zod`の影響を受けるため、検証用を分離します。
mkdir mcp-connection-checkcd mcp-connection-checknpm init -ynpm install --save-exact @modelcontextprotocol/client@2.0.0npm pkg set type=modulenpm ls @modelcontextprotocol/client
`package-lock.json`を保存し、検証記録へNode、npm、SDKの三つの版を転記します。社内レジストリを使う組織では、公開npmへ迂回せず、承認済みレジストリから同じハッシュのパッケージを取得します。インストール時に監査警告が出た場合は、警告の重大度と対象依存を確認し、接続成功を優先して無視しません。
ファイルは`check-stdio.mjs`と`check-http.mjs`に分けます。一つのスクリプトで自動フォールバックさせる前に、どちらのTransportが失敗したか分かる形で実行するためです。公式SDKの接続ガイドは、リモートには`StreamableHTTPClientTransport`、ローカル子プロセスには`StdioClientTransport`を使う構成を示しています[3]。
依存のインストール自体が許可されていない端末、Nodeの実行がアプリケーション制御で禁止されている端末、プロキシ証明書の配布が未完了の端末では、ここで作業を止めます。管理者権限で回避すると本番利用者の環境を再現できないため、端末管理部門へ必要条件を渡します。
stdioサーバーを子プロセスとして接続する
stdioではAIクライアントがMCPサーバーを子プロセスとして起動し、`stdin`へJSON-RPCを送り、`stdout`からJSON-RPCを受け取ります。MCP 2026-07-28のstdio仕様は、一メッセージを一行とし、ログは`stderr`へ書き、`stdout`へMCP以外を出してはならないと定めています[5]。起動直後の`console.log(“started”)`が一行あるだけでも、クライアントには壊れたJSONとして見えます。
import { Client } from "@modelcontextprotocol/client";import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";const client = new Client( { name: "inventory-connection-check", version: "1.0.0" }, { versionNegotiation: { mode: "auto" } });const transport = new StdioClientTransport({ command: "node", args: ["C:\\mcp\\inventory\\server.mjs"], cwd: "C:\\mcp\\inventory", env: { NODE_ENV: "test", INVENTORY_FIXTURE: "C:\\mcp\\inventory\\fixtures\\inventory.json" }, stderr: "pipe", maxBufferSize: 10 * 1024 * 1024});transport.stderr?.on("data", chunk => { process.stderr.write(`[server] ${chunk}`);});await client.connect(transport);console.log({ era: client.getProtocolEra(), server: client.getServerVersion(), capabilities: client.getServerCapabilities()});
`env`を指定するときは、必要な値だけを明示します。親プロセスの環境を丸ごと渡すと、クラウド資格情報や個人用トークンまで子プロセスが読める可能性があります。SDK v2の`StdioClientTransport`は安全な既定環境を作る補助関数も持ちますが、業務サーバーでは接続票に列挙した変数へ絞る方が監査しやすくなります。
実行後に`era`、サーバー名、能力が出なければ、まだAIクライアント設定へ進みません。`ENOENT`なら`command`またはスクリプトのパス、応答なしならstdout汚染と起動時例外、即時終了なら作業ディレクトリと環境変数を確認します。WindowsのパスはJSON設定ではバックスラッシュを二重にし、UIが配列入力を提供する場合は文字列へ連結しないでください。
Streamable HTTPのエンドポイントへ接続する
Model Context Protocol公式仕様「Streamable HTTP」2026-07-28版は、各JSON-RPCメッセージを独立したPOSTとし、プロトコルレベルのセッションと常設GETストリームを削除しています。サーバーは単一のMCPエンドポイントを公開し、応答をJSONまたは要求単位のSSEで返します[6]。古い記事にある`Mcp-Session-Id`保存を現行サーバーの必須要件として実装しないでください。
import { Client, StreamableHTTPClientTransport} from "@modelcontextprotocol/client";const endpoint = process.env.MCP_ENDPOINT;const token = process.env.MCP_ACCESS_TOKEN;if (!endpoint || !token) { throw new Error("MCP_ENDPOINT と MCP_ACCESS_TOKEN が必要です");}const client = new Client( { name: "inventory-http-check", version: "1.0.0" }, { versionNegotiation: { mode: "auto" } });const transport = new StreamableHTTPClientTransport( new URL(endpoint), { requestInit: { headers: { Authorization: `Bearer ${token}` } } });await client.connect(transport);console.log({ era: client.getProtocolEra(), server: client.getServerVersion()});
この例は短時間の疎通用に、既に発行されたテスト用Bearer Tokenを環境変数から渡します。本番AIクライアントでは、ブラウザー認可、PKCE、Protected Resource Metadata、`resource`パラメーター、更新トークンの保護まで含むOAuthフローを使います。固定トークンを設定ファイルやソースへ埋め込む方法は、本番手順ではありません。
HTTPサーバーをローカルで試す場合は`127.0.0.1`へだけbindし、外部サイトからのDNS rebindingを防ぐためOrigin検証を有効にします。MCP仕様は、Originが存在して不正ならHTTP 403を返し、ローカル稼働では`0.0.0.0`ではなくlocalhostへbindするよう求めています[6]。疎通のためにOrigin検証を無効化するのではなく、テストクライアントの送信元を許可リストへ追加します。
一覧取得とTool呼び出しで疎通を証明する
`connect()`が成功したら、期待するToolが一つだけ見えるかを確認します。Tool一覧が空でも、Transportとしては接続できている場合があります。反対に、管理者用Toolまで見えれば接続は成功していても権限設計は失敗です。次のコードを、stdioまたはHTTPで`client.connect()`した直後に追加します。
const listed = await client.listTools();const names = listed.tools.map(tool => tool.name).sort();if (JSON.stringify(names) !== JSON.stringify(["inventory_lookup"])) { throw new Error(`Tool一覧が想定外です: ${names.join(", ")}`);}const result = await client.callTool({ name: "inventory_lookup", arguments: { sku: "A-104", warehouse_id: "OSAKA" }});console.dir(result, { depth: null });await client.close();
架空の期待結果は、`available_units`が18、`as_of`がISO 8601日時、`source_system`が`inventory-fixture-v1`、`audit_id`が空でないことです。数量だけを見ると、古いキャッシュや別倉庫の値でも合格してしまいます。フィールド型、時刻、データ源、監査IDをまとめて検証します。Tool結果に自然文と`structuredContent`の両方がある場合は、構造化側を機械判定し、表示文は利用者向けの確認対象にします。
| 証跡 | 記録値 | 用途 |
|---|---|---|
| 実行環境 | OS、Node、npm、SDK版 | 再現条件の固定 |
| 世代 | `modern`または`legacy` | 互換分岐の特定 |
| サーバー識別 | nameとversion | 接続先違いの検知 |
| 公開機能 | Tool名を整列した一覧 | 過剰公開の発見 |
| 業務結果 | SKU、数量、as_of、audit_id | 正本との照合 |
AIクライアント側へ同じ条件を転記する
独立クライアントで合格した後、AIクライアントへ同じ起動点、引数、環境変数またはエンドポイントを設定します。製品ごとに設定ファイル名とフィールド名は異なるため、次のJSONは概念例です。実在製品へそのまま貼らず、その製品の現行公式資料でキー名と保存場所を確認してください。
{ "mcpServers": { "inventory-local-readonly": { "command": "node", "args": ["C:\\mcp\\inventory\\server.mjs"], "cwd": "C:\\mcp\\inventory", "env": { "NODE_ENV": "test", "INVENTORY_FIXTURE": "C:\\mcp\\inventory\\fixtures\\inventory.json" } } }}
設定後はAIクライアントを再起動し、Tool一覧で`inventory_lookup`だけが表示されることを確認します。「A-104の大阪在庫を確認して」と依頼し、実行前の確認UI、渡される引数、戻り値、最終回答を順に観察します。同じサーバーでも、AIクライアントがTool説明をモデルへ渡す方法や、利用者に確認を求めるタイミングが異なるため、SDK疎通結果をAI側の受入結果として流用してはいけません。
AIクライアントへ資格情報を入力する場合は、組織管理の秘密保管機能を使います。設定のエクスポート、画面共有、診断ログにトークンが含まれないかも確認します。利用者ごとに権限が異なるリモートサーバーを、部署共通の固定トークンで接続する方法は避けます。誰の権限で実行したかを監査できなくなるからです。
更新Toolを将来追加する場合は、読み取り接続へ上書きせず、別名の接続と別scopeで段階投入します。読み取り用の合格記録を残したまま、更新用は確認UI、二重送信、取消不能処理、権限剥奪を追加試験します。
2026系と2025系の世代差を切り分ける
MCP 2026-07-28はステートレスな「modern」世代で、各要求に仕様版とクライアント能力を含めます。2025系の「legacy」世代は接続時に`initialize`を行い、接続単位の状態を持ちます。SDK v2の仕様版ガイドは、`versionNegotiation: { mode: “auto” }`で現行方式を判定し、2025専用サーバーなら旧方式へ移る例を示しています[4]。
| 観点 | modern 2026-07-28 | legacy 2025系 |
|---|---|---|
| 開始 | `server/discover`で事前判定可能 | `initialize`ハンドシェイク |
| 要求メタデータ | 毎要求の`_meta`に含む | 接続開始時に交換 |
| HTTPセッション | プロトコル上は存在しない | 実装によりSession IDを利用 |
| 変更通知 | `subscriptions/listen`の応答ストリーム | 接続上の通知 |
| 確認値 | `getProtocolEra()`が`modern` | 同メソッドが`legacy` |
自動交渉は互換性を上げますが、結果を見えなくしてよいわけではありません。接続ログへ`getProtocolEra()`を出し、想定がmodernなのにlegacyへ落ちた場合は警告にします。サーバー更新後もlegacyのままなら、ロードバランサーの向き先、古いプロセス、キャッシュされた設定を疑います。
旧HTTP+SSEしか扱えないサーバーへのフォールバックは別問題です。Streamable HTTPを試した後、必要なら新しい`Client`で`SSEClientTransport`へ切り替えます。新規本番で旧Transportを選ぶのではなく、廃止日を決めた移行橋として扱います。旧サーバーの所有者と更新計画がない場合は、接続を増やさず代替製品の評価へ移ります。
エラーを発生地点とHTTP状態で読む
エラー文を検索する前に、プロセス起動、Transport、仕様世代、認証、能力、業務処理のどこで止まったかを分けます。stdioの`ENOENT`とHTTPの401は、どちらも「つながらない」と見えますが、修正担当も確認証跡も異なります。以下の表では、同じメッセージへ何度も設定を変えるのではなく、最初に見る一点を定めています。
| 症状 | 主な原因 | 最初の確認 | 続行条件 |
|---|---|---|---|
| `spawn ENOENT` | commandまたはPATHが不正 | 同じ利用者で実行ファイルを起動 | 絶対パスで版を表示できる |
| `Unexpected token` | stdoutへログを出力 | 先頭行がJSON-RPCか確認 | ログをstderrへ移動 |
| 接続後すぐ終了 | 環境変数、cwd、権限不足 | stderrと終了コード | 終了コード0で待機 |
| HTTP 400 | 仕様版やヘッダー不一致 | JSON-RPCエラー本文 | 対応版を明示して再試験 |
| HTTP 401 | トークンなし、期限切れ、audience不一致 | `WWW-Authenticate` | 新規トークンで成功 |
| HTTP 403 | scope不足またはOrigin拒否 | 要求scopeとOrigin | 許可変更の承認記録 |
| HTTP 404 | URL誤りまたはmethod非対応 | JSON-RPC code `-32601`の有無 | 正しいMCP endpointを特定 |
| Tool一覧が空 | 能力未表明または権限で非表示 | server capabilitiesとscope | 期待Toolだけを取得 |
| 呼び出しがタイムアウト | 上流API、SSEバッファ、処理停止 | 相関IDと層別時間 | 上限内で完了し取消も成功 |
Model Context Protocol公式仕様「Streamable HTTP」2026-07-28版は、`MCP-Protocol-Version`と本文`_meta`の版が不一致なら400、未実装methodなら404とJSON-RPC `-32601`を返すと定めています[6]。404だけを見てURL誤りと決めると、正しいendpointでmethodが古いケースを見落とします。状態コードとJSON-RPC本文を対で保存します。
自力対応を止める条件:本番トークンがログへ出た、意図しない更新が実行された、証明書検証を無効にしないと接続できない、別利用者の結果が返った場合です。資格情報を失効させ、サーバー所有者とセキュリティ担当へ時刻・相関ID・影響範囲を渡します。
正常・負例・復旧の12件を受入試験する
接続確認は正常例だけでは不足します。NIST SP 800-115は、情報セキュリティ試験を計画、実施、結果分析、対策へつなげる技術ガイドです[7]。MCP専用規格ではありませんが、負例を含む試験計画と証跡の考え方を採用できます。本稿では正常4件、入力異常3件、認可2件、復旧3件の計12件を固定します。
| 番号 | 入力・操作 | 期待結果 |
|---|---|---|
| 1 | サーバー発見 | 名前、版、世代が接続票と一致 |
| 2 | Tool一覧 | `inventory_lookup`だけを返す |
| 3 | SKU A-104、大阪 | 数量18と監査IDを返す |
| 4 | SKU B-205、倉庫省略 | 倉庫別の構造化結果を返す |
| 5 | 空のSKU | 入力エラーで上流APIを呼ばない |
| 6 | 形式外`../../secret` | JSON Schemaで拒否する |
| 7 | 未知の引数`include_cost` | 追加プロパティを受理しない |
| 8 | トークンなし | HTTP 401と認可情報を返す |
| 9 | 更新scopeだけのトークン | 読み取り対象外として拒否する |
| 10 | サーバープロセス強制終了 | stdio再起動後に再接続できる |
| 11 | 上流APIを3秒遅延 | 定めた上限で中断し理由を返す |
| 12 | AIクライアント再起動 | 過去の資格情報混線なく同じ一覧を得る |
合格は12件すべてです。ただし、応答時間の数値は業務要件から別に定めます。想定例では正常照会のp95を2秒以下、起動から一覧取得までを5秒以下、障害検知から層別判定までを10分以下とします。これらはMCP仕様の保証値ではありません。20回以上の実測を取り、端末、ネットワーク、日時、キャッシュ状態を添えて自社値へ置き換えます。
試験票には「成功」だけでなく実際のHTTP状態、JSON-RPC code、Tool結果ハッシュ、サーバーログの相関IDを残します。再試験で値が変わった場合、修正した設定、SDK、サーバー版のどれが原因か追跡できるようにします。
本番移行と切り戻しの基準を決める
受入合格後も、最初は読み取り専用アカウントと少人数のAIクライアントへ限定します。架空例では利用者5人、2週間、1日50呼び出しまでとし、Tool選択誤り、401・403比率、p95応答時間、手動差し戻しを観測します。総呼び出し数ではなく、失敗した入力と発生層を見ます。
本番拡大は、重大誤実行ゼロ、権限外結果ゼロ、正常成功率99%以上、p95が自社上限内、障害訓練で10分以内に所有者へ到達、の全条件を満たした場合だけです。成功率は`正常完了件数 ÷ 有効要求件数 × 100`で計算し、入力不備を分母から勝手に除外しません。利用者起因とシステム起因を別列で示します。
切り戻し手順
- AIクライアントで対象MCP接続を無効化し、新規Tool呼び出しを止める。
- 実行中のaudit_idを抽出し、完了か取消かを元APIで確認する。
- リモートのアクセストークンとクライアント登録を失効させる。
- stdioの子プロセスが残っていないことをOS側で確かめる。
- 直前の安定設定へ戻し、受入ケース1、2、3、8を再実行する。
更新後にmodernからlegacyへ意図せず変わる、Tool一覧が一件でも増える、401・403が5%を超えて30分続く、p95が基準の2倍を二測定区間連続で超える、資格情報が診断ログへ出る、のいずれかで自動拡大を止めます。修正期限を一週間として、解消できなければSDKまたは接続方式の乗り換えを検討します。
MCP接続でよくある質問
接続済み表示だけでMCPの設定完了と判断できますか?
判断できません。発見結果、期待するTool一覧、正常呼び出し、入力拒否、権限拒否、再起動後の再接続まで通して初めて業務上の接続完了です。
stdioのサーバーURLはどこに設定しますか?
stdioにはURLがありません。AIクライアントが`command`と`args`で子プロセスを起動し、標準入力と標準出力でJSON-RPCメッセージを交換します。
2026-07-28版と2025版のサーバーを同じクライアントで扱えますか?
SDK v2の`versionNegotiation`を`auto`にすると、現行方式を判定し、必要な場合は2025系の`initialize`方式へフォールバックできます。`getProtocolEra()`の記録で実際に選ばれた世代を確認します。
本番接続へ進めない方がよい状態は何ですか?
資格情報を設定ファイルへ直書きしている、更新Toolを利用者が拒否できない、stderrとstdoutを分離できない、401と403の原因を識別できない、切り戻しが未試験なら本番化を止めます。
MCP接続後に取る次の行動と成果物
次に取る行動は、六項目の接続票、版を固定した`package-lock.json`、12件の受入試験結果、資格情報を含まないエラーログ、AIクライアントの確認画面、切り戻し所要時間を一つの案件記録へまとめることです。未解決の警告があれば「接続済み」ではなく「限定検証」と記載し、所有者と期限を付けます。この記録があれば、次の担当者は設定を推測せず、同じ条件から再現できます。
記録の末尾には、接続を無効化する操作も実測で残します。stdioなら子プロセスの標準入力を閉じた時刻と終了時刻、Streamable HTTPならクライアント登録またはトークンを失効させた後に401へ変わることを確認します。12件という母数と三分という停止線は、接続確認の試算例であり公式仕様の合格値ではありません。停止操作が基準内に完了しない、更新Toolを拒否しても再試行される、秘密値が標準出力やHTTPログへ残る、といういずれかを検出した場合は利用者への配布を見送り、原因を除去してから受入試験を最初から再実行します。
参考文献・出典
- Node.js「Node.js Releases」(v24 LTS、ページ上の最終更新値2026-06-23、参照日2026-07-30)
- Model Context Protocol「@modelcontextprotocol/client 2.0.0 release」(2026-07-27公開、参照日2026-07-30)
- MCP TypeScript SDK「Connect to a server」(SDK v2、参照日2026-07-30)
- MCP TypeScript SDK「Protocol versions」(SDK v2、MCP 2026-07-28対応、参照日2026-07-30)
- Model Context Protocol「stdio」(Protocol Revision 2026-07-28、参照日2026-07-30)
- Model Context Protocol「Streamable HTTP」(Protocol Revision 2026-07-28、参照日2026-07-30)
- NIST「SP 800-115: Technical Guide to Information Security Testing and Assessment」(Final、2008-09、ページ更新2023-05-20、参照日2026-07-30)