fastmcp について
FastMCP は、MCP サーバーを構築する開発者向けの Python フレームワークです。ツール、リソース、プロンプトを公開でき、バックグラウンドタスク、コンポーネント構成、ミドルウェアにも対応します。
主な機能
- 01ツール、リソース、プロンプトの定義
- 02バックグラウンドタスクと進捗報告
- 03プロバイダー構成とトランスフォーム
- 04ミドルウェアとコンポーネント認可
- 05複数のストレージバックエンド
活用シーン
- 01Python 関数を MCP ツールとして公開
- 02クライアントにデータリソースとプロンプトを提供
- 03進捗を報告しながら長時間タスクを実行
- 04ミドルウェアでレート制限とキャッシュを適用
MCP 対応
FastMCP は、MCP クライアントにツール、リソース、プロンプトを公開する MCP サーバーの構築に使えます。リモート MCP サーバーのプロキシにも対応します。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ jezweb/ claude-skills --skill fastmcpインストール後、Claude Code で/fastmcpを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
README.md.claude-plugin/plugin.jsonagents/mcp-builder.mdmcp-developer.mdmcp-scaffold.mdreferences/cli-commands.mdcloud-deployment.mdcommon-errors.mdcontext-features.mdintegration-patterns.mdproduction-patterns.mdrules/fastmcp.mdscripts/check-versions.shdeploy-cloud.shtest-server.shtemplates/.env.exampleapi-client-pattern.pybasic-server.pyclient-example.pyerror-handling.pyopenapi-integration.pyprompts-examples.pypyproject.tomlrequirements.txtresources-examples.pyself-contained-server.pytools-examples.py詳細
FastMCP - Python で MCP サーバーを構築
FastMCP は、Claude などの大規模言語モデルにツール、リソース、プロンプトを公開する Model Context Protocol(MCP)サーバーを構築するための Python フレームワークです。このスキルでは、堅牢な MCP サーバーを構築するための、本番環境で検証済みのパターン、エラー防止策、デプロイ戦略を紹介します。
クイックスタート
インストール
pip install fastmcp
# or
uv pip install fastmcp
最小限のサーバー
from fastmcp import FastMCP
# MUST be at module level for FastMCP Cloud
mcp = FastMCP("My Server")
@mcp.tool()
async def hello(name: str) -> str:
"""Say hello to someone."""
return f"Hello, {name}!"
if __name__ == "__main__":
mcp.run()
実行するには:
# Local development
python server.py
# With FastMCP CLI
fastmcp dev server.py
# HTTP mode
python server.py --transport http --port 8000
v2.14.x の新機能(2025年12月)
v2.14.2(2024年12月31日)
- 互換性のため、MCP SDK を <2.x に固定
- Supabase プロバイダーに
auth_routeパラメーターを追加 - バグ修正:outputSchema の
$ref解決、OAuth Proxy の検証、OpenAPI 3.1 のサポート
v2.14.1:ツールを使ったサンプリング(SEP-1577)
ctx.sample()がツールを受け付けるようになりました。エージェント型ワークフローに対応しますAnthropicSamplingHandlerが実験的機能から昇格- 1 回の LLM 呼び出しで
SampleStepを返すctx.sample_step() - Python 3.13 のサポートを追加
v2.14.0:バックグラウンドタスク(SEP-1686)
- 長時間実行される処理向けのプロトコルネイティブなバックグラウンドタスク
- 非同期デコレーターに
task=Trueを追加すると、ブロックせずに進捗を追跡できます - MCP 2025-11-25 仕様をサポート
- SEP-1699:SSE ポーリングとイベント再開機能
- SEP-1330:複数選択可能な列挙型のエリシテーションスキーマ
- SEP-1034:エリシテーションスキーマのデフォルト値
⚠️ 破壊的変更(v2.14.0):
BearerAuthProviderモジュールを削除(JWTVerifierまたはOAuthProxyを使用してください)Context.get_http_request()メソッドを削除fastmcp.Imageのトップレベルインポートを削除(from fastmcp.utilities import Imageを使用してください)enable_docket、enable_tasks設定を削除(常に有効です)run_streamable_http_async()、sse_app()、streamable_http_app()、run_sse_async()メソッドを削除- デコレーターから
dependenciesパラメーターを削除 output_schema=Falseのサポートを廃止FASTMCP_SERVER_環境変数のプレフィックスを非推奨化
既知の互換性:
- MCP SDK は <2.x に固定されています(v2.14.2 以降)
v3.0.0 の新機能(ベータ版 - 2026年1月)
⚠️ 重大な破壊的変更 - FastMCP 3.0 は、アーキテクチャを全面的にリファクタリングしたものです。
プロバイダーアーキテクチャ
すべてのコンポーネントが プロバイダー 経由で提供されるようになりました:
FileSystemProvider- ディレクトリ内のデコレーター付き関数をホットリロードで検出SkillsProvider- エージェントのスキルファイルを MCP リソースとして公開OpenAPIProvider- OpenAPI 仕様から自動生成ProxyProvider- リモート MCP サーバーにプロキシ接続
from fastmcp import FastMCP
from fastmcp.providers import FileSystemProvider
mcp = FastMCP("server")
mcp.add_provider(FileSystemProvider(path="./tools", reload=True))
トランスフォーム(コンポーネントミドルウェア)
ソースコードを変更せずにコンポーネントを変更できます:
- 名前空間の設定、名前の変更、バージョンによるフィルタリング
ResourcesAsTools- リソースをツールとして公開PromptsAsTools- プロンプトをツールとして公開
from fastmcp.transforms import Namespace, VersionFilter
mcp.add_transform(Namespace(prefix="api"))
mcp.add_transform(VersionFilter(min_version="2.0"))
コンポーネントのバージョン管理
@mcp.tool(version="2.0")
async def fetch_data(query: str) -> dict:
# Clients see highest version by default
# Can request specific version
return {"data": [...]}
セッションスコープの状態
@mcp.tool()
async def set_preference(key: str, value: str, ctx: Context) -> dict:
await ctx.set_state(key, value) # Persists across session
return {"saved": True}
@mcp.tool()
async def get_preference(key: str, ctx: Context) -> dict:
value = await ctx.get_state(key, default=None)
return {"value": value}
その他の機能
- 開発中の自動再起動に対応する
--reloadフラグ - 同期関数をスレッドプールへ自動ディスパッチ
- ツールのタイムアウト
- OpenTelemetry トレーシング
- コンポーネントの認可:
@tool(auth=require_scopes("admin"))
移行ガイド
準備ができていない場合は v2 に固定してください:
# requirements.txt
fastmcp<3
ほとんどのサーバーでは、インポートを更新するだけで十分です:
# v2.x and v3.0 compatible
from fastmcp import FastMCP
mcp = FastMCP("server")
# ... rest of code works the same
参照:公式移行ガイド
基本概念
ツール
LLM が呼び出せる関数です。ベストプラクティス:明確な名前、充実した docstring(LLM が読み取ります!)、適切な型ヒント(Pydantic が検証します)、構造化された戻り値、エラー処理。
@mcp.tool()
async def async_tool(url: str) -> dict: # Use async for I/O
async with httpx.AsyncClient() as client:
return (await client.get(url)).json()
リソース
LLM にデータを公開します。URI スキーム:data://、file://、resource://、info://、api://、またはカスタムスキーム。
@mcp.resource("user://{user_id}/profile") # Template with parameters
async def get_user(user_id: str) -> dict: # CRITICAL: param names must match
return await fetch_user_from_db(user_id)
プロンプト
パラメーター付きの事前設定済みプロンプトです。
@mcp.prompt("analyze")
def analyze_prompt(topic: str) -> str:
return f"Analyze {topic} considering: state, challenges, opportunities, recommendations."
コンテキスト機能
高度な機能を利用するには、Context パラメーター(型ヒントも必要です!)を挿入します:
エリシテーション(ユーザー入力):
from fastmcp import Context
@mcp.tool()
async def confirm_action(action: str, context: Context) -> dict:
confirmed = await context.request_elicitation(prompt=f"Confirm {action}?", response_type=str)
return {"status": "completed" if confirmed.lower() == "yes" else "cancelled"}
進捗の追跡:
@mcp.tool()
async def batch_import(file_path: str, context: Context) -> dict:
data = await read_file(file_path)
for i, item in enumerate(data):
await context.report_progress(i + 1, len(data), f"Importing {i + 1}/{len(data)}")
await import_item(item)
return {"imported": len(data)}
サンプリング(ツールからの LLM 呼び出し):
@mcp.tool()
async def enhance_text(text: str, context: Context) -> str:
response = await context.request_sampling(
messages=[{"role": "user", "content": f"Enhance: {text}"}],
temperature=0.7
)
return response["content"]
バックグラウンドタスク(v2.14.0+)
クライアントをブロックせずに進捗を報告する、長時間実行される処理です。Docket タスクスケジューラーを使用します(v2.14.0+ では常に有効です)。
基本的な使い方:
@mcp.tool(task=True) # Enable background task mode
async def analyze_large_dataset(dataset_id: str, context: Context) -> dict:
"""Analyze large dataset with progress tracking."""
data = await fetch_dataset(dataset_id)
for i, chunk in enumerate(data.chunks):
# Report progress to client
await context.report_progress(
current=i + 1,
total=len(data.chunks),
message=f"Processing chunk {i + 1}/{len(data.chunks)}"
)
await process_chunk(chunk)
return {"status": "complete", "records_processed": len(data)}
タスクの状態: pending → running → completed / failed / cancelled
使用する場面:
- 30 秒を超える処理(LLM のタイムアウトのリスクがある場合)
- 項目ごとのステータス更新を伴うバッチ処理
- 実行中にユーザー入力が必要になる可能性のある処理
- 長時間実行される API 呼び出しやデータ処理
既知の制限事項(v2.14.x):
- バックグラウンドタスクのポーリング中、
ctx.report_progress()のstatusMessageはクライアントに転送されません(GitHub Issue #2904) - 進捗メッセージはサーバーログには表示されますが、クライアント UI には表示されません
- 回避策:現時点では FastMCP の代わりに公式 MCP SDK(
mcp>=1.10.0)を使用してください - ステータス:PR #2906 で修正対応中です
重要: タスクは Docket スケジューラーを通じて実行されます。プロキシ経由ではタスクを実行できません(エラーが発生します)。
ツールを使ったサンプリング(v2.14.1+)
サーバーは ctx.sample() にツールを渡し、LLM がサンプリング中にツールを呼び出せるエージェント型ワークフローを実現できます。
エージェント型サンプリング:
from fastmcp import Context
from fastmcp.sampling import AnthropicSamplingHandler
# Configure sampling handler
mcp = FastMCP("Agent Server")
mcp.add_sampling_handler(AnthropicSamplingHandler(api_key=os.getenv("ANTHROPIC_API_KEY")))
@mcp.tool()
async def research_topic(topic: str, context: Context) -> dict:
"""Research a topic using agentic sampling with tools."""
# Define tools available during sampling
research_tools = [
{
"name": "search_web",
"description": "Search the web for information",
"inputSchema": {"type": "object", "properties": {"query": {"type": "string"}}}
},
{
"name": "fetch_url",
"description": "Fetch content from a URL",
"inputSchema": {"type": "object", "properties": {"url": {"type": "string"}}}
}
]
# Sample with tools - LLM can call these tools during reasoning
result = await context.sample(
messages=[{"role": "user", "content": f"Research: {topic}"}],
tools=research_tools,
max_tokens=4096
)
return {"research": result.content, "tools_used": result.tool_calls}
単一ステップのサンプリング:
@mcp.tool()
async def get_single_response(prompt: str, context: Context) -> dict:
"""Get a single LLM response without tool loop."""
# sample_step() returns SampleStep for inspection
step = await context.sample_step(
messages=[{"role": "user", "content": prompt}],
temperature=0.7
)
return {
"content": step.content,
"model": step.model,
"stop_reason": step.stop_reason
}
サンプリングハンドラー:
AnthropicSamplingHandler- Claude モデル向け(v2.14.1+)OpenAISamplingHandler- GPT モデル向け
既知の制限事項:
クライアントが単一のサーバーに接続している場合、ctx.sample() は動作しますが、クライアントで複数のサーバーが設定されている場合は「Sampling not supported」エラーが発生します。サンプリングを使用しないツールは問題なく動作します。(コミュニティからの報告)
ストレージバックエンド
OAuth トークン、レスポンスキャッシュ、永続状態の保存に py-key-value-aio を使用しています。
利用可能なバックエンド:
- Memory(デフォルト):一時的で高速、開発用途のみ
- Disk:永続化され、
FernetEncryptionWrapperで暗号化されます。プラットフォームに対応しています(Mac/Windows のデフォルト) - Redis:分散型で、本番環境や複数インスタンス向け
- その他:DynamoDB、MongoDB、Elasticsearch、Memcached、RocksDB、Valkey
基本的な使い方:
from key_value.stores import DiskStore, RedisStore
from key_value.encryption import FernetEncryptionWrapper
from cryptography.fernet import Fernet
# Disk (persistent, single instance)
mcp = FastMCP("Server", storage=DiskStore(path="/app/data/storage"))
# Redis (distributed, production)
mcp = FastMCP("Server", storage=RedisStore(
host=os.getenv("REDIS_HOST"), password=os.getenv("REDIS_PASSWORD")
))
# Encrypted storage (recommended)
mcp = FastMCP("Server", storage=FernetEncryptionWrapper(
key_value=DiskStore(path="/app/data"),
fernet=Fernet(os.getenv("STORAGE_ENCRYPTION_KEY"))
))
プラットフォームごとのデフォルト: Mac/Windows では Disk、Linux では Memory が使用されます。storage パラメーターで変更できます。
サーバーのライフスパン
⚠️ v2.13.0 での破壊的変更:ライフスパンの動作がセッションごとからサーバーインスタンスごとに変更されました。
リソースはサーバーごとに一度だけ初期化・クリーンアップします(セッションごとではありません)。これは DB 接続や API クライアントにとって重要です。
from contextlib import asynccontextmanager
from dataclasses import dataclass
@dataclass
class AppContext:
db: Database
api_client: httpx.AsyncClient
@asynccontextmanager
async def app_lifespan(server: FastMCP):
"""Runs ONCE per server instance."""
db = await Database.connect(os.getenv("DATABASE_URL"))
api_client = httpx.AsyncClient(base_url=os.getenv("API_BASE_URL"), timeout=30.0)
try:
yield AppContext(db=db, api_client=api_client)
finally:
await db.disconnect()
await api_client.aclose()
mcp = FastMCP("Server", lifespan=app_lifespan)
# Access in tools
@mcp.tool()
async def query_db(sql: str, context: Context) -> list:
app_ctx = context.fastmcp_context.lifespan_context
return await app_ctx.db.query(sql)
ASGI 統合(FastAPI/Starlette):
mcp = FastMCP("Server", lifespan=mcp_lifespan)
app = FastAPI(lifespan=mcp.lifespan) # ✅ MUST pass lifespan!
状態管理:
context.fastmcp_context.set_state(key, value) # Store
context.fastmcp_context.get_state(key, default=None) # Retrieve
ミドルウェアシステム
組み込みの 8 種類: TimingMiddleware、ResponseCachingMiddleware、LoggingMiddleware、RateLimitingMiddleware、ErrorHandlingMiddleware、ToolInjectionMiddleware、PromptToolMiddleware、ResourceToolMiddleware
実行順序(順序が重要です!):
Request Flow:
→ ErrorHandlingMiddleware (catches errors)
→ TimingMiddleware (starts timer)
→ LoggingMiddleware (logs request)
→ RateLimitingMiddleware (checks rate limit)
→ ResponseCachingMiddleware (checks cache)
→ Tool/Resource Handler
基本的な使い方:
from fastmcp.middleware import ErrorHandlingMiddleware, TimingMiddleware, LoggingMiddleware
mcp.add_middleware(ErrorHandlingMiddleware()) # First: catch errors
mcp.add_middleware(TimingMiddleware()) # Second: time requests
mcp.add_middleware(LoggingMiddleware(level="INFO"))
mcp.add_middleware(RateLimitingMiddleware(max_requests=100, window_seconds=60))
mcp.add_middleware(ResponseCachingMiddleware(ttl_seconds=300, storage=RedisStore()))
カスタムミドルウェア:
from fastmcp.middleware import BaseMiddleware
class AccessControlMiddleware(BaseMiddleware):
async def on_call_tool(self, tool_name, arguments, context):
user = context.fastmcp_context.get_state("user_id")
if user not in self.allowed_users:
raise PermissionError(f"User not authorized")
return await self.next(tool_name, arguments, context)
フックの階層: on_message(すべて)→ on_request/on_notification → on_call_tool/on_read_resource/on_get_prompt → on_list_*(リスト操作)
サーバーの合成
2 つの方式:
-
import_server()- 静的スナップショット:インポート時に一度だけコピーされ、変更は反映されず、高速です(実行時の委譲なし)。用途:確定済みのコンポーネントバンドル。 -
mount()- 動的リンク:実行時にライブリンクされ、変更はすぐに反映され、実行時に委譲されます(低速)。用途:モジュール式の実行時合成。
基本的な使い方:
# Import (static)
main_server.import_server(api_server) # One-time copy
# Mount (dynamic)
main_server.mount(api_server, prefix="api") # Tools: api.fetch_data
main_server.mount(db_server, prefix="db") # Resources: resource://db/path
タグによるフィルタリング:
@api_server.tool(tags=["public"])
def public_api(): pass
main_server.import_server(api_server, include_tags=["public"]) # Only public
main_server.mount(api_server, prefix="api", exclude_tags=["admin"]) # No admin
リソースプレフィックスの形式:
- パス(v2.4.0 以降のデフォルト):
resource://prefix/path - プロトコル(レガシー):
prefix+resource://path
main_server.mount(subserver, prefix="api", resource_prefix_format="path")
OAuth と認証
4 つの認証パターン:
- トークン検証(
JWTVerifier):外部トークンを検証します - 外部 ID プロバイダー(
RemoteAuthProvider):DCR を使用した OAuth 2.0/OIDC - OAuth プロキシ(
OAuthProxy):DCR に対応していないプロバイダー(GitHub、Google、Azure、AWS、Discord、Facebook)とのブリッジ - フル OAuth(
OAuthProvider):完全な認可サーバー
パターン 1:トークン検証
from fastmcp.auth import JWTVerifier
auth = JWTVerifier(issuer="https://auth.example.com", audience="my-server",
public_key=os.getenv("JWT_PUBLIC_KEY"))
mcp = FastMCP("Server", auth=auth)
パターン 3:OAuth プロキシ(本番環境)
from fastmcp.auth import OAuthProxy
from key_value.stores import RedisStore
from key_value.encryption import FernetEncryptionWrapper
from cryptography.fernet import Fernet
auth = OAuthProxy(
jwt_signing_key=os.environ["JWT_SIGNING_KEY"],
client_storage=FernetEncryptionWrapper(
key_value=RedisStore(host=os.getenv("REDIS_HOST"), password=os.getenv("REDIS_PASSWORD")),
fernet=Fernet(os.environ["STORAGE_ENCRYPTION_KEY"])
),
upstream_authorization_endpoint="https://github.com/login/oauth/authorize",
upstream_token_endpoint="https://github.com/login/oauth/access_token",
upstream_client_id=os.getenv("GITHUB_CLIENT_ID"),
upstream_client_secret=os.getenv("GITHUB_CLIENT_SECRET"),
enable_consent_screen=True # CRITICAL: Prevents confused deputy attacks
)
mcp = FastMCP("GitHub Auth", auth=auth)
**OAuth プロキシの機能:**トークンファクトリーパターン(独自の JWT を発行)、同意画面(バイパスを防止)、PKCE のサポート、RFC 7662 トークンイントロスペクション
**対応プロバイダー:**GitHub、Google、Azure、AWS Cognito、Discord、Facebook、WorkOS、AuthKit、Descope、Scalekit、OCI(v2.13.1)
Supabase プロバイダー(v2.14.2 以降):
from fastmcp.auth import SupabaseProvider
auth = SupabaseProvider(
auth_route="/custom-auth", # Custom auth route (new in v2.14.2)
# ... other config
)
アイコン、API 統合、クラウドデプロイ
**アイコン:**サーバー、ツール、リソース、プロンプトに追加できます。Icon(url, size) を使用します。Icon.from_file() または Image.to_data_uri() を使ってデータ URI を指定できます(v2.13.1)。
API 統合(3 つのパターン):
- **手動:**ベース URL、ヘッダー、タイムアウトを指定して
httpx.AsyncClientを使用します - OpenAPI 自動生成:
FastMCP.from_openapi(spec, client, route_maps)— GET はリソース/テンプレートに、POST/PUT/DELETE はツールになります - FastAPI 変換:
FastMCP.from_fastapi(app, httpx_client_kwargs)
クラウドデプロイの重要な要件:
- ❗ モジュールレベルに
mcp、server、またはappという名前のサーバーを定義する - requirements.txt には PyPI の依存関係のみを記載する
- 公開 GitHub リポジトリ(またはアクセス可能なリポジトリ)
- 設定には環境変数を使用する
# ✅ CORRECT: Module-level export
mcp = FastMCP("server") # At module level!
# ❌ WRONG: Function-wrapped
def create_server():
return FastMCP("server") # Too late for cloud!
**デプロイ:**https://fastmcp.cloud → サインイン → プロジェクトを作成 → リポジトリを選択 → デプロイ
クライアント設定(Claude Desktop):
{"mcpServers": {"my-server": {"url": "https://project.fastmcp.app/mcp", "transport": "http"}}}
よくある 30 のエラー(解決策付き)
エラー 1:サーバーオブジェクトがない
エラー:RuntimeError: No server object found at module level
**原因:**サーバーがモジュールレベルで公開されていません(FastMCP Cloud の要件)
**解決策:**関数内ではなく、モジュールレベルに mcp = FastMCP("server") を定義します
エラー 2:Async/Await の混同
エラー:RuntimeError: no running event loop、TypeError: object coroutine can't be used in 'await'
**原因:**同期処理と非同期処理が正しく使い分けられていません
解決策:await を使うツールには async def を、非同期コードでない場合は同期の def を使用します
エラー 3:コンテキストが注入されない
エラー:TypeError: missing 1 required positional argument: 'context'
原因:Context 型の注釈がありません
解決策:async def tool(context: Context) のように、型ヒントを必ず指定します
エラー 4:リソース URI の構文
エラー:ValueError: Invalid resource URI: missing scheme
**原因:**リソース URI にスキームのプレフィックスがありません
解決策:@mcp.resource("config") ではなく、@mcp.resource("data://config") を使用します
エラー 5:リソーステンプレートのパラメーター不一致
エラー:TypeError: get_user() missing 1 required positional argument
**原因:**関数のパラメーター名が URI テンプレートと一致していません
解決策:@mcp.resource("user://{user_id}/profile") → def get_user(user_id: str) のように、名前を正確に一致させます
エラー 6:Pydantic の検証エラー
エラー:ValidationError: value is not a valid integer
**原因:**型ヒントが渡されたデータと一致していません
**解決策:**Pydantic モデルを使用します:class Params(BaseModel): query: str = Field(min_length=1)
エラー 7:トランスポート/プロトコルの不一致
エラー:ConnectionError: Server using different transport
**原因:**クライアントとサーバーで互換性のないトランスポートを使用しています
**解決策:**トランスポートを一致させます。stdio の場合:mcp.run() と {"command": "python", "args": ["server.py"]}、HTTP の場合:mcp.run(transport="http", port=8000) と {"url": "http://localhost:8000/mcp", "transport": "http"}
HTTP タイムアウトの問題(v2.14.3 で修正):
- HTTP トランスポートのデフォルトタイムアウトが、MCP のデフォルトである 30 秒ではなく 5 秒になっていました(GitHub Issue #2845)
- 5 秒を超えるツールは、v2.14.2 以前ではエラーが表示されないまま失敗していました
- **解決策:**fastmcp>=2.14.3 にアップグレードします(タイムアウトが MCP のデフォルトである 30 秒に準拠するようになりました)
エラー 8:インポートエラー(編集可能パッケージ)
エラー: ModuleNotFoundError: No module named 'my_package'
原因: パッケージが正しくインストールされていません
解決策: pip install -e . を実行するか、絶対インポートを使用するか、export PYTHONPATH="/path/to/project" を設定します
エラー 9:非推奨の警告
エラー: DeprecationWarning: 'mcp.settings' is deprecated
原因: 古い FastMCP v1 API を使用しています
解決策: mcp.settings.get("API_KEY") の代わりに os.getenv("API_KEY") を使用します
エラー 10:ポートはすでに使用中です
エラー: OSError: [Errno 48] Address already in use
原因: ポート 8000 はすでに使用されています
解決策: 別のポート --port 8001 を使用するか、プロセスを終了します:lsof -ti:8000 | xargs kill -9
エラー 11:スキーマ生成の失敗
エラー: TypeError: Object of type 'ndarray' is not JSON serializable
原因: サポートされていない型ヒント(NumPy 配列、カスタムクラス)を使用しています
解決策: JSON 互換の型 list[float] を返すか、{"values": np_array.tolist()} のように変換します
カスタムクラスはサポートされません(コミュニティからの情報): FastMCP は Pydantic 互換の型をすべてサポートしていますが、ツールの戻り値としてカスタムクラスを使用する場合は、辞書または Pydantic モデルに変換する必要があります:
# ❌ NOT SUPPORTED
class MyCustomClass:
def __init__(self, value: str):
self.value = value
@mcp.tool()
async def get_custom() -> MyCustomClass:
return MyCustomClass("test") # Serialization error
# ✅ SUPPORTED - Use dict or Pydantic
@mcp.tool()
async def get_custom() -> dict[str, str]:
obj = MyCustomClass("test")
return {"value": obj.value}
# OR use Pydantic BaseModel
from pydantic import BaseModel
class MyModel(BaseModel):
value: str
@mcp.tool()
async def get_model() -> MyModel:
return MyModel(value="test") # Works!
OutputSchema の $ref 解決(v2.14.2 で修正済み):
outputSchema内のルートレベルの$refが参照先に解決されませんでした(GitHub Issue #2720)- MCP 仕様への準拠違反とクライアントの互換性に関する問題が発生していました
- 解決策: fastmcp>=2.14.2 にアップグレードします($ref が自動的に参照先に解決されます)
エラー 12:JSON シリアル化
エラー: TypeError: Object of type 'datetime' is not JSON serializable
原因: JSON にシリアル化できないオブジェクトを返しています
解決策: datetime.now().isoformat() のように変換します。バイト列の場合は .decode('utf-8') を使用します
エラー 13:循環インポートエラー
エラー: ImportError: cannot import name 'X' from partially initialized module
原因: 循環依存(クラウドへのデプロイでよく見られます)
解決策: __init__.py で直接インポート from .api_client import APIClient を使用するか、関数内で遅延インポートします
エラー 14:Python バージョンの互換性
エラー: DeprecationWarning: datetime.utcnow() is deprecated
原因: 非推奨の Python 3.12 以降のメソッドを使用しています
解決策: datetime.utcnow() の代わりに datetime.now(timezone.utc) を使用します
エラー 15:インポート時の実行
エラー: RuntimeError: Event loop is closed
原因: モジュールのインポート時に非同期リソースを作成しています
解決策: 遅延初期化を使用します。非同期の connect() メソッドを持つ接続クラスを作成し、必要なときにツールから呼び出します
エラー 16:ストレージバックエンドが設定されていません
エラー: RuntimeError: OAuth tokens lost on restart、ValueError: Cache not persisting
原因: 永続化が必要な本番環境で、デフォルトのメモリストレージを使用しています
解決策: FernetEncryptionWrapper と組み合わせて、暗号化された DiskStore(単一インスタンス)または RedisStore(複数インスタンス)を使用します
エラー 17:ASGI アプリにライフスパンが渡されていません
エラー: RuntimeError: Database connection never initialized、Warning: MCP lifespan hooks not running
原因: ライフスパンを渡さずに、FastAPI/Starlette と FastMCP を使用しています(v2.13.0 の要件)
解決策: app = FastAPI(lifespan=mcp.lifespan) とします。ライフスパンを必ず渡してください!
エラー 18:ミドルウェアの実行順序エラー
エラー: RuntimeError: Rate limit not checked before caching
原因: ミドルウェアの順序が正しくありません(順序が重要です!)
解決策: ErrorHandling → Timing → Logging → RateLimiting → ResponseCaching の順序にします
エラー 19:ミドルウェアの循環依存
エラー: RecursionError: maximum recursion depth exceeded
原因: ミドルウェアが self.next() を呼び出していないか、誤った方法で呼び出しています
解決策: ミドルウェアフックでは、必ず result = await self.next(tool_name, arguments, context) を呼び出します
エラー 20:インポートとマウントの混同
エラー: RuntimeError: Subserver changes not reflected、ValueError: Unexpected tool namespacing
原因: mount() が必要な場面で import_server() を使用している(またはその逆)
解決策: 静的なバンドル(一度限りのコピー)には import_server()、動的な合成(ライブリンク)には mount() を使用します
エラー 21:リソースプレフィックス形式の不一致
エラー: ValueError: Resource not found: resource://api/users
原因: リソースプレフィックスの形式が正しくありません
解決策: パス形式(デフォルト、v2.4.0 以降):resource://prefix/path、プロトコル形式(レガシー):prefix+resource://path。resource_prefix_format="path" で設定します
エラー 22:同意画面なしの OAuth プロキシ
エラー: SecurityWarning: Authorization bypass possible
原因: 同意画面のない OAuth プロキシ(セキュリティ上の脆弱性)
解決策: 必ず enable_consent_screen=True を設定します。これにより混乱した代理人攻撃を防止できます(重大)
エラー 23:本番環境での JWT 署名キーの欠落
エラー: ValueError: JWT signing key required for OAuth Proxy
原因: OAuth プロキシに jwt_signing_key が設定されていません
解決策: secrets.token_urlsafe(32) で生成し、FASTMCP_JWT_SIGNING_KEY 環境変数に保存して、OAuthProxy(jwt_signing_key=...) に渡します
エラー 24:アイコンのデータ URI 形式エラー
エラー: ValueError: Invalid data URI format
原因: アイコンのデータ URI の形式が正しくありません
解決策: Icon.from_file("/path/icon.png", size="medium") または Image.to_data_uri()(v2.13.1)を使用します。手動で形式を指定しないでください
エラー 25:ライフスパンの動作変更(v2.13.0)
エラー: Warning: Lifespan runs per-server, not per-session
原因: v2.13.0 以降で、v2.12 の動作(セッション単位)を想定しています(サーバー単位)
解決策: v2.13.0 以降のライフスパンはセッションごとではなく、サーバーごとに 1 回実行されます。セッション単位のロジックにはミドルウェアを使用します
エラー 26:BearerAuthProvider の削除(v2.14.0)
エラー: ImportError: cannot import name 'BearerAuthProvider' from 'fastmcp.auth'
原因: v2.14.0 で BearerAuthProvider モジュールが削除されました
解決策: トークンの検証には JWTVerifier を、OAuth フロー全体には OAuthProxy を使用します。
# Before (v2.13.x)
from fastmcp.auth import BearerAuthProvider
# After (v2.14.0+)
from fastmcp.auth import JWTVerifier
auth = JWTVerifier(issuer="...", audience="...", public_key="...")
エラー 27:Context.get_http_request() の削除(v2.14.0)
エラー: AttributeError: 'Context' object has no attribute 'get_http_request'
原因: v2.14.0 で Context.get_http_request() メソッドが削除されました
解決策: ミドルウェア経由でリクエスト情報にアクセスするか、ミドルウェアに公開される InitializeResult を使用します
エラー 28:Image のインポートパス変更(v2.14.0)
エラー: ImportError: cannot import name 'Image' from 'fastmcp'
原因: v2.14.0 で fastmcp.Image のトップレベルインポートが削除されました
解決策: 新しいインポートパスを使用します。
# Before (v2.13.x)
from fastmcp import Image
# After (v2.14.0+)
from fastmcp.utilities import Image
エラー 29:FastAPI のマウントパスの二重化
エラー: クライアントが /mcp エンドポイントに接続できず、404 が返されます
参照元: GitHub Issue #2961
原因: FastMCP を /mcp にマウントすると、パスプレフィックスが重複して、エンドポイントが /mcp/mcp に作成されます
解決策: ルート / にマウントするか、クライアント設定を調整します
# ❌ WRONG - Creates /mcp/mcp endpoint
from fastapi import FastAPI
from fastmcp import FastMCP
mcp = FastMCP("server")
app = FastAPI(lifespan=mcp.lifespan)
app.mount("/mcp", mcp) # Endpoint becomes /mcp/mcp
# ✅ CORRECT - Mount at root
app.mount("/", mcp) # Endpoint is /mcp
# ✅ OR adjust client config
# In claude_desktop_config.json:
{"url": "http://localhost:8000/mcp/mcp", "transport": "http"}
重要: FastAPI に lifespan=mcp.lifespan も渡す必要があります(エラー #17 を参照)。
エラー 30:バックグラウンドタスクで「No Active Context」が発生する(ASGI マウント)
エラー: RuntimeError: No active context found
参照元: GitHub Issue #2877
原因: task=True のバックグラウンドタスクを使用して FastMCP を FastAPI/Starlette にマウントした際の ContextVar の伝播に関する問題
解決策: fastmcp>=2.14.3 にアップグレードします
# In v2.14.2 and earlier - FAILS
from fastapi import FastAPI
from fastmcp import FastMCP, Context
mcp = FastMCP("server")
app = FastAPI(lifespan=mcp.lifespan)
@mcp.tool(task=True)
async def sample(name: str, ctx: Context) -> dict:
# RuntimeError: No active context found
await ctx.report_progress(1, 1, "Processing")
return {"status": "OK"}
app.mount("/", mcp)
# ✅ FIXED in v2.14.3
# pip install fastmcp>=2.14.3
注: エラー #17(ASGI アプリへのライフスパンの未渡し)に関連しています。
本番環境のパターン、テスト、CLI
本番環境の4つのパターン:
- Utils モジュール:Config クラスと
format_success/errorヘルパーを含む単一のutils.py - コネクションプーリング:
get_client()クラスメソッドを持つシングルトンのhttpx.AsyncClient - バックオフ付きリトライ:
retry_with_backoff(func, max_retries=3, initial_delay=1.0, exponential_base=2.0) - 時間ベースのキャッシュ:
.get()と.set()メソッドを持つTimeBasedCache(ttl=300)
テスト:
- 単体:
pytest+create_test_client(test_server)+await client.call_tool() - 統合:
Client("server.py")+list_tools()+call_tool()+list_resources()
CLI コマンド:
fastmcp dev server.py # Run with inspector
fastmcp install server.py # Install to Claude Desktop
FASTMCP_LOG_LEVEL=DEBUG fastmcp dev # Debug logging
ベストプラクティス: モジュールレベルでエクスポートするファクトリーパターン、検証付きの環境設定、包括的な docstring(LLM が読み取ります!)、ヘルスチェック用リソース
プロジェクト構成:
- シンプル:
server.py、requirements.txt、.env、README.md - 本番環境:
src/(server.py、utils.py、tools/、resources/、prompts/)、tests/、pyproject.toml
参考資料とまとめ
公式: https://github.com/jlowin/fastmcp、https://fastmcp.cloud、https://modelcontextprotocol.io、Context7:`/jlowin/fastmcp` 関連スキル: openai-api、claude-api、cloudflare-worker-base、typescript-mcp パッケージのバージョン: fastmcp>=2.14.2(PyPI)、Python>=3.10(v2.14.1+ では 3.13 をサポート)、httpx、pydantic、py-key-value-aio、cryptography 最終更新日:2026-01-21
重要なポイント17項目:
- モジュールレベルでのサーバーのエクスポート(FastMCP Cloud)
- OAuth/キャッシュ用の永続ストレージ(Disk/Redis)
- リソース管理用のサーバーライフスパン
- ミドルウェアの順序:エラー → タイミング → ロギング → レート制限 → キャッシュ
- 合成:
import_server()(静的)とmount()(動的) - OAuth セキュリティ:同意画面 + 暗号化ストレージ + JWT 署名
- 非同期処理を適切に行う(イベントループをブロックしない)
- 構造化されたエラー処理
- 循環インポートを避ける
- ローカルでテストする(
fastmcp dev) - 環境変数を使う(シークレットをハードコードしない)
- 包括的な docstring(LLM が読み取ります!)
- 本番環境のパターン(utils、プーリング、リトライ、キャッシュ)
- OpenAPI の自動生成
- ヘルスチェック + モニタリング
- 長時間実行される処理用のバックグラウンドタスク(
task=True) - エージェント型ワークフロー用のツールを使ったサンプリング(
ctx.sample(tools=[...]))
本番環境への対応: 暗号化ストレージ、4つの認証パターン、8種類のミドルウェア、モジュール式の合成、OAuth セキュリティ(同意画面、PKCE、RFC 7662)、レスポンスキャッシュ、コネクションプーリング、タイミングミドルウェア、バックグラウンドタスク、エージェント型サンプリング、FastAPI/Starlette へのマウント、v3.0 のプロバイダーアーキテクチャ
30件以上のエラーを防止。トークンを90~95%節約。