header image

枝折

MCP の仕組みや作り方について

生成 AI

CREATED: 2026 / 06 / 23 Tue

UPDATED: 2026 / 06 / 23 Tue

MCP がどのような仕組みで動いているのかを改めて考えてみる

RAG と FunctionCalling

RAG による回答の拡張

RAG(Retrieval Augmented Generation) はデータを用いて LLM の回答を補強するための仕組みです。

従来のシステムにおける RAG ではアプリケーションのサーバーがユーザーからリクエストを受け付け、その内容を LLM に渡し、必要に応じて外部の情報を提供しながら LLM から最終的な回答を引き出すというような方法を取っていました。

以下の図はこの構成のイメージです👇

flowchart LR
    A[ユーザー]-- リクエスト -->B[サーバー]
    B[サーバー]-- 推論リクエスト -->C[LLM]
    C[LLM]-- 推論レスポンス -->B[サーバー]
    B[サーバー]-- 外部情報参照 -->D[外部情報(ナレッジベース)]
    D[外部情報(ナレッジベース)]-- 外部情報提供 -->B[サーバー]

特定の限定的な機能であればこれでも問題なく回るものではありましたが、ユーザーのリクエストから様々な条件分岐を経て最適な回答(かつ豊富でリッチな回答)を導き出すためには、LLM に必要な外部情報をサーバーで細かく判断して外部から必要な情報を取得する必要がありました。

LLM に情報を取得させれば良いじゃないかと言う話もあるかもしれませんが、LLM の非決定性では返答の安定性を確保しづらく、外部 API などの呼び出しはあくまで冪等なプログラム上で実行する方がベターであると考えられます。

まあ、そんなわけで、この実装方法は人によって、組織によって異なっていたので、実装パターンが統一されず、再利用性に乏しいものになっていました。

こんな経緯で FunctionCalling が登場するのですが、この登場以前にも、LLM に外部情報カタログを投げて必要に応じて外部情報をリクエストさせるような仕組みは作られていました(だからこそ FunctionCalling が登場したのでしょうが)。
しかし、自作したそのような仕組みではなかなか安定性を得られなかったようです。

FunctionCalling によるツール選定の安定化

RAG の仕組み上で、LLM が必要な情報を自発的にリクエストできるように、そのプロンプトの中にツールカタログ(呼び出し可能な機能のリスト)を組み込んで、その中から LLM に必要な情報源を選ばせる仕組みを FunctionCalling と呼びます。

前述のようにこの仕組みを自作することもできましたが、ChatGPT や Claude はこの仕組みをモデルレベルで組み込んでいるので、かなり精度が安定するようになりました。
今となっては MCP を支える基本的な仕組みになっています。

FunctionCalling を利用すると、サーバーから LLM への推論リクエストであるプロンプトに、提供可能なツールのカタログを含めることになります(フローチャート再掲👇)。

flowchart LR
    A[ユーザー]-- リクエスト -->B[サーバー]
    B[サーバー]-- 推論リクエスト -->C[LLM]
    C[LLM]-- 推論レスポンス -->B[サーバー]
    B[サーバー]-- 外部情報参照 -->D[外部情報(ナレッジベース)]
    D[外部情報(ナレッジベース)]-- 外部情報提供 -->B[サーバー]

そうすると、LLM レスポンスとして以下のようなデータを取得することができるようになります(あくまで例です)🗒️

{
	"response_type": "function_call",
	"function_name": "request_api",
	"argument": {
		"words": ["AI でブロック崩しを作る方法"]
	}
}

これを受け取ったアプリケーションのサーバーは、なるほど、FunctionCalling で request_api と言う関数呼び出しが行われているなと判断し、指定された関数を呼び出して設定されている API にアクセスします。
この処理については LLM は絡まないので、冪等な処理が行われるイメージです。

と言うことで便利な FunctionCalling なのですが、プロンプトに利用可能なツールのカタログを組み込むのでその分プロンプトが圧迫されます。
これは MCP でもデメリットとしてよく知られているところの話です👀

で、もう一つ面倒なのが、選定されたツールの呼び出しはあくまでアプリケーションのサーバーで行うと言う点です。
外部情報を提供する API の数が増えるほど面倒になるイメージですが、それぞれが別々のインターフェースを持っており、個別に設定するのは正直面倒です。

MCP はこの面倒な外部情報ごとの呼び出しの仕組みをまとめた規格です。
各外部情報の提供元がこの MCP に従って MCP サーバーを提供すれば、利用者は MCP クライアント(Claude Code など)を用いてそこにアクセスするだけで済みます。

MCP の登場

FunctionCalling である簡単になったツール呼び出しですが、次にそのツールを呼び出すと言う仕組みづくりを標準化したいと言う話になりました。
その結果生まれたのが MCP です。

MCP には MCP サーバーと MCP クライアントで構成されます。

MCP クライアントは自作することもできるかと思いますが、基本的に Claude Code とかを使えば良いです。

MCP サーバーは MCP クライアントからのリクエストを受け付けるサーバーです。
このサーバーは外部情報の提供元によって公開されます。

図に以下のようなイメージです👇

flowchart LR
    A[ユーザー]-- リクエスト -->B[サーバー]
    B[サーバー]-- 推論リクエスト -->C[LLM]
    C[LLM]-- 推論レスポンス -->B[サーバー]
    B[サーバー]-- ツール呼び出しが必要なら -->E[MCP クライアント]
    E[MCP クライアント]-- 外部情報要求 -->F[MCP サーバー]
    F[MCP サーバー]-- 情報を返す -->E[MCP クライアント]

アプリケーションのサーバーは事前に MCP クライアント経由で MCP サーバーに呼び出し可能なツールのカタログを要求し、これを LLM へのリクエストプロンプトの中に埋め込みます。
推論レスポンスにツールの呼び出しが含まれていれば、サーバーは MCP クライアント経由で MCP サーバーのツールを呼び出します。

このように呼び出し箇所を自前で書かずに、MCP クライアント経由で MCP サーバーのツールを呼び出すだけになってます。
これによって外部情報を取得するためのツールの可用性が高まり、実装が楽になりました 💪

MCP はこれらクライアントとサーバーのやり取りの作り方を標準化した規格で、外部情報の提供元がこれらの規格に従って MCP サーバーを作ることで、誰もが MCP クライアントを用いて同じ方法で MCP サーバーにアクセスして欲しい情報を取得することができるようになります。

MCP クライアント/サーバーの接続について

MCP クライアントとサーバーの接続には stdio(標準入出力)か SSE(Server Side Event)か Streamable HTTP が利用されます。
基本的にローカルでサーバーを立ち上げる場合は stdio を、リモートでサーバーを立ち上げる場合は SSE か Streamable HTTP を利用することになるかと思います。

ただし、SSE はあまり推奨されていないようなので、リモートでサーバーを立ち上げる場合は Streamable HTTP を利用するのが良さそうです。

Model Context Protocol | Streamable HTTP

で、データのやり取りは JSON RPC を利用して行われます。
エンジニアに馴染みがある JSON 形式の Remote Procedure Call でクラサバ間のデータのやり取りができます。

大まかに、MCP クライアントとサーバーのやり取りは以下のようなイメージです👇

sequenceDiagram
    participant client as MCP クライアント
    participant server as MCP サーバー
    client->>server: 1. 接続要求
    server->>client: 2. 接続完了
    client->>server: 3. ツールのカタログを要求
    server->>client: 4. ツールのカタログを返却
    client->>server: 5. ツールを利用
    server->>client: 6. ツールの実行結果を返却
    client->>server: 7. 切断要求
    server->>client: 8. 切断完了

MCP サーバーの設計について

MCP サーバーを実装する場合、どの外部情報をどれだけ参照し、何をクライアントに返すべきかを考える必要があります。

例えば、単純に天気予報のデータを返すべきなのか、天気予報のデータを参考に外出先に適した観光名所の情報やアクティビティに関する情報も返すべきなのか、といった感じです。

アーキテクチャについてはシステム一般で言えること同じで、シンプルかつ可用性を高く維持できるような構成を心がけると良いかと思われます。
次々と新しいツールに対応する必要が出てきた時に、可用性が低く各コンポーネントの依存度が高いシステムだと、機能の拡張性が損なわれてしまいます👀

以下の参考書ではこの辺の設計や、インフラ構築の考え方からテストまで概観しているので良いです👇
MCPサーバー開発大全 独自サーバーの実装から自動テストの構築まで

を仕舞い

MCP の仕組み中心に整理しましたが、後ほど MCP における認可と認証についても書く予定です📝