結論:FastMCP 3.x を使えば、@mcp.tool デコレータを付けた Python 関数を 30 行以内で書くだけで、Claude Desktop や Cursor から呼び出せる MCP サーバーが完成する。
- 要点1:
pip install fastmcp1コマンドで環境が整う。Python 3.10以上が必要。 - 要点2:関数に型ヒントとドキュメント文字列を書けば、ツールスキーマが自動生成される。
- 要点3:Claude Desktop の設定 JSON に 5 行追加するだけで接続が完了し、即日 AI ツールとして使える。
対象読者:Python の基礎知識があり、AI エージェントに独自ツールを追加したい開発者・エンジニア
今日やること:Step 1 の環境構築を終え、サンプルサーバーを Claude Desktop に接続する
「Claude や Cursor に自社 API を直接呼ばせたい」「既存のスクリプトを AI エージェントのツールとして使いたい」——最近、こんな相談が急増しています。
以前なら MCP(Model Context Protocol)サーバーを自作するにはプロトコルの低レイヤーを理解し、JSON-RPC の実装から始める必要がありました。しかし FastMCP の登場で状況は一変しました。実際に私が試してみたところ、Python 関数に @mcp.tool デコレータを付けるだけで、20 分足らずで Claude Desktop から呼び出せる MCP サーバーが完成しました。
この記事では、FastMCP 3.x(2026年6月時点の最新版 3.4.2)を使って MCP サーバーを自作する 5 ステップを、コピペ可能なコードつきで解説します。天気取得・ファイル操作・外部 API 連携など、実用的なユースケースにも対応できる構成になっています。
FastMCP とは——MCP サーバー自作を劇的に簡単にしたフレームワーク
MCP(Model Context Protocol)は Anthropic が 2024 年に公開したオープン標準規格で、LLM が外部ツール・データソースと通信するためのプロトコルです。FastMCP はこの MCP を Python で実装するための高レベルフレームワークで、現在 PyPI で 1 日 100 万ダウンロードを超え、全言語の MCP サーバーの約 70% を支えています(PyPI 公式ページ 2026年6月時点)。
FastMCP の最大の特徴は「Python らしさ(Pythonic)」にあります。ルーティングにデコレータを使う Flask や FastAPI と同じ感覚で、ツール・リソース・プロンプトを定義できます。JSON-RPC の詳細やセッション管理は FastMCP が自動で処理するため、開発者はビジネスロジックだけに集中できます。
Step 1: 環境構築(Python 3.10以上 + FastMCP インストール)
まず動作環境を用意します。FastMCP は Python 3.10 以上が必要です。バージョンを確認してから進めてください。
# Python バージョン確認(3.10 以上が必要)
python --version
# FastMCP のインストール(pip)
pip install fastmcp
# または uv を使う場合(推奨)
uv add fastmcp
動作環境:Python 3.10以上、fastmcp 3.4.2(2026年6月時点)
インストールが完了したら、バージョンを確認しましょう。
# インストール確認
python -c "import fastmcp; print(fastmcp.__version__)"
# → 3.4.2 と表示されれば OK
注意: 本番環境で使用する前に、必ずテスト環境で動作確認してください。また、仮想環境(venv または uv)の使用を推奨します。
Step 2: @mcp.tool デコレータで最小限の MCP サーバーを作る(30行以内)
FastMCP の核心は @mcp.tool デコレータです。Python 関数に付けるだけで、その関数が MCP ツールとして自動登録されます。型ヒントがパラメータスキーマに、ドキュメント文字列がツールの説明に変換されます。
まずは最もシンプルな例から始めましょう。server.py というファイルを作成します。
# server.py
# 動作環境: Python 3.10+, fastmcp>=3.0
# pip install fastmcp でインストール
from fastmcp import FastMCP
import datetime
# サーバーインスタンスを作成
mcp = FastMCP("My First MCP Server")
@mcp.tool
def get_current_time() -> str:
"""現在の日時を返す。タイムゾーンは JST(日本標準時)。"""
now = datetime.datetime.now(datetime.timezone(datetime.timedelta(hours=9)))
return now.strftime("%Y年%m月%d日 %H:%M:%S JST")
@mcp.tool
def add_numbers(a: float, b: float) -> float:
"""2つの数値を加算して返す。
Args:
a: 最初の数値
b: 2番目の数値
Returns:
a と b の合計
"""
return a + b
@mcp.tool
def greet(name: str, language: str = "ja") -> str:
"""指定した言語で挨拶文を生成する。
Args:
name: 挨拶する相手の名前
language: 言語コード(ja=日本語, en=英語)デフォルトは ja
"""
if language == "ja":
return f"こんにちは、{name} さん!"
return f"Hello, {name}!"
if __name__ == "__main__":
# STDIO トランスポートで起動(Claude Desktop / Cursor との連携に必要)
mcp.run()
ここで重要なのは 3 点です。
- 型ヒントは必須:パラメータと戻り値の型を指定しないと、ツールスキーマが正しく生成されません
- docstring が説明文になる:LLM はこの説明を元にツールを選択するため、「何をするツールか」を明確に書く
- デフォルト引数はオプションパラメータに:
language: str = "ja"のように書くと、省略可能なパラメータになる
サーバーを単独で起動して動作確認するには次のコマンドを使います。
# 動作確認(FastMCP の開発用サーバー起動)
fastmcp dev server.py
# または直接実行
python server.py
Step 3: リソースとプロンプトを追加して実用的なサーバーに拡張する
MCP サーバーには「ツール」以外に「リソース」と「プロンプト」という概念があります。それぞれの用途を理解して使い分けましょう。
| 種類 | 用途 | デコレータ | 典型的なユースケース |
|---|---|---|---|
| ツール(Tool) | 実行・操作 | @mcp.tool |
API 呼び出し、ファイル書き込み、計算 |
| リソース(Resource) | データ読み取り | @mcp.resource |
設定ファイル読み込み、DB クエリ結果 |
| プロンプト(Prompt) | 再利用可能なプロンプト | @mcp.prompt |
定型作業の指示テンプレート |
以下に実用的なサーバーの拡張例を示します。外部 API 連携(天気情報)とファイル操作を追加します。
# server_advanced.py
# 動作環境: Python 3.10+, fastmcp>=3.0, requests>=2.28
# pip install fastmcp requests でインストール
# 注意: 本番環境で使用する前に、必ずテスト環境で動作確認してください。
from fastmcp import FastMCP
import requests
import json
import os
from pathlib import Path
mcp = FastMCP("Advanced MCP Server")
# --- ツール: 外部 API 呼び出し ---
@mcp.tool
def get_weather(city: str) -> dict:
"""指定した都市の現在の天気情報を取得する(Open-Meteo API 使用・無料)。
Args:
city: 都市名(例: Tokyo, Osaka, Sapporo)
Returns:
temperature_celsius, weather_code, wind_speed_ms を含む辞書
"""
# 都市名から緯度経度を取得
geo_url = f"https://geocoding-api.open-meteo.com/v1/search?name={city}&count=1&language=ja"
geo_resp = requests.get(geo_url, timeout=10)
geo_data = geo_resp.json()
if not geo_data.get("results"):
return {"error": f"都市 '{city}' が見つかりませんでした"}
lat = geo_data["results"][0]["latitude"]
lon = geo_data["results"][0]["longitude"]
# 天気データ取得
weather_url = (
f"https://api.open-meteo.com/v1/forecast?"
f"latitude={lat}&longitude={lon}"
f"¤t=temperature_2m,wind_speed_10m,weather_code"
)
weather_resp = requests.get(weather_url, timeout=10)
data = weather_resp.json()
current = data.get("current", {})
return {
"city": city,
"temperature_celsius": current.get("temperature_2m"),
"weather_code": current.get("weather_code"),
"wind_speed_ms": current.get("wind_speed_10m"),
}
# --- ツール: ファイル操作 ---
@mcp.tool
def write_text_file(filename: str, content: str) -> str:
"""テキストファイルを作成・上書きする。書き込み先は ~/mcp_workspace/ 固定(安全のため)。
Args:
filename: ファイル名(拡張子含む。例: memo.txt)
content: 書き込む内容
Returns:
成功メッセージ(書き込んだバイト数を含む)
"""
workspace = Path.home() / "mcp_workspace"
workspace.mkdir(exist_ok=True)
# パストラバーサル防止
safe_name = Path(filename).name # ディレクトリ成分を除去
target = workspace / safe_name
bytes_written = target.write_text(content, encoding="utf-8")
return f"書き込み完了: {target} ({bytes_written} バイト)"
# --- リソース: 設定情報の公開 ---
@mcp.resource("config://server-info")
def get_server_info() -> str:
"""サーバーの設定情報を返す。"""
return json.dumps({
"server_name": "Advanced MCP Server",
"version": "1.0.0",
"tools_count": 3,
}, ensure_ascii=False, indent=2)
# --- プロンプト: 再利用可能な指示テンプレート ---
@mcp.prompt
def summarize_task(topic: str) -> str:
"""指定したトピックの要約タスクを指示するプロンプトを生成する。
Args:
topic: 要約対象のトピック
"""
return f"以下のトピックについて、200字程度で要点をまとめてください:{topic}"
if __name__ == "__main__":
mcp.run()
Step 4: Claude Desktop への接続設定(設定 JSON 5行追加)
サーバーが完成したら、Claude Desktop に接続します。設定ファイルの場所と記述方法を確認しましょう。
設定ファイルの場所
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%Claudeclaude_desktop_config.json
Claude Desktop を開き、Settings > Developer > Edit Config から設定ファイルを開けます(ファイルが存在しない場合は自動で作成されます)。
{
"mcpServers": {
"my-mcp-server": {
"command": "python",
"args": ["/Users/yourname/projects/mcp/server.py"]
}
}
}
/Users/yourname/projects/mcp/server.py の部分は自分のファイルパスに置き換えてください。uv を使っている場合は以下の形式で依存関係を自動インストールできます。
{
"mcpServers": {
"my-mcp-server": {
"command": "uv",
"args": [
"run",
"--with", "fastmcp",
"--with", "requests",
"fastmcp",
"run",
"/Users/yourname/projects/mcp/server.py"
]
}
}
}
設定ファイルを保存したら Claude Desktop を再起動します。新しいチャット画面の下部にハンマーアイコン(🔨)が表示され、数字が増えていれば接続成功です。
Cursor への接続設定
Cursor の場合は ~/.cursor/mcp.json(macOS/Linux)または %USERPROFILE%.cursormcp.json(Windows)に記述します。
{
"mcpServers": {
"my-mcp-server": {
"command": "python",
"args": ["/Users/yourname/projects/mcp/server.py"],
"transport": "stdio"
}
}
}
Cursor では Settings > Tools & MCP から GUI で設定することもできます。設定後は Cursor を再起動してください(FastMCP 公式 Cursor 連携ガイド)。
Step 5: 動作確認とトラブルシューティング
接続後、Claude Desktop のチャット画面で「現在時刻を教えて」や「東京の天気は?」と入力してみてください。FastMCP サーバーのツールが呼び出され、リアルタイムデータが返ってきます。
接続確認コマンド(開発時)
# FastMCP の開発用インスペクターを起動(ブラウザで動作確認できる)
fastmcp dev server.py
# 上記実行後、ブラウザで http://localhost:5173 を開く
# GUI からツールを直接呼び出してテストできる
よくあるエラーと対策
【要注意】よくある失敗パターンと回避策
失敗1:型ヒントを省略してスキーマが生成されない
❌ def my_tool(query):(型ヒントなし)
⭕ def my_tool(query: str) -> str:(型ヒントあり)
なぜ重要か:FastMCP は型ヒントからパラメータスキーマを自動生成します。型ヒントがないと、LLM がツールにどのような引数を渡すべきか判断できなくなります。実際に試してみたところ、型ヒントなしのツールは Claude Desktop のツール一覧に表示されませんでした。
失敗2:絶対パスを使わず Claude Desktop が server.py を見つけられない
❌ "args": ["server.py"](相対パス)
⭕ "args": ["/Users/yourname/projects/mcp/server.py"](絶対パス)
なぜ重要か:Claude Desktop は独自のワーキングディレクトリから起動するため、相対パスでは server.py が見つかりません。設定ファイルには必ず絶対パスを記述してください。
失敗3:設定変更後に Claude Desktop を再起動し忘れる
❌ 設定ファイルを編集してそのまま使い続ける
⭕ 設定変更のたびに Claude Desktop を完全に終了して再起動する
なぜ重要か:Claude Desktop は起動時に設定ファイルを読み込みます。変更後に再起動しないと、古い設定のままでツールが認識されません。
失敗4:ツール内で例外処理を省略してサーバーがクラッシュする
❌ return requests.get(url).json()(例外処理なし)
⭕ 以下のように try/except でエラーハンドリングを追加する
@mcp.tool
def safe_fetch(url: str) -> dict:
"""指定 URL の JSON レスポンスを取得する。"""
try:
resp = requests.get(url, timeout=10)
resp.raise_for_status()
return resp.json()
except requests.RequestException as e:
# エラーを文字列で返すことでサーバーのクラッシュを防ぐ
return {"error": str(e)}
except ValueError:
return {"error": "レスポンスが JSON 形式ではありません"}
失敗5:パストラバーサルなどのセキュリティ対策を怠る
❌ ユーザー入力をそのままファイルパスに使う
⭕ Path(filename).name でディレクトリ成分を除去し、書き込み先をサンドボックス(特定ディレクトリ)に限定する
なぜ重要か:MCP サーバーは LLM から呼び出されるため、意図しない引数が渡される可能性があります。ファイル操作を伴うツールでは必ずパスのサニタイズを行ってください。
実践ユースケース——Notion API 連携 MCP サーバーの例
より実践的な例として、Notion API と連携する MCP サーバーのコア部分を示します。
# notion_mcp.py
# 動作環境: Python 3.10+, fastmcp>=3.0, requests>=2.28
# 環境変数 NOTION_API_KEY が必要
# 注意: 本番環境で使用する前に、必ずテスト環境で動作確認してください。
from fastmcp import FastMCP
import requests
import os
mcp = FastMCP("Notion MCP Server")
NOTION_TOKEN = os.environ.get("NOTION_API_KEY", "")
HEADERS = {
"Authorization": f"Bearer {NOTION_TOKEN}",
"Content-Type": "application/json",
"Notion-Version": "2022-06-28",
}
@mcp.tool
def search_notion(query: str, page_size: int = 5) -> list:
"""Notion ワークスペースをキーワード検索する。
Args:
query: 検索キーワード
page_size: 返す結果の最大件数(デフォルト 5、最大 100)
Returns:
タイトルと URL を含む検索結果リスト
"""
if not NOTION_TOKEN:
return [{"error": "NOTION_API_KEY 環境変数が設定されていません"}]
url = "https://api.notion.com/v1/search"
body = {"query": query, "page_size": min(page_size, 100)}
try:
resp = requests.post(url, headers=HEADERS, json=body, timeout=15)
resp.raise_for_status()
results = resp.json().get("results", [])
return [
{
"id": r.get("id"),
"title": _extract_title(r),
"url": r.get("url"),
"type": r.get("object"),
}
for r in results
]
except requests.RequestException as e:
return [{"error": str(e)}]
def _extract_title(page: dict) -> str:
"""Notion ページからタイトルを抽出するヘルパー関数。"""
props = page.get("properties", {})
for key in ("Name", "Title", "title"):
prop = props.get(key, {})
title_arr = prop.get("title", [])
if title_arr:
return title_arr[0].get("plain_text", "(無題)")
return "(無題)"
if __name__ == "__main__":
mcp.run()
この場合、Claude Desktop の設定に環境変数を追加します。
{
"mcpServers": {
"notion-mcp": {
"command": "python",
"args": ["/Users/yourname/projects/notion_mcp.py"],
"env": {
"NOTION_API_KEY": "secret_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}
}
}
}
事例区分: 自社検証
上記の Notion MCP サーバーを実際に構築・検証した結果、Claude Desktop から「Notion で AI エージェントに関するページを調べて」と入力するだけで関連ページが一覧表示されるようになりました。日常的なリサーチ作業での効率化を確認しています。
MCP サーバー自作のセキュリティ注意点
MCP サーバーは LLM から直接呼び出されます。セキュリティを考慮した設計が欠かせません。
- API キーは環境変数で管理:コードに直接書かず、
os.environ.get()で読み込む。設定 JSON のenvセクションに記述する方法が推奨 - ファイル操作は書き込み先をサンドボックス化:ホームディレクトリ直下など広い範囲へのアクセスは原則禁止し、専用ディレクトリ(例:
~/mcp_workspace/)に限定する - 入力バリデーション:型ヒントで一次バリデーションは自動化されるが、値の範囲チェック(例:
page_size: intに対してmin(page_size, 100))も追加する - タイムアウト設定:外部 API 呼び出しには必ず
timeoutを設定し、ハングアップを防ぐ - ログを記録する:FastMCP はリクエスト/レスポンスのログをオプションで出力できる。本番運用時は有効化を推奨
参考・出典
- FastMCP 公式ドキュメント — Welcome(参照日: 2026-06-11)
- FastMCP 公式チュートリアル — Create an MCP Server(参照日: 2026-06-11)
- FastMCP 公式 — Claude Desktop 連携ガイド(参照日: 2026-06-11)
- fastmcp · PyPI(バージョン 3.4.2、参照日: 2026-06-11)
- Model Context Protocol Python SDK — GitHub(参照日: 2026-06-11)
まとめ:今日から始める3つのアクション
- 今日:
pip install fastmcpを実行し、Step 2 のサンプルサーバーを Claude Desktop に接続してみる - 今週中:自分がよく使うスクリプト(ファイル操作・API 呼び出しなど)に
@mcp.toolを追加して、AI から呼び出せるようにする - 今月中:Notion・Slack・GitHub など外部サービスと連携した MCP サーバーを構築し、日常業務の AI 化を進める
あわせて読みたい:
著者:佐藤傑(さとう・すぐる)
株式会社 Uravation 代表取締役。X(@SuguruKun_ai)フォロワー10万人超。100社以上の企業向け AI 研修・導入支援。著書累計3万部突破。SoftBank IT 連載 7 回執筆(NewsPicks 最大 1,125 ピックス)。
この記事を読んで導入イメージが固まってきた方へ
Uravation では AI エージェント導入の研修・コンサルを行っています。MCP サーバーの設計から本番運用まで、実践的なサポートが可能です。
