ModelContextProtocol tools入門
こんにちは。開発部の松浦佑弥と申します!
現在は、jinjer株式会社で勤怠プロダクトの開発とAI推進を行っています。
最近はModelContextProtocol(以下、MCP)の社内利用の推進のために、仕様やセキュリティ周りについて調査/仕組み化を進めていました。
MCPに関する理解を深める中で、今後セキュリティリスクに対応するためにMCPの自作ニーズが一層向上すると予測しています。
本記事では、MCPの機能で一番よく使われるtools機能についての仕様とハンズオンをシェアできればと思います。
この記事の目的:
- MCP自作のための底力をつけるために仕様理解を向上させる
この記事で扱う内容:
- MCP公式リポジトリのtools周りの仕様 (Initialize ~ tools/callまで)
- PythonのFastMCPを使ったSTDIOベースの簡単なハンズオン
この記事で扱わない内容:
- そもそもMCPってなに?的な内容
- tools以外の機能
- リモートMCP
MCPの仕様はどこに定義されている?
参考: https://modelcontextprotocol.io/community/communication
modelcontextprotocol.ioのContributor Communicationページには、以下の3つの場所で仕様が議論されると明記されています。
- Discord
- GitHubのdicussions
- GitHubのissues
また、まとまった仕様は以下で管理、公開されています。
- modelcontextprotocolリポジトリの、docs/specification/やschema/
- modelcontextprotocol.ioのSpecificationページ
tools機能を理解する
続いて、MCPの仕様をのぞいてみましょう。
tools機能までの流れとしては、ハンドシェイク -> tools/listやtools/callをリクエストとなります。
tools機能の仕様を理解したいので、
- MCPのハンドシェイク
- tools/list, tools/call
について仕様を覗いてみましょう。
ハンドシェイク
説明:
ハンドシェイクは、MCPクライアントとMCPサーバーが機能のネゴシエーションやプロトコルバージョンの合意などを行う、初期化のフェーズです。
params内の各フィールドで、利用バージョンやMCPの各機能の設定などを指定します。
request -> response -> initialized notificationの流れでやりとりが行われます。
schema:
例:
# リクエスト(client -> server)
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"capabilities": {
"roots": {
"listChanged": true
},
"sampling": {},
"elicitation": {}
},
"clientInfo": {
"name": "ExampleClient",
"title": "Example Client Display Name",
"version": "1.0.0"
}
}
}
# レスポンス(server -> client)
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2024-11-05",
"capabilities": {
"logging": {},
"prompts": {
"listChanged": true
},
"resources": {
"subscribe": true,
"listChanged": true
},
"tools": {
"listChanged": true
}
},
"serverInfo": {
"name": "ExampleServer",
"title": "Example Server Display Name",
"version": "1.0.0"
},
"instructions": "Optional instructions for the client"
}
}
# initialized notification(client -> server)
{
"jsonrpc": "2.0",
"method": "notifications/initialized"
}
tools/list
参考:
説明:
MCPクライアントからMCPサーバーに利用可能なツールの一覧をリクエストし、ツールのリストが返却されます。paramsのcursorはページネーション機能に対応しています。
schema:
例:
# リクエスト(client -> server)
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list",
"params": {
"cursor": "optional-cursor-value"
}
}
# レスポンス(server -> client)
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"tools": [
{
"name": "get_weather",
"title": "Weather Information Provider",
"description": "Get current weather information for a location",
"inputSchema": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "City name or zip code"
}
},
"required": ["location"]
}
}
],
"nextCursor": "next-page-cursor"
}
}
tools/call
参考:
説明:
MCPクライアントからMCPサーバーにツール呼び出しをリクエストし、ツールの処理結果が返されます。
schema:
例:
# リクエスト(client -> server)
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "get_weather",
"arguments": {
"location": "New York"
}
}
}
# レスポンス(server -> client)
{
"jsonrpc": "2.0",
"id": 3,
"result": {
"content": [
{
"type": "text",
"text": "Current weather in New York:\nTemperature: 72°F\nConditions: Partly cloudy"
}
],
"isError": false
}
}
ハンズオン: 仕様通りに動くか確認してみる
参考: https://github.com/jlowin/fastmcp
FastMCPはPythonベースの、MCP仕様準拠のMCPサーバー構築ライブラリです。
ハンズオンではFastMCPを使うので、馴染みが薄い場合は、公式リポジトリのREADME.mdのガイドを参照ください。
理解したInitialize ~ tools/callまでのJSONRPCベースのやりとりを、FastMCPを使って再現してみましょう。
今回は、STDIOベースのMCPサーバーを構築して、標準入力を使って上述のJSON文字列を送信していきます。
今後理解を深める際も、ローカルでMCPサーバーを立てて実験してみることが非常に有効です。
手順
- python仮想環境の構築
python3 -m venv venv && . venv/bin/activate
- fastmcpをインストール
pip install fastmcp
- server.pyを作成して以下を実装
# server.py
from fastmcp import FastMCP
mcp = FastMCP("Demo 🚀")
@mcp.tool
def add(a: int, b: int) -> int:
"""Add two numbers"""
return a + b
if __name__ == "__main__":
mcp.run()
引用: jlowin/fastmcp/README.md
- server.pyを実行
python server.py
- 標準入力にJSONRPC形式の文字列を入力
initialize
{"jsonrpc": "2.0", "id": 0, "method": "initialize", "params": {"protocolVersion": "2024-11-05", "capabilities": {}, "clientInfo": {"name": "ExampleClient", "title": "Example Client Display Name", "version": "1.0.0"}}}
notifications/initialized
{"jsonrpc": "2.0", "method": "notifications/initialized"}
tools/list
{"jsonrpc": "2.0", "id": 1, "method": "tools/list"}
tools/call
{"jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": {"name": "add", "arguments": {"a": 1, "b": 2}}}
- レスポンスをみる
結果
╭─ FastMCP 2.0 ──────────────────────────────────────────────────────────────╮
│ │
│ _ __ ___ ______ __ __ _____________ ____ ____ │
│ _ __ ___ / ____/___ ______/ /_/ |/ / ____/ __ \ |___ \ / __ \ │
│ _ __ ___ / /_ / __ `/ ___/ __/ /|_/ / / / /_/ / ___/ / / / / / │
│ _ __ ___ / __/ / /_/ (__ ) /_/ / / / /___/ ____/ / __/_/ /_/ / │
│ _ __ ___ /_/ \__,_/____/\__/_/ /_/\____/_/ /_____(_)____/ │
│ │
│ │
│ │
│ 🖥️ Server name: Demo 🚀 │
│ 📦 Transport: STDIO │
│ │
│ 📚 Docs: https://gofastmcp.com │
│ 🚀 Deploy: https://fastmcp.cloud │
│ │
│ 🏎️ FastMCP version: 2.11.3 │
│ 🤝 MCP version: 1.13.1 │
│ │
╰────────────────────────────────────────────────────────────────────────────╯
[08/29/25 12:48:18] INFO Starting MCP server 'Demo 🚀' with transport 'stdio' server.py:1445
{"jsonrpc": "2.0", "id": 0, "method": "initialize", "params": {"protocolVersion": "2024-11-05", "capabilities": {}, "clientInfo": {"name": "ExampleClient", "title": "Example Client Display Name", "version": "1.0.0"}}}
{"jsonrpc":"2.0","id":0,"result":{"protocolVersion":"2024-11-05","capabilities":{"experimental":{},"prompts":{"listChanged":false},"resources":{"subscribe":false,"listChanged":false},"tools":{"listChanged":true}},"serverInfo":{"name":"Demo 🚀","version":"1.13.1"}}}
{"jsonrpc": "2.0", "method": "notifications/initialized"}
{"jsonrpc": "2.0", "id": 1, "method": "tools/list"}
{"jsonrpc":"2.0","id":1,"result":{"tools":[{"name":"add","description":"Add two numbers","inputSchema":{"properties":{"a":{"title":"A","type":"integer"},"b":{"title":"B","type":"integer"}},"required":["a","b"],"type":"object"},"outputSchema":{"properties":{"result":{"title":"Result","type":"integer"}},"required":["result"],"title":"_WrappedResult","type":"object","x-fastmcp-wrap-result":true},"_meta":{"_fastmcp":{"tags":[]}}}]}}
{"jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": {"name": "add", "arguments": {"a": 1, "b": 2}}}
{"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text","text":"3"}],"structuredContent":{"result":3},"isError":false}}
終わりに
普段何気なく使っているMCPサーバーとツールは、今回理解した明確な仕様のもと動いています。
MCPサーバーを自作する際には、基礎的な仕様理解が非常に重要です。
今回はMCPの仕様周りの概説と簡単なtools機能のハンズオンでしたが、好評であれば
- tools以外の機能
- セキュリティ周り
- リモートMCP
などについて、jinjer社内での知見や取り組み、今後の計画もシェアしていきたいと思っていますので、いいねのご協力をお願いします!
また、jinjerでは開発者を絶賛募集中です。
日本一の開発組織を目指しているので、興味のある方はご連絡ください!
Discussion