初めてのMCPサーバーを構築する
Python MCP SDKを使ってリソース、ツール、プロンプトを公開するサーバーを作成し、Claude Desktopに接続して一連の動作を確認します。
「初めてのMCPサーバーを構築する」はCoddyKit上の無料AI Engineering Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはAI Engineering Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 AI Engineering Academyコースには全4レッスンが含まれています。
MCP サーバーのプロジェクト設定
Python で MCP サーバーを構築するには、mcp SDK と Python 環境が必要です。Claude Desktop や任意の MCP クライアントが stdio 経由で接続できるサーバーを作成します。まずパッケージをインストールし、サーバーファイルを作成します。
# Create a project directory
# mkdir my_mcp_server && cd my_mcp_server
# Create a virtual environment
# python -m venv venv && source venv/bin/activate
# Install the MCP SDK
# pip install mcp httpx
# Project structure:
# my_mcp_server/
# server.py <- Your MCP server
# requirements.txt
# README.mdサーバーオブジェクトの作成
mcp パッケージからインポートし、サーバー名を指定して Server インスタンスを作成します。この名前はクライアントの MCP サーバー一覧に表示されるため、内容が分かる名前を選んでください。サーバーオブジェクトは、すべてのツール、リソース、プロンプトを登録するためのエントリーポイントです。
# server.py
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp import types
import asyncio
import httpx
# Create the server — name is shown in Claude Desktop
app = Server('weather-server')
# --- Tool registrations go here ---
# Entry point
if __name__ == '__main__':
asyncio.run(stdio_server(app))@app.list_tools() によるツールの登録
@app.list_tools() デコレーターは、クライアントから要求されたときに利用可能なツールの一覧を返すハンドラーを登録します。各ツールは、名前、説明、パラメーターを記述する JSON Schema である inputSchema を使って定義します。クライアントはこの一覧を LLM に送信し、呼び出し可能なツールをモデルに知らせます。
@app.list_tools()
async def list_tools() -> list[types.Tool]:
return [
types.Tool(
name='get_weather',
description='Get current weather for a city. Use when the user asks about weather in a specific location.',
inputSchema={
'type': 'object',
'properties': {
'city': {
'type': 'string',
'description': 'City name, e.g. London or New York'
},
'units': {
'type': 'string',
'enum': ['metric', 'imperial'],
'description': 'Temperature units. Default is metric.'
}
},
'required': ['city']
}
),
types.Tool(
name='list_cities',
description='Return a list of major cities the user can query weather for.',
inputSchema={'type': 'object', 'properties': {}, 'required': []}
)
]ツール実行の実装
@app.call_tool()デコレーターは、ツールの実行を処理します。クライアントがツールを呼び出すと、このハンドラーはツール名と引数を受け取ります。適切な処理を実行し、結果の文字列を含むTextContentオブジェクトのリストを返します。
@app.call_tool()
async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
if name == 'list_cities':
cities = ['London', 'New York', 'Tokyo', 'Paris', 'Sydney']
return [types.TextContent(type='text', text=', '.join(cities))]
if name == 'get_weather':
city = arguments['city']
units = arguments.get('units', 'metric')
unit_symbol = 'C' if units == 'metric' else 'F'
# Call real weather API (stub here)
async with httpx.AsyncClient() as client:
# Replace with actual API call
result = f'{city}: 18{chr(176)}{unit_symbol}, partly cloudy, humidity 65%'
return [types.TextContent(type='text', text=result)]
raise ValueError(f'Unknown tool: {name}')リソースの公開
リソースとは、モデルが読み取れる静的または動的なデータで、ファイルのようなものです。リソースURIを定義し、リソースリーダーを実装します。リソースはクライアント上で、AIが参照できる項目として表示されます。設定、ドキュメント、頻繁に利用するデータのスナップショットなどに便利です。
@app.list_resources()
async def list_resources() -> list[types.Resource]:
return [
types.Resource(
uri='weather://supported-cities',
name='Supported Cities List',
description='Complete list of cities available in this weather server.',
mimeType='text/plain'
)
]
@app.read_resource()
async def read_resource(uri: str) -> str:
if uri == 'weather://supported-cities':
cities = ['London', 'New York', 'Tokyo', 'Paris', 'Sydney', 'Dubai', 'Singapore']
return '\n'.join(cities)
raise ValueError(f'Unknown resource URI: {uri}')プロンプトテンプレートの登録
プロンプトとは、クライアントがスラッシュコマンドやクイックアクションとしてユーザーに提示する、再利用可能なメッセージテンプレートです。パラメーターを受け取り、会話の初期コンテキストとなるメッセージのリストを返します。ユーザーが1つのコマンドで実行できる複雑な指示を定義するのに適しています。
@app.list_prompts()
async def list_prompts() -> list[types.Prompt]:
return [
types.Prompt(
name='weather-report',
description='Generate a formatted weather report for a city.',
arguments=[
types.PromptArgument(name='city', description='City name', required=True)
]
)
]
@app.get_prompt()
async def get_prompt(name: str, arguments: dict) -> types.GetPromptResult:
if name == 'weather-report':
city = arguments.get('city', 'London')
return types.GetPromptResult(
description=f'Weather report for {city}',
messages=[
types.PromptMessage(
role='user',
content=types.TextContent(
type='text',
text=f'Use the get_weather tool to look up {city} and give me a detailed weather report including what clothing I should wear.'
)
)
]
)
raise ValueError(f'Unknown prompt: {name}')Claude Desktopへの接続
Claude DesktopでMCPサーバーを使用するには、Claude Desktopの設定ファイルに追加します。macOSでは、ファイルは~/Library/Application Support/Claude/claude_desktop_config.jsonにあります。サーバーの起動コマンドと、サーバーが必要とする環境変数を指定します。
# ~/Library/Application Support/Claude/claude_desktop_config.json
# Add this JSON configuration:
# {
# "mcpServers": {
# "weather-server": {
# "command": "/path/to/venv/bin/python",
# "args": ["/path/to/my_mcp_server/server.py"],
# "env": {
# "WEATHER_API_KEY": "your_api_key_here"
# }
# }
# }
# }
# After saving, restart Claude Desktop.
# Your server's tools will appear in Claude's tool list.MCP CLIでサーバーをテストする
Claude Desktopに接続する前に、MCPインスペクターまたはCLIツールを使ってサーバーをテストします。mcp devコマンドを実行すると、サーバーが起動し、ブラウザーベースのインスペクターが開きます。ここではツールを手動で呼び出し、プロトコルの生メッセージを確認できるため、問題を簡単にデバッグできます。
# Install the MCP development tools
# pip install 'mcp[cli]'
# Start the inspector with your server
# mcp dev server.py
# The inspector opens at http://localhost:5173
# You can:
# - See all registered tools and their schemas
# - Call tools with custom arguments
# - Browse available resources
# - Test prompt templates
# - View the full JSON-RPC message exchangeMCPサーバーのエラーハンドリング
MCPサーバーは、不正な入力を受け取っても決してクラッシュしてはいけません。すべてのツール実行をtry/exceptで囲み、例外を送出するのではなく、TextContentとしてエラーメッセージを返します。致命的なエラー(設定の問題やAPIキーの不足など)は起動時にログへ記録し、サーバーがメインループに入る前に例外を送出します。
@app.call_tool()
async def call_tool_safe(name: str, arguments: dict) -> list[types.TextContent]:
try:
if name == 'get_weather':
city = arguments.get('city')
if not city:
return [types.TextContent(type='text', text='Error: city argument is required.')]
result = await fetch_weather(city, arguments.get('units', 'metric'))
return [types.TextContent(type='text', text=result)]
raise ValueError(f'Unknown tool: {name}')
except httpx.TimeoutException:
return [types.TextContent(type='text', text='Error: Weather API timed out. Try again.')]
except Exception as e:
return [types.TextContent(type='text', text=f'Error: {str(e)}')]MCPサーバーのロギング
MCPサーバーはクライアントとstdio経由で通信するため、print文を使うとプロトコルが壊れます。ログには必ずstderrを使用してください。stderrはMCPのメッセージ交換に干渉しない別のストリームです。Pythonのloggingモジュールを設定して、stderrに書き込むようにします。
import logging
import sys
# Configure logging to stderr (NOT stdout — that's the MCP channel)
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [%(levelname)s] %(message)s',
stream=sys.stderr
)
logger = logging.getLogger('weather-server')
# In your tool handler:
# logger.info(f'Getting weather for {city}')
# logger.error(f'API call failed: {e}')
# Claude Desktop captures stderr to a log file:
# ~/Library/Logs/Claude/mcp-server-weather-server.logMCPサーバーのパッケージ化
pyproject.tomlを使ってMCPサーバーをパッケージ化しPyPIに公開するか、チーム向けにDockerコンテナとして配布します。APIキーやデータベースURLなど、すべてのシークレットには環境変数を使用し、サーバーの設定をコードから分離します。必要な環境変数と設定例を、わかりやすいREADMEに記載してください。
理解度チェック
PythonでMCPサーバーを構築する方法について、理解度を確認します。
レッスンのまとめ
このレッスンでは、MCPサーバーが@ app.list_tools()と@app.call_tool()デコレーターを介してツールを公開すること、リソースとプロンプトによって、読み取り可能なデータと再利用可能なテンプレートをサーバーに追加できること、そしてMCPのstdioプロトコルチャネルを壊さないために、ロギングにはstderrを使う必要があることを学びました。次は、MCPサーバーをデータベースに接続し、ページネーション対応の動的なリソースを公開します。
よくある質問
「初めてのMCPサーバーを構築する」レッスンは無料ですか?
はい。「初めてのMCPサーバーを構築する」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、AI Engineering Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 AI Engineering Academyコースには全4レッスンが含まれています。
「初めてのMCPサーバーを構築する」で何を学びますか?
Python MCP SDKを使ってリソース、ツール、プロンプトを公開するサーバーを作成し、Claude Desktopに接続して一連の動作を確認します。 ブラウザで直接実行するハンズオンコードでAI Engineering Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
AI Engineering Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのAI Engineering Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。
「初めてのMCPサーバーを構築する」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このAI Engineering Academyレッスンでコードを書いて実行できますか?
はい。すべてのAI Engineering Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- MCPとは何か、なぜ重要なのか
- 初めてのMCPサーバーを構築する
- MCPでデータベースリソースを公開する
- MCPのセキュリティと認証