この記事は何?
この記事は MCP (Model Context Protocol) の仕組みについて僕が調べたことを書いています。
MCP って何? や使い方については既に Anthropic を始め、公式の記事が充実しています。
なので、内部の仕組みを知らなくても普通に使えはするのですが、僕はどうしても 「MCP ってどういう仕組み(内部が〜・仕様が〜)で動いてるの?」 が気になってしまうので調べました... 😅
MCP について(概要)
MCP の概要については、昨年11月に Anthropic がとても親切な記事を書いてくれています。
実際の使い方だけなら、次の記事を読めばすぐに始められます。
簡単に説明をすると、MCP の仕組みを用いると AI Agent (MCP Client) が MCP Server を介して外部の様々なリソースに自律的にアクセス(情報取得・変更処理)を行えるようになります。
例えば...
- Claude for Desktop の AI が、MCP Server を介してローカルのファイルを読み書きできたり
- Cursor や Cline がエラーの解消の目的で、MCP Server を介してデータベースの中を見ることができたり
実際に動かしてみる
ということで、実際に動かしてみて仕組みを探ります。
公式のこれ ↓ に従うと Python (& Anthropic API) で MCP Client コードが書けます。
実際に私が書いたものが ↓ です。少し改変して npx
で提供されている MCP Server を自由に使えるようにしました。
動作例
実際に Client コード (Anthropic API) を動かしてみます。
一緒に立ち上げる MCP Server は @modelcontextprotocol/server-filesystem にしました。この MCP Server を使うと、AI がローカルのファイルを参照できるようになります。
uv run client.py npx -y @modelcontextprotocol/server-filesystem "/Users/{username}/Desktop/"
client.py スクリプトに引数で渡しているコマンド ↓ が、MCP Server 単体の起動コマンドです。
npx -y @modelcontextprotocol/server-filesystem "/Users/{username}/Desktop/"
AI (Anthropic API) に「私のデスクトップ上のファイルを一覧して!」とお願いしてみました。
すると、次の通りデスクトップのファイル一覧を参照した上で回答をしくれます。
はい、デスクトップのファイルを一覧表示するために`list_directory`関数を使用します。指定されたパスで実行してみましょう。
[Calling tool list_directory with args {'path': '/Users/yuusuke-kawatsu/Desktop'}]
デスクトップ上のファイルとディレクトリを以下のように整理してお伝えします:
主なファイル:
1. PDFファイル:
- xxx.pdf
2. 画像ファイル:
- 各種PNG/JPG/GIFファイル (GREAT.png, LGTM.gif など)
- スクリーンショット画像
- プロフィール関連画像 (face.jpg, face_circle.png)
3. 開発関連ファイル:
- index.html
- index.js
4. 機密/業務関連ファイル:
- xxx
システムファイル:
- .DS_Store
- .localized
Client ⇔ Server 間の通信
MCP は Client ⇔ Server 間の通信プロトコルです。
MCP 自体は JSON-RPC 仕様に基づいています。
そのため、プロトコル下層の Client ⇔ Server 間のネットワーク経路は様々な手段を取れます。
たとえば前章の動かしてみたケースは「標準入出力を用いたプロセス間通信」でした。
MCP の具体的なスキーマ仕様は以下の Github repo で定義管理されています。
実際に MCP 通信の本文 (req/res) を覗いてみる!
MCP Server との通信は JSON-RPC メッセージを標準入出力でやりとりするだけです。
実際に Mac の Terminal 上で試してみましょう!
tmux の使い方
今回、MCP Server プロセスと標準入出力で通信をするために tmux を用います。
brew install tmux
tmux セッションを経由して、MCP Server (@modelcontextprotocol/server-filesystem) のプロセスを起動します。
tmux new-session -d -s my_session "npx -y @modelcontextprotocol/server-filesystem /Users/yuusuke-kawatsu/Desktop/"
早速、MCP Server へ標準入力で JSON-RPC リクエストを送信します。
tmux send-keys -t my_session '{"method": "initialize", "params": {"protocolVersion": "2024-11-05", "capabilities": {"roots": {"listChanged": true}}, "clientInfo": {"name": "mcp", "version": "0.1.0"}}, "jsonrpc": "2.0", "id": 0}' C-m
↑ メッセージの意味は次章で説明します。
すると、MCP Server から標準出力で応答が返ります。
tmux capture-pane -t my_session -p
Secure MCP Filesystem Server running on stdio
Allowed directories: [ '/Users/yuusuke-kawatsu/Desktop' ]
{"method": "initialize", "params": {"protocolVersion": "2024-11-05", "capabilities": {"roots": {"listChanged": true}}, "clientInfo": {"name": "mcp", "version": "0.1.0"}}, "jsonrpc": "2.0", "id": 0}
(ちゃんと tmux session を片付けておく)
tmux kill-session -t my_session
通信の中身
ここからは、Client ⇔ Server 間で具体的にどのようなメッセージ内容のやり取りが行われているか? を見ていきましょう。
MCP Server は引き続き @modelcontextprotocol/server-filesystem を例にしています。
1. 初期化メッセージの送信
Client から MCP Server へ、初期化の指示を送っています。
{
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"capabilities": {
"roots": {
"listChanged": true
}
},
"clientInfo": {
"name": "mcp",
"version": "0.1.0"
}
},
"jsonrpc": "2.0",
"id": 0
}
完了したよ!の応答。
{
"jsonrpc": "2.0",
"id": 0,
"result": {
"protocolVersion": "2024-11-05",
"capabilities": {
"tools": {}
},
"serverInfo": {
"name": "secure-filesystem-server",
"version": "0.2.0"
}
}
}
2. クライアント側の準備OK通知
Client 側の準備が完了したことを MCP Server へ通知しています。
{
"method": "notifications/initialized",
"jsonrpc": "2.0",
"params": null
}
method が notifications
の場合は MCP Server からの応答はありません。
3. Client が MCP Server へ、利用可能な tools を問い合わせする
Client が MCP Server へ、どのような tools が利用可能か? を問い合わせしています。
これによって AI は、状況に応じて tools の利用可否を自律的に判断出来るようになります。
{
"method": "tools/list",
"params": null,
"jsonrpc": "2.0",
"id": 1
}
(長いので大分省略しましたが) tools に関する説明とインターフェース仕様についての応答ですね。
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"tools": [
{
"name": "read_file",
"description": "Read the complete contents of a file from the file system. Handles various text encodings and provides detailed error messages if the file cannot be read. Use this tool when you need to examine the contents of a single file. Only works within allowed directories.",
"inputSchema": {
"type": "object",
"properties": {
"path": {
"type": "string"
}
},
"required": [
"path"
],
"additionalProperties": false,
"$schema": "http://json-schema.org/draft-07/schema#"
}
},
{
"name": "read_multiple_files",
"description": "Read the contents of multiple files simultaneously. This is more efficient than reading files one by one when you need to analyze or compare multiple files. Each file's content is returned with its path as a reference. Failed reads for individual files won't stop the entire operation. Only works within allowed directories.",
"inputSchema": { ... }
},
{
"name": "write_file",
"description": "Create a new file or completely overwrite an existing file with new content. Use with caution as it will overwrite existing files without warning. Handles text content with proper encoding. Only works within allowed directories.",
"inputSchema": { ... }
},
{
"name": "edit_file",
"description": "Make line-based edits to a text file. Each edit replaces exact line sequences with new content. Returns a git-style diff showing the changes made. Only works within allowed directories.",
"inputSchema": { ... }
},
{
"name": "create_directory",
"description": "Create a new directory or ensure a directory exists. Can create multiple nested directories in one operation. If the directory already exists, this operation will succeed silently. Perfect for setting up directory structures for projects or ensuring required paths exist. Only works within allowed directories.",
"inputSchema": { ... }
},
{
"name": "list_directory",
"description": "Get a detailed listing of all files and directories in a specified path. Results clearly distinguish between files and directories with [FILE] and [DIR] prefixes. This tool is essential for understanding directory structure and finding specific files within a directory. Only works within allowed directories.",
"inputSchema": { ... }
},
{
"name": "directory_tree",
"description": "Get a recursive tree view of files and directories as a JSON structure. Each entry includes 'name', 'type' (file/directory), and 'children' for directories. Files have no children array, while directories always have a children array (which may be empty). The output is formatted with 2-space indentation for readability. Only works within allowed directories.",
"inputSchema": { ... }
},
{
"name": "move_file",
"description": "Move or rename files and directories. Can move files between directories and rename them in a single operation. If the destination exists, the operation will fail. Works across different directories and can be used for simple renaming within the same directory. Both source and destination must be within allowed directories.",
"inputSchema": { ... }
},
{
"name": "search_files",
"description": "Recursively search for files and directories matching a pattern. Searches through all subdirectories from the starting path. The search is case-insensitive and matches partial names. Returns full paths to all matching items. Great for finding files when you don't know their exact location. Only searches within allowed directories.",
"inputSchema": { ... }
},
{
"name": "get_file_info",
"description": "Retrieve detailed metadata about a file or directory. Returns comprehensive information including size, creation time, last modified time, permissions, and type. This tool is perfect for understanding file characteristics without reading the actual content. Only works within allowed directories.",
"inputSchema": { ... }
},
{
"name": "list_allowed_directories",
"description": "Returns the list of directories that this server is allowed to access. Use this to understand which directories are available before trying to access files.",
"inputSchema": { ... }
}
]
}
}
4. Client が MCP Server へ tools の使用を要求
tool list_directory
の使用を要求しています。arguments
で対象のディレクトリを指定していますね。
{
"method": "tools/call",
"params": {
"name": "list_directory",
"arguments": {
"path": "/Users/yuusuke-kawatsu/Desktop"
}
},
"jsonrpc": "2.0",
"id": 3
}
(恥ずかしいのでふせましたが...😅) 私のデスクトップ上のファイル一覧が返ってきています。
{
"jsonrpc": "2.0",
"id": 3,
"result": {
"content": [
{
"type": "text",
"text": "[FILE] .DS_Store\n[FILE] 20240705_aaaaa.pdf\n[DIR] images\n"
}
]
}
}