はじめに
Claude CodeのようなCLI型AIコーディングツールが普及し、自作ツールをAIに安全に渡したいニーズが高まっています。その標準規格がMCP (Model Context Protocol) です。本記事では、Pythonで書いた最小のMCPサーバーをClaude Codeに登録し、ツールを実際に呼び出せるところまでを確認します。
Claude Desktopへの接続手順は、前回の記事で解説しました。
Claude Codeの登録方法は、設定ファイルを手で編集する方式ではなく、登録はclaude mcp addコマンド1つで済む点が大きく異なります。本記事はその手順と、つまづきやすいポイントの整理です。
前提
- Python 3.10以上
- Python SDKは
pip install "mcp[cli]"で導入します - Claude Code CLIがインストール済み
ディレクトリ構成は次のとおりです。
mcp-echo/
src/server.py (サーバー本体)
.mcp.json (共有設定、後述)
ステップ1: 最小のMCPサーバーを書く
動作確認用として、行リストを受け取って整形レポートを返すツールを1つだけ持つサーバーを作ります。ファイル名はsrc/server.pyとします。
#!/usr/bin/env python3
"""Claude Code接続確認用の最小MCPサーバー"""
import sys
import asyncio
import logging
from mcp.server import Server
from mcp.server.stdio import stdio_server
import mcp.types as types
# ログは必ずstderrへ出すこと
# stdoutはMCPの通信路なので汚さない
logging.basicConfig(
level=logging.INFO,
stream=sys.stderr,
format="%(asctime)s - %(levelname)s - %(message)s",
)
server = Server("echo-utilities")
@server.list_tools()
async def list_tools() -> list[types.Tool]:
return [
types.Tool(
name="format_report",
description="タイトルと行リストから整形レポートを生成する",
inputSchema={
"type": "object",
"properties": {
"title": {"type": "string", "description": "レポートの見出し"},
"rows": {"type": "array", "items": {"type": "string"}, "description": "本文行"},
},
"required": ["title", "rows"],
},
),
]
ツール定義で大切なのは2点です。
- nameとdescriptionはLLMがツール選択に使う — 曖昧な説明だと、使うべき場面でClaudeがそのツールを選んでくれません
- inputSchemaはJSON Schemaとして正確に — ここが正確なほど、引数の渡し間違いが減ります
call_toolハンドラとエントリポイントを追加します。
@server.call_tool()
async def call_tool(name: str, arguments: dict):
if name == "format_report":
title = arguments["title"]
rows = arguments["rows"]
lines = ["## " + title, ""]
lines += ["- " + r for r in rows]
text = "\n".join(lines) + "\n"
return [types.TextContent(type="text", text=text)]
raise ValueError(f"Unknown tool: {name}")
async def main():
async with stdio_server() as (read_stream, write_stream):
await server.run(read_stream, write_stream)
if __name__ == "__main__":
asyncio.run(main())
MCPのstdioトランスポートは標準出力をJSON-RPCの通信路として使います。ログをstdoutに出すと通信が壊れるため、ログ出力は必ずstderrへ向けてください。
MCP Inspectorで動作確認
クライアントに渡す前に、まずMCP Inspectorで単体動作を確認できます。
mcp dev src/server.py
ブラウザでInspectorが起動するので、そのtoolsタブからformat_reportを実行してみてください。期待どおりのレポートが返れば、サーバー側の準備は完了です。
ステップ2: claude mcp add で登録する
登録コマンドは1行です。セパレータの--の後にサーバー起動コマンドを並べます。
cd mcp-echo
claude mcp add echo-utils -- python src/server.py
スコープは3種類あります。用途で使い分けます。
| スコープ | コマンド例 | 用途 |
|---|---|---|
| local | claude mcp add echo-utils -- python src/server.py | 自分専用、このプロジェクト限定 |
| project | claude mcp add --scope project echo-utils -- python src/server.py | .mcp.jsonに書き込まれ、チーム共有 |
| user | claude mcp add --scope user echo-utils -- python src/server.py | マシン内の全プロジェクトで使用 |
登録結果は次のコマンドで確認します。
claude mcp list
一覧にecho-utilsが現れ、接続ステータスが正常なら成功です。セッション内では/mcpコマンドで、サーバーとツールの状態をいつでも確認できます。
ステップ3: .mcp.json でチーム共有する
projectスコープで登録すると、設定がプロジェクトルートの .mcp.json に書き込まれます。このファイルをGitにコミットすれば、チームの全員が同じMCPサーバーを使えます。
{
"mcpServers": {
"echo-utils": {
"command": "python",
"args": ["src/server.py"],
"env": {}
}
}
}
形式はClaude Desktopの設定ファイルとほぼ同じです。初回読み込み時には「このプロジェクトのMCPサーバーを使用しますか」と承認を求められます。信頼できないリポジトリが勝手にサーバーを仕込むのを防ぐセーフガードなので、内容を確認してから承認してください。
ステップ4: セッションで実際に呼び出す
プロジェクトルートで claude を起動し、次のように依頼します。
src/sample_report.mdにレポートを保存してください。見出しは「週次進捗」、本文は3行です
初回呼び出し時には、パーミッション確認が出ます。「このセッションで許可」を選べば、以降は確認なしで動きます。ツールが呼ばれると標準入出力でMCPメッセージが往復し、整形されたレポートをファイルに書き込んでくれます。
つまづきポイントまとめ
最後に、私が実際に引っかかった点を整理します。
- stdoutを汚す: ログ出力にprintを使うと、サーバーが応答しなくなります。ログはloggingでstderrへ出します
- スコープの取り違え: localとprojectを混同すると、チームメンバーの環境だけサーバーが見えない事故になります。共有したいものは必ずprojectスコープで登録します
- Windows環境のcommand: npxベースのサーバーではcmd /cのラッパーが必要なケースがあります。ただしPython製サーバーなら基本的に問題ありません
- 承認プロンプトの見落とし: .mcp.jsonを読み込んだ直後は承認を求められます。承認するまでサーバーは無効のままです
どれも理解すれば単純ですが、初回は時間を溶かしがちです。この記事の手順に沿えば、登録から動作確認まで10分ほどで終わるはずです。