🌟

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

また、まとまった仕様は以下で管理、公開されています。

tools機能を理解する

続いて、MCPの仕様をのぞいてみましょう。
tools機能までの流れとしては、ハンドシェイク -> tools/listやtools/callをリクエストとなります。
tools機能の仕様を理解したいので、

  • MCPのハンドシェイク
  • tools/list, tools/call

について仕様を覗いてみましょう。

ハンドシェイク

参考: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-06-18/basic/lifecycle.mdx

説明:
ハンドシェイクは、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

参考:
https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-06-18/server/tools.mdx

説明:
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

参考:
https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-06-18/server/tools.mdx

説明:
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サーバーを立てて実験してみることが非常に有効です。

手順

  1. python仮想環境の構築
python3 -m venv venv && . venv/bin/activate
  1. fastmcpをインストール
pip install fastmcp
  1. 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

  1. server.pyを実行
python server.py
  1. 標準入力に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}}}
  1. レスポンスをみる
結果
╭─ 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では開発者を絶賛募集中です。
日本一の開発組織を目指しているので、興味のある方はご連絡ください!

jinjerテックブログ

Discussion