はじめに
MCPサーバ初心者です。まだ何も分からないです。
公開レポジトリから、GitHub Copilot が無料で使えるので、感覚をつかむ意味で簡単なサンプルを作りました。これから MCPサーバを試そうと考えている人向けの記事です。
コードはここで公開しています。 cloneすれば、この記事の「実行例」にある手順からすぐに試せると思います。
構成
- 言語: Python
- FastMCP
- mcp >= 1.9.2
- FastMCP
- MCP
- type: stdio
インストール
-
mcp
パッケージを導入しますpython -m pip install -r requirements.txt --user
-
requirements.txt
mcp >= 1.9.2
-
MCPサーバの作成
今回はこんな感じのコードです。Copilotで生成したコードです。
stdio
なのでJSON-RPCの形式で要求を送ると、結果をJSONで返してくれます。
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("simple-mcp-server")
@mcp.tool(name="add", description="Add two numbers")
async def add(a: int, b: int) -> int:
return a + b
@mcp.tool(name="subtract", description="Subtract two numbers")
async def subtract(a: int, b: int) -> int:
return a - b
if __name__ == "__main__":
mcp.run(transport="stdio")
見た目、APIサーバみたいなコードで、実際そうです。通常のAPIサーバと異なるのは、 description
の部分が重要な意味をもつことですね。Agentモードで言語モデルが description の内容をもとにどのツールのどのメソッドが問題解決に役立つか、ここの文章で判断します。
GitHub Copilot側の設定
.vscode/mcp.json
に設定を入れると VS-Code から読み取ってくれます。
${workspaceFolder}
の変数を使うことで、絶対パスをコードに埋め込まないで済みます。
{
"inputs": [],
"servers": {
"simple-mcp-server": {
"command": "python",
"type": "stdio",
"args": ["${workspaceFolder}/src/server.py"],
"env": {}
}
}
}
実行例
簡単でしょ?
おまけ: 単体テスト
単体テストをやりたくなって、知識がない状態から試行錯誤で試しました。
最初は、こんな風にできるのかな?と思ったらできなかった。
echo '{"method": "add", "params": {"a": 2, "b": 4}, "id": 1}' | python src/server.py
まず、JSON-RPCの作法で入力を流し込む必要がある。そして、まず初期化のリクエストが完了している必要がある。
それに、同期して応答が返る訳ではないようで、リクエストを送って、標準入力に何もないので echo や cat で入力を流し込んでも、テストプログラムは応答を受け取る前に終了してしまう。
……というわけで、 最終的には expect
を使って動かしたコードがこんな感じです。
多分、こんなマニアックなことをせずにテストできるツールはあるでしょうけど、どういう仕組みかが気になって仕方なかったので、これで満足です。
#!/usr/bin/expect -f
set timeout 10
set t0 [clock seconds]
spawn python src/server.py
send -- "{\"jsonrpc\": \"2.0\", \"id\": 0, \"method\": \"initialize\", \"params\": {\"protocolVersion\": \"2024-11-05\", \"capabilities\": {}, \"clientInfo\": {\"name\": \"whatever\", \"version\": \"0.0.0\"}}}\r"
expect \"result\"
send -- "{\"jsonrpc\":\"2.0\",\"method\":\"notifications/initialized\",\"params\":{}}\r"
set t1 [clock seconds]
set elapsed_time [expr {$t1 - $t0}]
puts "Elapsed time for Initialize: $elapsed_time seconds"
send -- "{\"jsonrpc\": \"2.0\", \"id\": 0, \"method\": \"tools/list\", \"params\":{}}\r"
expect \"result\"
send -- "{\"jsonrpc\": \"2.0\", \"id\": 0, \"method\": \"tools/call\", \"params\":{\"name\": \"add\", \"arguments\": {\"a\": 1, \"b\": 3}}}\r"
expect \"result\"
set t2 [clock seconds]
set elapsed_time_initialize [expr {$t2 - $t1}]
puts "Elapsed time for call: $elapsed_time seconds"
exit