
最近、MCP(Model Context Protocol)が話題になっていますね!どうやら、MCPを使うと、PythonスクリプトをAIの「手」として直接呼び出させることができるようです!
そこで今回は、Python環境でMCPのサーバー自作からClaude Desktop連携までを実際に行ってみました!FastMCP 3.2.4(2026年最新)を使えば10行以下で動くサーバーが完成しますので、ぜひ皆さんも記事を読んで試してみてください!
この記事で分かること
- MCPとは何か、従来のAI連携と何が違うのか
- FastMCP 3.2.4を使ったPython MCPサーバーの最小構成と実装方法
- Claude Desktopとの接続設定・よくあるエラーの解決法
MCPとは何か:30秒で理解する概念
ChatGPTやClaudeに「このフォルダのファイルを検索して」と頼んでも、AIはローカル環境にアクセスできない。MCPを使えばこの問題が解決できます。
MCP(Model Context Protocol)でできること
- ローカルファイルやスクリプトをAIから直接呼び出す
- Gitリポジトリ・データベース・外部APIをClaudeの「手」として接続する
- 自分のPythonツールをAIのプラグインとして公開する
ここで、「今まであったChatGPT Pluginsや独自APIと何が違うの?」という疑問が出てくると思います。
大きな差としては、「クライアント・モデル・ツールが完全に分離された標準プロトコル」が可能ということです。これにより、一度作ったMCPサーバーをClaude DesktopでもCursorでもChatGPT Desktopでも無改造で使うことが出来ます。
今話題の、「AI Agentのツール呼び出し標準化」というやつですね!
MCPの登場前後を比較すると、以下のようになります。
| 項目 | MCP登場前 | MCP登場後 |
|---|---|---|
| ツール連携方法 | 各AI向けに個別実装 | MCPサーバー1本でどのクライアントも対応 |
| スキーマ定義 | JSON Schemaを手書き | Pythonの型ヒントから自動生成 |
| 認証・トランスポート | 独自実装が必要 | stdio/HTTP/OAuth をFastMCPが肩代わり |
「MCPって難しそう…」と感じるかもしれませんが、この記事を読めばPython中級者の方でも大丈夫!順を追ってClaude Desktop連携・実用ツール実装まで解説します。
環境構築:5分でMCPサーバーを動かす
手順1:必要な環境を確認する(Python 3.10以上)
FastMCP 3.2.4はPython 3.10以上が必要です。バージョンを確認してから進めましょう。
# バージョン確認(3.10.x 以上であればOK)
python --version
手順2:FastMCPをインストールする
インストール方法は2通りあります。プロジェクト管理にuvを使っている場合は方法Aがおすすめです。この後の説明も、方法Aを元に行います。
方法A:uvを使ったインストール(おすすめ)
uvは2025年以降にPythonコミュニティで急速に普及した高速パッケージマネージャーです。依存関係の管理とvirtualenv作成を自動で行います。
# ファイル名: setup.sh
# uvがなければ先にインストール
pip install uv
# プロジェクト作成 & FastMCPインストール
uv init my-mcp-server
cd my-mcp-server
uv add fastmcp
方法B:pipを使ったインストール(お試し向け)
既存の仮想環境やグローバル環境にそのままインストールしたい場合はこちら。
# ファイル名: install.sh
pip install fastmcp
手順3:最小構成のMCPサーバーを起動する
以下が「動くMCPサーバー」の最小コードです。10行以内で完結します。
# ファイル名: server.py
# FastMCP 3.2.4 / Python 3.12 で動作確認済み
from fastmcp import FastMCP
mcp = FastMCP("my-first-mcp")
@mcp.tool()
def add(a: int, b: int) -> int:
"""2つの整数を足して返す。"""
return a + b
if __name__ == "__main__":
mcp.run() # stdio モードで起動
起動して動作確認します。サーバー起動後、MCPインスペクター(Anthropic公式のデバッグUI)でツールの呼び出しが確認できます。
# ファイル名: run.sh
# サーバー起動(stdioモードで待機状態になる)
uv run server.py
# 別ターミナルで: MCP Inspector でUIデバッグ(npx が必要)
npx @modelcontextprotocol/inspector uv run server.py
ブラウザで http://127.0.0.1:6274 が開き、add ツールが一覧に表示されれば起動成功です。a=21, b=21 を入力して「Run」を押すと 42 が返ってきます。
ツールを定義する:@mcp.tool()デコレータの使い方
@mcp.tool() を関数に付けるだけで、その関数がAIへの「命令窓口」になります。Pythonの型ヒントが自動でJSON Schemaに変換され、Claudeは「この関数にどんな引数を渡せばいいか」を自動で理解します。docstringがAIへの説明文になるため、docstringは丁寧に書くのが鉄則です。
サンプル①:ローカルファイル一覧を返すツール
# ファイル名: tools/list_files.py
# 指定ディレクトリのファイル一覧を返すMCPツール
import os
from fastmcp import FastMCP
mcp = FastMCP("file-tools")
@mcp.tool()
def list_files(directory: str, extension: str = "") -> list[str]:
"""指定したディレクトリのファイル一覧を返す。
Args:
directory: 対象ディレクトリの絶対パス
extension: 絞り込む拡張子(例: ".py")。空文字なら全ファイル
"""
try:
files = os.listdir(directory)
if extension:
files = [f for f in files if f.endswith(extension)]
return sorted(files)
except FileNotFoundError:
raise ValueError(f"ディレクトリが見つかりません: {directory}")
if __name__ == "__main__":
mcp.run()
サンプル②:Pythonスクリプトを実行して結果を返すツール
# ファイル名: tools/run_script.py
# 指定したPythonファイルを実行し、標準出力を返すMCPツール
import subprocess
from fastmcp import FastMCP
mcp = FastMCP("script-runner")
@mcp.tool()
def run_python_script(filepath: str, timeout: int = 30) -> dict:
"""指定したPythonスクリプトを実行し、stdoutとstderrを返す。
Args:
filepath: 実行するPythonファイルの絶対パス
timeout: タイムアウト秒数(デフォルト30秒)
"""
try:
result = subprocess.run(
["python", filepath],
capture_output=True, # stdoutとstderrを同時にキャプチャ
text=True,
timeout=timeout
)
return {
"stdout": result.stdout,
"stderr": result.stderr,
"returncode": result.returncode
}
except subprocess.TimeoutExpired:
raise ValueError(f"タイムアウト({timeout}秒)を超過しました")
except FileNotFoundError:
raise ValueError(f"ファイルが見つかりません: {filepath}")
if __name__ == "__main__":
mcp.run()
⚠️ subprocess.run()を使う際は必ずcapture_output=Trueを指定してください。stdioモードのMCPサーバーはstdoutをJSON-RPCのトランスポートとして使っているため、子プロセスのstdoutが混入するとサーバーがハングします。
Claude Desktopと接続する
作成したMCPサーバーをClaude Desktopに認識させるには、設定ファイル claude_desktop_config.json にエントリを追加します。
{
"mcpServers": {
"my-mcp-server": {
"command": "uv",
"args": ["--directory", "/Users/yourname/my-mcp-server", "run", "server.py"]
}
}
}
設定ファイルの場所はOSによって異なります。
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
Windowsの場合、パスに日本語フォルダ名が含まれると文字化けすることがあるため、英数字のみのパスを推奨します。
{
"mcpServers": {
"my-mcp-server": {
"command": "python",
"args": ["C:\\Users\\YourName\\my-mcp-server\\server.py"]
}
}
}
接続確認の手順は以下のとおりです。
- 設定ファイルを保存後、Claude Desktopを完全に終了して再起動する
- メッセージ入力欄の左下に「🔨 ハンマーアイコン」が表示されることを確認する
- ハンマーアイコンをクリックすると、登録済みのツール一覧(例:
list_files、run_python_script)が表示される - Claudeのチャット欄に「/Usersフォルダのファイルを一覧表示して」と入力し、ツールが自動呼び出しされることを確認する
ハンマーアイコンが表示されない場合、JSON構文エラーか、サーバー起動クラッシュの可能性が高いです。次のセクション「よくあるエラーと解決法」を参照してください。
実用的な応用例3選
応用例①:ローカルファイル検索ツール
キーワードを指定してローカルのMarkdownやテキストファイルを全文検索します。Claudeに「プロジェクトXの仕様メモを探して」と依頼するだけで実行されます。
# ファイル名: tools/search_files.py
# ディレクトリを再帰的に検索し、マッチした行を返すMCPツール
from pathlib import Path
from fastmcp import FastMCP
mcp = FastMCP("search-tools")
@mcp.tool()
def search_files(query: str, directory: str, extension: str = ".md", limit: int = 10) -> list[dict]:
"""ディレクトリを再帰的に検索し、クエリにマッチした行を返す。
Args:
query: 検索キーワード(大文字小文字を区別しない)
directory: 検索対象ディレクトリの絶対パス
extension: 対象ファイルの拡張子(デフォルト: .md)
limit: 返す最大件数(デフォルト: 10)
"""
results = []
base = Path(directory)
for path in base.rglob(f"*{extension}"):
try:
text = path.read_text(encoding="utf-8")
except (UnicodeDecodeError, PermissionError):
continue
for i, line in enumerate(text.splitlines(), start=1):
if query.lower() in line.lower():
results.append({
"file": str(path.relative_to(base)),
"line": i,
"snippet": line.strip()[:200]
})
break
if len(results) >= limit:
break
return results
if __name__ == "__main__":
mcp.run()
応用例②:Git logを要約させるツール
指定リポジトリの直近コミット履歴を取得してClaudeに渡します。「先週のバックエンドの変更内容をまとめて」といった用途に使えます。
# ファイル名: tools/git_log.py
# git logを取得してAIが要約できる形式で返すMCPツール
import subprocess
from fastmcp import FastMCP
mcp = FastMCP("git-tools")
@mcp.tool()
def get_git_log(repo_path: str, count: int = 20) -> str:
"""指定リポジトリの直近のgit logを取得して返す。
Args:
repo_path: Gitリポジトリのルートディレクトリパス
count: 取得するコミット数(デフォルト: 20)
"""
result = subprocess.run(
["git", "-C", repo_path, "log", "--oneline", f"-n{count}"],
capture_output=True,
text=True,
check=True
)
if result.returncode != 0:
raise ValueError(f"git log取得失敗: {result.stderr}")
return result.stdout
if __name__ == "__main__":
mcp.run()
応用例③:任意のPythonスクリプトをAIから実行させるツール
スクリプトのパスを渡すと実行して結果を返す汎用ツールです。データ処理スクリプトやテスト実行を自然言語で指示できるようになります。セキュリティのため、ALLOWED_DIRで実行可能なパスを制限しています。
# ファイル名: tools/secure_runner.py
# 許可ディレクトリ内のスクリプトのみ実行できるセキュアなMCPツール
import subprocess
from pathlib import Path
from fastmcp import FastMCP
mcp = FastMCP("secure-runner")
# セキュリティ:実行を許可するディレクトリを明示的に制限する
ALLOWED_DIR = Path.home() / "scripts"
@mcp.tool()
def run_allowed_script(script_name: str, args: list[str] = []) -> dict:
"""~/scripts/ ディレクトリ内のPythonスクリプトを安全に実行する。
Args:
script_name: スクリプトファイル名(例: analyze.py)
args: スクリプトに渡す引数のリスト
"""
script_path = ALLOWED_DIR / script_name
# パストラバーサル攻撃対策:許可ディレクトリ外へのアクセスをブロック
if not script_path.resolve().is_relative_to(ALLOWED_DIR.resolve()):
raise ValueError("許可されていないパスへのアクセスです")
if not script_path.exists():
raise ValueError(f"スクリプトが見つかりません: {script_name}")
result = subprocess.run(
["python", str(script_path)] + args,
capture_output=True,
text=True,
timeout=60
)
return {
"stdout": result.stdout,
"stderr": result.stderr,
"returncode": result.returncode
}
if __name__ == "__main__":
mcp.run()
よくあるエラーと解決法
MCPサーバーのトラブルシューティングで最も重要なのはログの場所を把握することです。Claude Desktopのログは以下のパスに出力されます。
# macOS: ログをリアルタイムで確認
tail -f ~/Library/Logs/Claude/mcp*.log
# Windows (PowerShell)
Get-Content "$env:APPDATA\Claude\Logs\mcp*.log" -Wait
エラー①:ModuleNotFoundError: No module named 'fastmcp'
Claude Desktopがサーバーを起動する際、systemのPythonではなくvenv内のPythonを使っているかどうかを疑います。
# 原因確認:どのPythonが実行されているか確認
which python
pip show fastmcp # インストール済みか確認
# 解決策A:uvを使って確実にプロジェクト環境で実行
uv run server.py
# 解決策B:Pythonの絶対パスをconfig.jsonに指定する
# (which python の出力パスを使う)
{
"mcpServers": {
"my-mcp-server": {
"command": "/Users/yourname/.venv/bin/python",
"args": ["/Users/yourname/my-mcp-server/server.py"]
}
}
}
エラー②:claude_desktop_config.jsonが反映されない
JSONの構文エラーが最も多い原因です。カンマ抜け・末尾カンマはJSONとして無効なため、Claude Desktopが設定ファイルを丸ごと無視します。
# JSONの構文検証(Pythonで手軽にチェック)
python -m json.tool ~/Library/Application\ Support/Claude/claude_desktop_config.json
問題がなければ設定内容がそのまま出力されます。JSONDecodeErrorが出た場合はその行番号を修正してください。修正後はClaude Desktopを完全終了(メニューバーからも終了)して再起動してください。タスクバーに残った状態での再起動では設定が読み直されません。
エラー③:ツールが一覧に表示されない・呼び出されない
ハンマーアイコンが表示されているのにツールが実行されないケースの大半は、サーバーの起動クラッシュまたはツール名とdocstringの情報不足です。
# デバッグ:MCPサーバーを単独で手動起動してエラーを確認
uv run server.py
# もしくは
python server.py
# → ImportErrorやSyntaxErrorがターミナルに表示される
# FastMCP CLIでサーバーの状態を検査する
fastmcp inspect server.py
fastmcp inspect server.pyを実行すると、登録されているツール・リソース・プロンプトの一覧と各ツールのスキーマが表示されます。ここで意図したツールが表示されていない場合は、デコレータの書き方かインポートのパスを再確認してください。
# 期待される出力例
# Server
# Name: my-first-mcp
# Version: 1.0.0
#
# Components
# Tools: 3
# Resources: 0
# Prompts: 0
まとめ:MCPサーバー自作で広がるAI連携
今回は、FastMCP 3.2.4を使ったPython MCPサーバーの自作からClaude Desktop連携までに挑戦してみました。
独自APIやLangChainツールも便利ですが、「MCP」はさらに便利で、一度作ればClaude・Cursor・ChatGPT Desktopどこでも動くため、これは使わないのはもったいない!と感じました。
導入もpip install fastmcpの1行で済みますので、みなさんも今回の記事を参考に、ぜひ「MCP」を活用してみてください!
この記事で学んだこと
- MCPはAIとツールをつなぐ標準プロトコルで、一度実装すれば複数のAIクライアントで再利用できる
- FastMCP 3.2.4では
@mcp.tool()デコレータを関数に付けるだけでAIが呼び出せるツールになる claude_desktop_config.jsonにコマンドパスを記述してClaude Desktopを再起動するだけで接続が完了する
【関連記事リンク:「プロンプトインジェクション対策」】
作ったMCPサーバーをセキュアに運用するには?外部入力を経由した悪意あるプロンプトでローカルスクリプトが乗っ取られるリスクがあります。次の記事では、MCPサーバーに対するプロンプトインジェクション攻撃の手口と対策を解説します。