第3章 — サーバープリミティブ: コンテキストと能力の公開
LLM Primer IV: MCPで設計するAI認知 のウォークスルー第3回です。今回は、MCPサーバーが実際に「何を言えるか」 — 3つの名詞、3つのライフサイクル、3つのエラーモデル — を見て、正しい名詞を選ぶ規律がサーバーをスケールさせるか、淀ませるかを決める話をします。
なぜこの章があるのか
プロトコルは、それで何を言えるか以上に有用にはなりません。第2章ではホスト・クライアント・サーバーのメンタルモデルを組み立て、能力交渉でセッションが立ち上がる様子を見ました。ハンドシェイクが終わって両者が相手の能力を知ったとき、具体的にテーブルに乗っているものは何でしょうか。MCPは3つの名詞で答えます: Resources、Prompts、Tools。見かけは似ています — どれも「名前があり、スキーマで記述された、サーバーが出力したり実行したりできるもの」 — が、3つの異なる意図に対応します。Resources は読む状態。Prompts は再利用可能な足場。Tools は書き込みの動作。この章ではそれぞれを順に歩きます: スキーマ、ライフサイクル、エラーモデル、そしてエンジニアが間違える場所。
3.1 Resources: 読み取り専用のコンテキスト・データ
Resource はサーバーがコンテキストとしてモデルへ渡せるデータです。定義語は「読み取り専用」。Resource は世界を変えません。世界を記述します。フェッチが状態変化を引き起こすなら — 監査ログの行、インクリメントされるカウンタ、発火するWebhook — それはResourceではなく、衣装を着たToolです。この線をはっきり引くのがMCPサーバー設計の最初の規律です。
各Resourceは、サーバーが選んだスキーム(file://、postgres://、linear://issue/ENG-1234)で安定したURIを持ち、メタデータが付きます: 名前、任意の説明、MIMEタイプ、任意のサイズ。これらは飾りではありません。説明のないResourceは、モデルが評価できないResourceです。ライフサイクルは素直です: resources/list で列挙、resources/read で取得。resources/write はありません — サーバーが書き込みを望むならToolを公開する必要があります。この非対称が信頼境界を可視に保ちます。
規模で実用にするには2つのパターンが効きます。URIテンプレートはパラメータ化された読み取りエンドポイント(db://orders/{order_id})を公開し、何百万行も列挙せずに済ませます。サブスクリプションは、ホストがURIへの関心を登録し、対象データが変わったら notifications/resources/updated を受け取る仕組み — 代替はLLMの間隔でのポーリングで、両方向で無駄になります。避けたい落とし穴は、セッション開始時に内部オブジェクトをすべてResourceリストに投げ込むこと: 3000個のリストはユーザーが文字を打つ前に数万トークンを食べます。整理されたトップレベルとテンプレートを公開して、ロングテールはモデルに探させます。
3.2 Prompts: 再利用可能なテンプレートとワークフロー
MCPのPromptは、LLMのシステムプロンプトとは無関係です。ユーザー — あるいはユーザーの代わりにホスト — が、特定の対話を起こすために呼び出せる、サーバー定義の再利用可能テンプレートです。Promptは「人格」よりも「スラッシュコマンド」に近い。狙いは、サーバーが公開するツールやリソースとともに「動くと分かっている対話パターン」を出荷して、キーボードの前にいる人が要求の言い回しを覚え直さなくていいようにすることです。
Promptは、名前、任意の説明、引数のリストを持ちます。ホストは prompts/list で発見し、prompts/get でPromptを「ホストがモデルループに流し込めるメッセージ列」へ展開します。PromptはURIでResourceを参照でき、その場合ホストは送信前にインライン化します。サーバーが最初の数ターンをスクリプト化し、そこからモデルが引き継ぎます。
Promptとシステムメッセージの違いは大事です。/review_pr を発動したユーザーは、自分の会話ログでアシスタントがレビューに取りかかるのを見ます — 何が始まったかを理解し、止められ、監査できる。代わりにサーバーがホストのシステムプロンプトに静かに指示を追記していたら、ユーザーはなぜアシスタントが突然違うふるまいをしたのか分かりません。Promptはユーザーから見える足場、システムプロンプトはホストの枠付け。両者を混ぜてはいけません。明示的に見せたらユーザーが承認しないだろうPromptの中身は、欠陥です。
3.3 Tools: 動作、構造化された出力、冪等性
MCPが面白くも危険にもなるのがToolです。Resourcesは読む、Promptsは足場、Toolsは書く — 行を作り、メッセージを送り、サービスをデプロイし、カードに課金します。各ToolはJSON Schemaで記述された名前、説明、入力スキーマを持ちます。説明はMCP面の中で最も重要なフィールドです。なぜなら、モデルがToolを呼ぶかどうか、いつ、どんな引数で呼ぶかを決めるために読むテキストだからです。「メールを送る」とだけ書かれたToolは、メールっぽい意図が出るたびに手が伸びます。「マーケティング・プラットフォーム経由でトランザクション・メールを送る、検証済みの顧客アドレス向け、私信には使わない」と書かれたToolは、より選別して使われます。
エラーモデルは2チャネルです。プロトコルエラー(壊れた引数、未知のツール)はJSON-RPCエラーを返し、フレーミングの失敗です。ツールエラー(宛先がバウンス、ディスクが満杯)は 成功した レスポンスを isError: true と内容ブロックで何が起きたかを説明します。区別が重要です: モデルはツールエラーを見て適応すべきで、ホストはプロトコルエラーを見てトランスポートを復旧すべき。混ぜると、モデルから必要な情報を奪います。
最近のMCPは、宣言された outputSchema に従う structuredContent を、散文の content 配列と並べてサポートします。長いトレース上での効きは本物です: 短い構造化されたツール出力は、モデルに実際の推論のための余裕を残し、長い散文の出力はコンテキスト予算を食べます。さらに2つの規律: 最小性 — よく説明された12のツールは、ほとんどのベンチマークで60のツールに勝ちます。list_users、get_user、search_users、count_users ではなく、構造化フィルタ付きの find_users を公開する。冪等性 — リトライの嵐は避けられません。冪等キーを受け取るか、決定的な識別子スキームを使うToolは、ネットワークの瞬断をデータ整合性事故ではなくno-opに変えます。
3.4 構成: 3つのプリミティブがどう協調するか
プリミティブは協調するときに最大に役立ちます。よく設計されたサーバーは普通、3つを混ぜて公開します: よくある対話を起こす少数のPrompts、本当に必要な動作のための小さなToolsセット、状況を満たす(場合によっては大きな)Resources。カスタマーサポートのサーバーなら、顧客プロファイルやチケット履歴をResourcesとして、reply_to_ticket と escalate_ticket をToolsとして、/triage_ticket をPromptとして公開し、適切なResourceを読み込んでモデルに分類を頼む。Promptsが種をまき、Resourcesが状況を埋め、Toolsが世界を変える。
プリミティブの柔軟さを見ると、すべてを一つで通したくなります。読み取り専用のToolでResourcesを偽装する、コマンドを出力するようモデルを誘導するPromptsでToolsを偽装する、指示を含むResourcesでPromptsを偽装する。どれも設計空間を圧縮し、何かを失います。Tools として偽装したResources はキャッシュも購読もPrompt展開へのインラインもできない。Prompts として偽装したToolsは呼び出し地点で認可できず、構造化された出力チャネルもない。プリミティブは恣意的ではなく、ホストがそれぞれを安全に扱う方法を知れるよう因数分解されています。因数分解を尊重するのがプロトコルの契約の一部です。
この章を踏まえて
サーバー側の取り決めを歩きました。サーバーはResources、Prompts、Toolsを公開し、それぞれが自分のスキーマ、ライフサイクル、エラーモデルを持ちます。規律は、サーバーができる各事柄に正しいプリミティブを選び、名前と説明を丁寧に書き、過負荷に抵抗すること。しかしMCPは一方通行ではありません。ホストもサーバーへ能力を貸し返せます — そしてそこに、プロトコルの最も面白く、セキュリティ上もっとも繊細な設計選択があります。
次回 — 第4章: クライアントプリミティブ — Sampling・Roots・Elicitation。 逆向きの面 — ホストがサーバーに貸し返すもの — と、信頼境界を越えて渡される各能力のセキュリティ上の含意。