1. なぜ専用のプロトコルが必要なのか
本モジュールではようやく、MCP(Model Context Protocol)とは何か、そしてそれが ChatGPT App のスタックにどう収まるのかを整理します。まずはアーキテクチャにおける MCP の位置付けを確認し、「典型的な REST」と比較し、プロトコルの基本エンティティである tools、resources、prompts を理解しましょう。
あなたが普通のウェブサービスを作っていると想像してください。昔ながらに REST API を立てます。例えば /api/gifts、/api/users、/api/orders のようなパスがあり、それぞれ入出力形式・エラーコード・認可のやり方が違います。これは馴染みがありますが、1 つ問題があります。各クライアントに対して「何をどう実装したのか」を毎回説明しなければならないのです。ドキュメント、OpenAPI、サンプル、SDK — こうしたものが必要なのは、API の形式をあなたが独自に決めているからです。
ChatGPT App では状況がさらに複雑になります。クライアントはフロントエンドだけでなく、モデルそのものでもあります。モデルは次のことを知る必要があります。
- どの操作が利用可能なのかを把握する;
- 各操作にどんな引数が必要かを理解する;
- 対話の途中でそれらの操作を呼び出す(時には複数回、パラメータを変えて呼ぶ);
- 構造化された応答を解釈し、ユーザーに見せるべきものと、次の発話のためのコンテキストとして使うべきものを判断する。
もし各開発者が好き勝手に API 形式を作ると、モデルは統合作業の地獄に陥ります。アプリごとにカスタムクライアントと大量の「ラッパー」コード、壊れやすいロジックが必要になります。ここで「プロトコル」という発想が問題を解決します。
MCP(Model Context Protocol)は、LLM クライアント(ChatGPT、IDE プラグイン、エージェント等)があなたのツール/データサーバーとやり取りする標準的な方法を定めたオープン仕様です。サーバーが自分のツール、リソース、プロンプトを宣言し、クライアントがそれらを呼び出して結果を受け取るための共通言語を提供します。
直感的には、MCP は AI の世界における USB‑C ポートのようなものです。あなたが「フラッシュドライブ」(サービス、データベース、CRM、検索エンジン)を作るなら、標準のコネクタを 1 つ実装すればよい、という考え方です。そうすれば、どんな「ノート PC」(ChatGPT、他のエージェント、IDE)でもカスタムケーブルなしで接続できます。
2. 俯瞰: ChatGPT App のアーキテクチャにおける MCP の位置
全体像を固定するために、すでにおなじみのアーキテクチャを思い出しつつ、今度は MCP レイヤーを明示して見てみましょう。
これまでのメンタルモデルはこうでした。ユーザーは ChatGPT と対話し、その中でウィジェット(Apps SDK)がレンダリングされ、どこか外側にあなたのバックエンドが存在する。ここに MCP を加えて、すべてをレイヤーに分解してみます。
簡略化した図:
ユーザー
↓(自然言語)
ChatGPT(モデル + UI)
↓(MCP を介した tool calls)
ChatGPT 内の MCP クライアント
↓(JSON-RPC, MCP)
あなたの MCP サーバー(バックエンド)
↓
あなたの DB / 外部 API / キュー
ここでいう「ChatGPT 内の MCP クライアント」とは、あなたの MCP サーバーとプロトコルで対話するプラットフォーム内部のコンポーネントを指します。ディスカバリーを行い、ツールを呼び出し、リソースを読みます。
Apps SDK の観点では、最小の ChatGPT App は 3 つのコンポーネントから成ります。1 つ目は、ツールを宣言し構造化データを返す MCP サーバー。2 つ目は、ChatGPT 内でレンダリングされ、window.openai を通じてこれらのデータを読む UI バンドル(ウィジェット)。3 つ目は、いつどのツールを呼び出すか、ユーザーにどう返答するかを判断するモデルです。
ここで重要なのは次の点です。これまでのモジュールでは、主に Apps SDK とウィジェットという、図の上半分で作業してきました。今は MCP サーバーというレベルに降りていきます。これは ChatGPT や、あなたの App を使う他のクライアントと公式に「会話」するための言語です。
3. MCP 対「典型的な REST」: 何が違うのか
上の図で、MCP が ChatGPT App のアーキテクチャのどこに位置するかは確認できました。では、「自前の REST」と MCP を丁寧に比較して、なぜ ChatGPT Apps の文脈では後者がほぼ常に有利なのかを見ていきましょう。
REST 的アプローチでは、エンドポイントやリクエスト/レスポンス形式を、あなたの都合で設計します。クライアントが連携するには、URL・メソッド・スキーマ・エラーコードを知る必要があります。OpenAPI が助けになることもあれば、README に例を貼るだけのこともあります。モデル自体はそれらを何も理解しません。「50 歳の母に合うプレゼントを選んで」のような自然言語を具体的な HTTP リクエストに変換し、戻ってきた JSON を対話に使えるデータに戻すためのコード層が必要になります。
一方、MCP では次のことがプロトコル自体で定義されています。
- クライアントがあなたのツール一覧をどう取得するか;
- 引数や結果を JSON Schema でどう記述するか;
- リソースやプロンプトをどう記述するか;
- ツールの呼び出しとその応答がどう見えるか。
これにより、ChatGPT などの MCP クライアントは自動的に次のことができます。
- discovery を実行し、tools/resources/prompts を把握する;
- 各ツールのパラメータスキーマを内部的に構築する;
- カスタムのハードコードなしで呼び出す;
- メタデータをキャッシュし、アプリの検索やランキングに利用する。
違いは次の小さな表にまとめられます。
| 問い | 自前の REST / gRPC | MCP |
|---|---|---|
| クライアントはあなたが何をできるかをどうやって知る? | ドキュメント、README、OpenAPI から | 標準的な discovery メソッド経由(tools/resources の一覧) |
| 誰がパラメータを記述する? | あなたが任意形式で(JSON、FormData など) | ツールのフィールド内で JSON Schema により |
| モデルはどうやって関数を呼ぶ? | あなたのカスタムクライアントコード経由 | MCP のプリミティブで直接 |
| クライアント側の「お膳立て」はどれくらい? | 多い。サービスごとに異なる | すべての MCP サーバーに共通の 1 つのプロトコル |
| 複数クライアントからの利用 | クライアントごとに SDK を書く必要 | MCP サーバーは自己記述的。クライアントはロジックを再利用可能 |
ざっくり言えば、REST は「各自バラバラ」、MCP は「モデルとデータにどう対話するかについてエコシステム全体の合意」です。
4. MCP の主要エンティティ: tools、resources、prompts
では MCP の 3 人の主役、ツール、リソース、プロンプトを名前付きで紹介します。
Tools: なじみのある「操作」
tools にはモジュール 4 で既に触れました。そこで私たちはツールを記述し、名前・説明・引数の JSON Schema を与え、モデルが callTool を通じてそれを呼び出しました。MCP レベルでは、ツールは明確な契約を持つサーバー側のオペレーションです。
- 名前と説明(モデル用および UX/ディスカバリー用);
- 引数のための JSON Schema;
- 結果構造の JSON Schema または記述;
- 追加のメタ情報(例えば Apps SDK の特定 UI コンポーネントへの紐付け)。
MCP サーバーは少なくとも「ツール一覧の要求」に応え、「ツールの呼び出し」を処理し、構造化された結果を返せる必要があります。
学習用の Gift アシスタントアプリには、例えば suggest_gifts というツールがあり、年齢・性別・予算・嗜好などを受け取り、推薦ギフトのリストを返すとします。
仮の TypeScript スケッチは次のようになるでしょう(擬似コード/スタブ):
// 疑似コード。最終的な SDK API ではありません
const suggestGiftsTool = defineTool({
name: "suggest_gifts",
description: "受取人の条件に基づいてギフト案を提案します",
inputSchema: z.object({
age: z.number(),
relation: z.enum(["friend", "partner", "parent"]),
budgetUsd: z.number(),
}),
handler: async (input) => {
// TODO: あなたのビジネスロジック
return { items: [] };
},
});
具体的なシグネチャは次回以降に扱います。ここで大事なのは、ツールは単なる REST エンドポイントではなく、スキーマを伴って宣言されるプロトコル要素だという点です。
Resources(リソース): ID/URI で参照できるデータ
MCP におけるリソース(resources)は、利用可能なデータ(ファイル、ディレクトリ、DB レコード、Wiki ページ、検索インデックスの結果など)を記述する手段です。クライアントは次のことができます。
- リソースの一覧を取得する;
- ID/URI で特定のリソースを読む;
- 場合によっては検索を実行する。
何かを「する」tools と異なり、resources は通常「何かを保持」します。たとえば Gift‑App では、商品カタログをリソース gift_catalog として表し、モデルがそこにアクセスして利用可能なカテゴリやフィルタ、価格帯などを知る、といったことが可能です。
コード上の概念例は次のようになります:
const giftCatalogResource = defineResource({
uri: "catalog://gifts",
description: "レコメンドに利用可能なギフトのカタログ",
read: async () => {
// カタログの構造を返す
return { categories: [], priceRanges: [] };
},
});
ここではまだ MCP メッセージ形式の詳細には踏み込みません。リソースはアドレス指定可能なエンティティであり、MCP サーバーが参照し、クライアントが読み取ってコンテキストの一部として利用できる、と覚えておきましょう。
Prompts: 用意済みのプロンプト
MCP の文脈における Prompts は、サーバーがクライアントに提供できる問い合わせや指示のテンプレートです。例えば、ツールを呼び出す前にユーザーから受取人の詳細を確認する方法を記述したプロンプト gift_followup を宣言する、といった使い方です。
プロトコル的な典型例としては、サーバーがプロンプトの名前・目的・必要に応じてパラメータを与えます。クライアントはプロンプトの一覧を取得し、必要なものを選び、モデルへのリクエストに差し込みます。
なぜ ChatGPT App に必要なのでしょうか。第一に、複雑なプロンプトをクライアント間で再利用するための統一的な方法になるからです。第二に、MCP はこうしたプロンプトを暗黙ではなく契約に基づいた「明示的なもの」にします。
Capabilities: 何をサポートしているかの宣言
最後に 4 つ目の要素が capabilities です。これは単なる宣言で、サーバーがどのエンティティ(tools、resources、prompts、通知など)をサポートし、どのメソッドを実装しているかを示します。クライアントにとっては「何ができて何ができないのか」を推測せずに済み、サーバーの能力に合わせて振る舞いを調整する手段です。
実際には、ChatGPT はあなたの MCP サーバーに接続すると、まず「ハンドシェイク」を行い、capabilities の一覧を取得し、その後に「ツールやリソースを見せて」と問い合わせます。
5. MCP は既存の App にどう組み込まれているか
ここまで少し抽象的に聞こえるかもしれませんが、実はあなたは既に Apps SDK を通じて MCP に触れています。これまでに書いた Apps SDK のコードとどう繋がるのかを確認しましょう。先ほど導入したエンティティを、現在の App テンプレートの構成に結び付けます。
テンプレートで既に実装したチェーンを振り返りましょう:
- ウィジェットが window.openai または用意されたフックを通じて、ツール名と引数を指定して callTool を呼び出す。
- ChatGPT 内の Apps SDK が、それを App のサーバー側への呼び出しに変換する。
- サーバーはツールを実行し、ToolOutput(structuredContent、content、_meta を含む)を返す。
- ウィジェットが ToolOutput を受け取り、UI を描画する。
ポイントは、手順 2–3 が MCP によるダイアログとして実装されていることです。あなたの Next.js テンプレートには(通常は app/mcp/route.ts などの)エンドポイントがあり、それがまさに MCP サーバーです。そこでは次のことが行われます。
- ツールの登録;
- JSON Schema による記述;
- ハンドラの実装;
- ChatGPT からの MCP リクエスト list tools と call tool に応答。
つまり、テンプレートを使っているだけでも、実は既に MCP を使っています。プロトコル周りの多くの処理が SDK に隠蔽されているだけなのです。
モジュール 6 の目的は、MCP を「魔法のブラックボックス」として扱うのをやめ、意識的に設計できるようにすることです。
- ツールを追加・バージョン管理する;
- tools だけでなく resources や prompts も使う;
- MCP のログを読み解く;
- 必要に応じて Next.js テンプレート外で独立した MCP サーバーを立てる(例: ML モデル用の Python サービス、企業データベースへのアクセス専用サービスなど)。
6. 役割ごとに見る MCP: プロダクト vs. 開発者
MCP がプロダクトマネージャーとエンジニアに何をもたらすかを、分けて整理しておくと有用です。
MCP(プロダクト向け)
プロダクトの観点では、MCP はあなたのサービスを ChatGPT、他の LLM クライアント、IDE プラグイン、自社エージェントなど「多様なクライアントに接続可能なモジュール」にする方法です。サーバーの能力を tools/resources/prompts の集合として一度記述すれば、どんなクライアントでも次のことができます。
- あなたのサービスを自動的に検出する;
- どの課題を解決できるかを理解する;
- 必要な操作を安全に呼び出す。
ChatGPT App の場合、これはあなたのアプリが選ばれる確率向上にもつながります。モデルはあなたのツールに関するメタデータを使って、いつユーザーにあなたの App を提案するか、どう提示するかを判断するからです。
ごく簡潔に言えば、MCP はあなたのサービスを「1〜2 個のクライアント専用のカスタム統合」ではなく、エコシステムの標準的な「ブロック」にしてくれます。
MCP(開発者向け)
エンジニアの観点では、MCP は契約でありプロトコルです。次の問いに答えてくれます。
- ツールはどの形式で宣言すればよいか?
- 引数をどう記述し、結果をどう返せばよいか?
- クライアントは、私がリソースやプロンプトをサポートしていることをどう理解するか?
- どんな JSON がネットワークを流れるのか?
プロトコルがあると、次のことが容易になります。
- 異なる言語でサーバーを書く(TypeScript と Python の公式 SDK があります);
- MCP Inspector などのツールでアプリをデバッグする;
- チーム間で責務分担する: あるチームはデータとツールを持つ MCP サーバー、別のチームは Apps SDK のウィジェット、さらに別のチームは同じ MCP サーバーの上に独自のエージェントを構築する、といった具合です。
7. 小さな実践的視点: 最初の MCP サーバー
この講義では、メッセージ形式やサーバー実装の詳細にはあえて踏み込みません(それは次のトピックです)。ただ、どこに向かっているのかを先に掴むために、TypeScript での最小構成の MCP サーバーの全体像を見ておきましょう。
実際には、公式の TypeScript 向け MCP ライブラリが、サーバー作成、tools/resources/prompts の登録、トランスポート(通常は HTTP や SSE)の起動といったプリミティブを提供します。
概念的な擬似例は次のようになります:
// これは概念的な例です。SDK の API は後で解説します
import { createServer } from "@modelcontextprotocol/sdk";
const server = createServer({
name: "gift-genius",
version: "1.0.0",
});
// ツールを登録
server.tool("suggest_gifts", {
description: "受取人の嗜好に基づいてギフトを選定します",
inputSchema: {/* ... */},
handler: async (input) => {
// あなたのロジック
return { items: [] };
},
});
// トランスポートを起動(例: HTTP)
server.listen(3001);
重要な点は、ここには ChatGPT、Apps SDK、あなた固有のフロントエンドへの言及が一切ないことです。MCP サーバーは自律的で、MCP リクエストに応答できるだけの存在です。ChatGPT App は、そうしたサーバーを利用するクライアントの形態の 1 つに過ぎません。
本講座では Next.js テンプレートをベースに進め、MCP サーバーはプロジェクトの一部として動かしますが、これが唯一の選択肢ではありません。
8. エコシステムにおける MCP: Apps SDK、Agents SDK、ACP
MCP を「Apps SDK だけの機能」と捉えないために、もう少し広い視野で見てみましょう。
第一に、Apps SDK は ChatGPT と外部サービスをつなぐ標準ブリッジとして、MCP に直接依存しています。公式ドキュメントは、Apps SDK があらゆる MCP サーバーと動作することを強調しています。プロトコル自体が、ツールの記述、構造化データの返却、UI におけるレンダリングコンポーネントの指定を可能にします。
第二に、別モジュールで扱う Agents SDK も、MCP サーバーに接続できます。つまり、同じビジネスロジックを持つ MCP サーバーを次のように再利用できます。
- ChatGPT の内部で、あなたの App の一部として;
- 自社製品の裏側やバッチモードで動く自律エージェントの内部で。
第三に、購入や Instant Checkout に必要となる ACP(Agentic Commerce Protocol)は、論理的には MCP 的アプローチの上に成り立っています。モデルやエージェントはコマース系のツールを呼び出しますが、それらも標準化された契約で記述されます。
このように、MCP は UI(Apps SDK)、エージェントのシナリオ(Agents SDK)、コマース(ACP)が乗る基盤になっています。MCP をしっかり理解していれば、他の要素も一層分かりやすく、予測可能になります。
注: 形式的には ACP は仕様として MCP に依存していませんが、実装上はモデルが ACP のツールを MCP インターフェース経由で呼び出すことになるはずです。両者は非常に相性が良く、綺麗に重なります。実用化まで、もうそう長くはありません。
9. 実践前のちょっとした「頭の体操」
次回、MCP メッセージ形式に飛び込む前に、いくつか思考トレーニングをしておくと良いでしょう。これにより、「典型的な REST」から「プロトコル + 契約」への発想転換が進みます。
あなたの Gift‑App につながるのが ChatGPT だけでなく、VS Code の IDE プラグインや社内 Slack のアシスタントでもあると想像してください。彼ら全員があなたのサービスについて知るべきことを、一文で表現してください。おそらく答えはこうなるでしょう。「私たちには suggest_gifts というツール(このようなパラメータ)があり、ギフトカタログがこのリソース経由で利用できる」。これこそ MCP が形式化している内容です。
次の 2 点も短く言語化してみてください:
- あなたの App のプロダクトにとっての MCP(ヒント: 複数クライアント向けに機能を「パッケージ化」する標準的手段);
- 開発者にとっての MCP(ヒント: 明確な tools/resources/prompts プリミティブを持つ JSON‑RPC プロトコル)。
これを滞りなく説明できるなら、あなたはすでに MCP を自信を持って扱える地点の半分まで来ています。
要点を 1 文にまとめるならこうです。MCP は単なる追加の抽象 API ではなく、あなたのロジックと LLM クライアントの間の基本契約です。次の講義ではプロトコル内部を覗き、MCP メッセージ形式、handshake/capabilities を分解し、インスペクタを使ってトラフィックを確認する方法を学びます。抽象論ではなく、実務で使える道具にしていきましょう。
10. MCP をめぐる典型的な誤りと誤解
間違い 1: MCP を「自分の REST の上に重ねるもう一枚の API 層」とみなす。
ついこう考えたくなることがあります。「もう REST はあるし、薄いアダプターで MCP 呼び出しと REST を相互変換してしまえば忘れられる」と。形式上は可能ですが、その場合、古い API の癖(妙な型、非構造化な応答、明示的スキーマの欠如)を MCP 内に「引きずり込む」ことになりがちです。アダプターは肥大化し、MCP の利点が薄れます。MCP を主契約として捉え、既存の REST は必要なら実装詳細に留める方が得策です。
間違い 2: MCP は「ChatGPT Apps 専用」だと思う。
MCP は ChatGPT、IDE プラグイン、自律エージェントなど、あらゆる LLM クライアント向けのオープンな共通プロトコルです。特定の 1 つの App だけを念頭に MCP サーバーを設計すると、将来の可能性を狭めてしまいます。「他のクライアントからも使われる」という前提で、ツールとリソースを少し汎用的に設計する方がずっと有利です。
間違い 3: JSON Schema を無視して、引数を「口頭」で説明する。
SDK が「任意の JSON」を渡せるように見えても、引数と結果のスキーマは丁寧に記述しましょう。これは、モデルがツールを正しく呼び出す能力、補完やディスカバリーの品質、インスペクタ等によるデバッグのしやすさに直結します。未記述・不十分なスキーマは、謎の tool‑call エラーへの近道です。
間違い 4: MCP を「魔法のトランスポート」と捉え、ログを見ない。
すべてが動いている間は、MCP は「気にしなくていい見えないもの」に感じられます。ところが何かが壊れると、MCP の構造を理解していない限り、「Apps SDK が悪いのか? モデルか? バックエンドか?」と長時間迷うことになります。早い段階から MCP メッセージやログを見る習慣が、無駄な時間を救ってくれます。
間違い 5: 複雑なワークフローを REST だけで設計し、MCP のプリミティブを無視する。
多段のシナリオ(ギフト検索 → 嗜好の確認 → 選定 → 注文手続き)が出てくると、「大きな REST エンドポイントを 1 つ作ればよい」と思いがちです。ChatGPT Apps の文脈では、これは管理性を悪化させることが多いです。モデルは中間ステップを理解しづらく、MCP クライアントはリソースやプロンプトを再利用する機会を失います。機能は複数の、よく記述された tools/resources に分解し、システムプロンプトや適切な記述でロジックを結ぶ方がはるかに良いのです。
GO TO FULL VERSION