初めまして。エンタープライズ第一本部、2年目の佐藤悠です。
最近話題のMCPサーバーを構築して、外部情報との連携をするという挑戦をします。
既出の内容かつ不正確な部分もあるかもしれませんが、私の個人的な検証記録として残しておきたいという目的でこの記事を作成します。
想定読者はジュニアレベルのエンジニアがMCPサーバーとは何か?どのように構築するか?を実例と共に知りたいという際に読むのがちょうど良いと思っています。
目次
MCPとは?
提供元のAnthropicにおける説明[1]は以下の通りです。
The Model Context Protocol is an open standard that enables developers to build secure, two-way connections between their data sources and AI-powered tools.
私の理解ではAI Agentが外部情報を取得する際の接続部の話だと思っています。
つまりは、MCPサーバーを用いることでAI Agentと外部データリソース間での安全なコネクタの規格整備をしたということです。
統一規格が提供されているのでAI Agentに手軽に機能を載せたり、精度を向上させたりできるようになり、MCPを中心にエコシステムが盛り上がりを見せているという現状の分析をしています。
2025年3月27日にはChatGPTのAgent SDKにもMCPをサポートするといったような旨の発表がありました。
このように異なるAI Agentで様々なデータソースを利用できるようになると、自由度や拡張性が高まりAI Agentの発展につながると感じますし、この潮流に乗りたいとも感じさせます。
以上がMCPの概観で、本記事を作成するモチベーションです。
アーキテクチャ概要
本記事では、はじめに公式が提供しているQuickStart[2]と全く同じ構成でサーバーを作成してみました。
簡単な構成図は以下のとおりです。

ClaudeDesktopのインストールとサインインをあらかじめしておきます。
このアプリからMCPサーバーにアクセスし、天気の情報を取得するような構成です。
具体的には以下のツールをMCPサーバーに配置します。
- get-alerts
- 指定された地域で現在出ている警報を取得する
- get-forecast
- 指定された地点の天気予報を取得する
私の環境はWindowsなので、参考資料のその手順に従い実行したコマンドを載せています。
以下のコマンドをPowerShellで実行します。
uvをインストールし、Pythonプロジェクトを用意します。
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
次に作業ディレクトリを作成し、移動。
仮想環境を作成して依存関係をインストール。
最後にPythonのファイルを作成するコマンドを実行します。
# Create a new directory for our project uv init weather cd weather # Create virtual environment and activate it uv venv .venv\Scripts\activate # Install dependencies uv add mcp[cli] httpx # Create our server file new-item weather.py
以降の作業は、ここで作成したweather.pyを編集します。
ファイルの先頭に以下のコードを追加。
from typing import Any import httpx from mcp.server.fastmcp import FastMCP # Initialize FastMCP server mcp = FastMCP("weather") # Constants NWS_API_BASE = "https://api.weather.gov" USER_AGENT = "weather-app/1.0"
The FastMCP class uses Python type hints and docstrings to automatically generate tool definitions, making it easy to create and maintain MCP tools.
ここの注釈にPyhtonの型ヒントとドキュメントで、このツールを定義しますとあります。
この部分の理解は実装後にできました。最後に解説します。
次に外部情報を取得する部分を実装しています。
#これがリクエスト処理 async def make_nws_request(url: str) -> dict[str, Any] | None: """Make a request to the NWS API with proper error handling.""" headers = { "User-Agent": USER_AGENT, "Accept": "application/geo+json" } async with httpx.AsyncClient() as client: try: response = await client.get(url, headers=headers, timeout=30.0) response.raise_for_status() return response.json() except Exception: return None #これがフォーマッタ def format_alert(feature: dict) -> str: """Format an alert feature into a readable string.""" props = feature["properties"] return f""" Event: {props.get('event', 'Unknown')} Area: {props.get('areaDesc', 'Unknown')} Severity: {props.get('severity', 'Unknown')} Description: {props.get('description', 'No description available')} Instructions: {props.get('instruction', 'No specific instructions provided')} """
これは以下のツール内の処理で外部に情報を取得しにいく際と取得したデータ整形に使用しています。
get_alertsはUS state codeを期待しています。
これをURLのパスに加えることで、その地域の情報をAPIで取得できるという寸法です。
get_forecastも緯度・経度で同様の処理をリクエストしていますね。
@mcp.tool() async def get_alerts(state: str) -> str: """Get weather alerts for a US state. Args: state: Two-letter US state code (e.g. CA, NY) """ url = f"{NWS_API_BASE}/alerts/active/area/{state}" data = await make_nws_request(url) if not data or "features" not in data: return "Unable to fetch alerts or no alerts found." if not data["features"]: return "No active alerts for this state." alerts = [format_alert(feature) for feature in data["features"]] return "\n---\n".join(alerts) @mcp.tool() async def get_forecast(latitude: float, longitude: float) -> str: """Get weather forecast for a location. Args: latitude: Latitude of the location longitude: Longitude of the location """ # First get the forecast grid endpoint points_url = f"{NWS_API_BASE}/points/{latitude},{longitude}" points_data = await make_nws_request(points_url) if not points_data: return "Unable to fetch forecast data for this location." # Get the forecast URL from the points response forecast_url = points_data["properties"]["forecast"] forecast_data = await make_nws_request(forecast_url) if not forecast_data: return "Unable to fetch detailed forecast." # Format the periods into a readable forecast periods = forecast_data["properties"]["periods"] forecasts = [] for period in periods[:5]: # Only show next 5 periods forecast = f""" {period['name']}: Temperature: {period['temperature']}°{period['temperatureUnit']} Wind: {period['windSpeed']} {period['windDirection']} Forecast: {period['detailedForecast']} """ forecasts.append(forecast) return "\n---\n".join(forecasts)
最後にサーバーの初期化と実行を記述します。
この部分の意味は直接スクリプトが実行された際に、標準入出力を通信手段に使うという意味です。
if __name__ == "__main__": # Initialize and run the server mcp.run(transport='stdio')
これでMCPサーバーの準備は終わりました。
次にClaudeがこのサーバーにアクセスできるようにします。
code $env:AppData\Claude\claude_desktop_config.json
以上のコードをPowerShellで実行します。これは単純にClaude Desktopの設定ファイルを書くためにVScodeを開くだけのものです。
開いたら以下の設定を記述。
{
"mcpServers": {
"weather": {
"command": "uv",
"args": [
"--directory",
//ここは実際のweather.pyまでの絶対パスを記述する
"/ABSOLUTE/PATH/TO/PARENT/FOLDER/weather",
"run",
"weather.py"
]
}
}
}
さっきまで編集していたweather.pyを実行するようにClaude側に指示しています。
これで標準入出力を待つサーバーが立ち、回答のための情報をどのツールで取得するかを選ぶ準備がClaudeでもできました。
それではテストです!
テスト実行
以下の画像の赤丸で囲った部分のハンマーアイコンがMCPサーバーに配置されたツールの数を示しています。
手順に沿っていれば2個のはずです。私は追加したので3の表示です。

私は実装直後はここで変更が反映されていませんでした。
以下のスクショのように、一度左上のメニューからファイル→終了(または表示→再読み込み)で戻ってくると反映されていました。

またメニューから開発者の機能を有効にするとMCPサーバーのログを見ることができるようになります。
他の原因で動作しなかった場合は、このログを参考に修正を加えると良いと思います。
反映されたら、ハンマーアイコンをクリックすると使用できるツールが表示されます。
では、ようやくですが参考資料に従って以下のプロンプトでテストを実行します。
- What’s the weather in Sacramento?
- What are the active weather alerts in Texas?
まずは一つ目。日本語でも指示が可能かも含めて検証しました。
- What’s the weather in Sacramento?
ツールを使用していいかの許可を聞くポップアップが出ます。

おお!無事サクラメントの天気を取得できました。

次に二つ目。ちゃんと英語でも検証します。
- What are the active weather alerts in Texas?
同様にツール実行のポップアップが出て、回答が生成されました。

何気なく使用していますが、ツールの引数に緯度・経度/US state codeを求めているのに回答生成できるの凄いですね!LLMが情報補完をしているのでしょう。
追加の検証
さらにツールを作ってみる実験をしました。
コードは以下のとおりです。
@mcp.tool() def add(a:int,b:int) -> int: """例の処理といえばこれです. 引数: 数字:文章中の二つの数字 """ return a + b
今までの書き方を参考に、処理を追加しました!
「例の処理」のように背景情報が必要なものはLLMは通常回答できません。

このツールの追加で足し算が実行出来たら、追加の検証は成功です。

出来ました!足し算はLLMでも回答できますが、「例の処理」の理解はできないですからね!
ちゃんとツール実行許可のポップアップも出現しました。
ここの注釈にPyhtonの型ヒントとドキュメントで、このツールを定義しますとあります。
この部分の理解は実装後にできました。最後に解説します。
最後に追加実装部分を例に上記の内容を解説します。
関数宣言の後に以下のテキストを追加していますね。
"""例の処理といえばこれです. 引数: 数字:文章中の二つの数字 """
ここでしか、「例の処理」は記述していません。
つまり、関数の説明をテキストですれば、ツールを選択可能な状態にできるということですね。
型ヒントの部分もdef add(a:int,b:int) -> int:のように記述することで、図中のプロンプトの後半、意味不明な数値の並び例の処理を頼む 10 20を解釈できていそうです。
検証は以上です。
まとめ
MCPがなぜ盛り上がっているのかを実装してみて理解しました。
今のうちに詳しくなっておきたいですね!
参考文献
[1] https://www.anthropic.com/news/model-context-protocol
[2] https://modelcontextprotocol.io/quickstart/server#installing-prerequisites-macos
執筆:@sato.yu、レビュー:@miyazawa.hibiki
(Shodoで執筆されました)



