このチュートリアルでは、FastAPI と MCP Python SDK を使用してタスク管理ツールを公開するモデル コンテキスト プロトコル ( MCP) サーバーを構築します。 サーバーを Azure Container Apps にデプロイし、VS Code の GitHub Copilot Chat からサーバーに接続します。
このチュートリアルでは、次の操作を行います。
- MCP ツールを公開する FastAPI アプリを作成する
- GitHub Copilot を使用して MCP サーバーをローカルでテストする
- アプリをコンテナー化して Azure Container Apps にデプロイする
- デプロイされた MCP サーバーに GitHub Copilot を接続する
[前提条件]
- アクティブなサブスクリプションを持つ Azure アカウント。 無料で作成できます。
- Azure CLI バージョン 2.62.0 以降。
- Python 3.10 以降。
- GitHub Copilot 拡張機能を含む Visual Studio Code。
- Docker Desktop (省略可能 - コンテナーをローカルでテストするためにのみ必要)。
アプリの雛形を作成する
このセクションでは、FastAPI と MCP Python SDK を使用して新しい Python プロジェクトを作成します。
プロジェクト ディレクトリを作成し、仮想環境を設定します。
mkdir tasks-mcp-server && cd tasks-mcp-server python -m venv .venv source .venv/bin/activaterequirements.txtを作成します。fastapi>=0.115.0 uvicorn>=0.30.0 mcp[cli]>=1.2.0依存関係をインストールします。
pip install -r requirements.txtインメモリ データ ストアの
task_store.pyを作成します。from dataclasses import dataclass, field from datetime import datetime, timezone @dataclass class TaskItem: id: int title: str description: str is_complete: bool = False created_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc)) def to_dict(self) -> dict: return { "id": self.id, "title": self.title, "description": self.description, "is_complete": self.is_complete, "created_at": self.created_at.isoformat(), } class TaskStore: def __init__(self): self._tasks: list[TaskItem] = [ TaskItem(1, "Buy groceries", "Milk, eggs, bread"), TaskItem(2, "Write docs", "Draft the MCP tutorial", True), ] self._next_id = 3 def get_all(self) -> list[dict]: return [t.to_dict() for t in self._tasks] def get_by_id(self, task_id: int) -> dict | None: task = next((t for t in self._tasks if t.id == task_id), None) return task.to_dict() if task else None def create(self, title: str, description: str) -> dict: task = TaskItem(self._next_id, title, description) self._next_id += 1 self._tasks.append(task) return task.to_dict() def toggle_complete(self, task_id: int) -> dict | None: task = next((t for t in self._tasks if t.id == task_id), None) if task is None: return None task.is_complete = not task.is_complete return task.to_dict() def delete(self, task_id: int) -> bool: task = next((t for t in self._tasks if t.id == task_id), None) if task is None: return False self._tasks.remove(task) return True # For demonstration only — not thread-safe. store = TaskStore()TaskItemデータクラスは、シリアル化のためのto_dict()メソッドを使用してデータ モデルを定義します。TaskStoreクラスは、サンプル データが事前に設定されたメモリ内リストを管理し、CRUD メソッドを提供します。 シンプルにするために、シングルトンstoreモジュール レベルがアプリケーション全体で共有されます。
MCP ツールを定義する
このセクションでは、AI モデルが MCP サーバーを呼び出して FastAPI アプリケーションにマウントできる MCP ツールを定義します。
mcp_server.pyを作成します。from mcp.server.fastmcp import FastMCP from task_store import store mcp = FastMCP("TasksMCP", stateless_http=True) @mcp.tool() async def list_tasks() -> list[dict]: """List all tasks with their ID, title, description, and completion status.""" return store.get_all() @mcp.tool() async def get_task(task_id: int) -> dict | None: """Get a single task by its numeric ID. Args: task_id: The numeric ID of the task to retrieve. """ return store.get_by_id(task_id) @mcp.tool() async def create_task(title: str, description: str) -> dict: """Create a new task with the given title and description. Returns the created task. Args: title: A short title for the task. description: A detailed description of what the task involves. """ return store.create(title, description) @mcp.tool() async def toggle_task_complete(task_id: int) -> str: """Toggle a task's completion status between complete and incomplete. Args: task_id: The numeric ID of the task to toggle. """ task = store.toggle_complete(task_id) if task: status = "complete" if task["is_complete"] else "incomplete" return f"Task {task['id']} is now {status}." return f"Task with ID {task_id} not found." @mcp.tool() async def delete_task(task_id: int) -> str: """Delete a task by its numeric ID. Args: task_id: The numeric ID of the task to delete. """ if store.delete(task_id): return f"Task {task_id} deleted." return f"Task with ID {task_id} not found."重要なポイント:
-
FastMCP("TasksMCP", stateless_http=True)は、Python SDK のステートレス HTTP パターンを使用して MCP サーバーを作成します。 ストリーミング可能な HTTP エンドポイントは、既定で/mcpサブパスに設定されます。 - 各
@mcp.tool()関数は、呼び出し可能なツールになります。 関数 docstring とパラメーター注釈は、AI モデルが各ツールの使用方法を理解するのに役立ちます。
-
app.pyを作成します。 このファイルは、MCP サーバーをマウントする FastAPI アプリケーションを定義します。from contextlib import AsyncExitStack, asynccontextmanager from fastapi import FastAPI from fastapi.responses import JSONResponse from mcp_server import mcp @asynccontextmanager async def lifespan(app: FastAPI): async with AsyncExitStack() as stack: await stack.enter_async_context(mcp.session_manager.run()) yield app = FastAPI(lifespan=lifespan) app.mount("/", mcp.streamable_http_app()) @app.get("/health") async def health(): return JSONResponse({"status": "healthy"})MCP サーバー アプリはルート (
/) にマウントされます。 SDK のストリーミング可能な HTTP エンドポイントは既定で/mcpされるため、完全なエンドポイント パスは/mcp。Container Apps の正常性プローブには、個別の
/healthエンドポイントが使用されます。 MCP エンドポイントは、JSON-RPC POST 要求を想定しており、正常性チェックとしては適していません。
MCP サーバーをローカルでテストする
Azure にデプロイする前に、MCP サーバーがローカルで実行され、GitHub Copilot から接続されて動作することを確認します。
アプリケーションを起動します。
uvicorn app:app --reload --port 8080VS Code を開き、 Copilot チャット を開き、[ エージェント モード] を選択します。
[ ツール ] ボタンを選択し、[ その他のツールの追加] を選択します。..>MCP サーバーを追加します。
HTTP (HTTP または Server-Sent イベント) を選択します。
サーバーの URL を入力します。
http://localhost:8080/mcpサーバー ID を入力します。
tasks-mcp[ ワークスペースの設定] を選択します。
新しい Copilot チャット プロンプトで、「すべてのタスクを表示する」と入力します。
MCPツールの確認をCopilotが求めた場合は、[続行]を選択します。
メモリ内ストアから返されたタスク リストが表示されます。
ヒント
「PR を確認するタスクを作成する」、「タスク 1 を完了としてマークする」、「タスク 2 を削除する」などの他のプロンプトを試してください。
アプリケーションのコンテナー格納
Azure にデプロイする前にローカルでテストできるように、アプリケーションを Docker コンテナーとしてパッケージ化します。
Dockerfileを作成します。FROM python:3.12-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . EXPOSE 8080 CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8080"]Dockerfile は Python 3.12 のスリムな基本イメージを使用し、
requirements.txtから依存関係をインストールしてから、アプリケーション コードをコピーします。 Uvicorn は、ポート 8080 で FastAPI アプリを提供します。コンテナーがビルドされ、ローカルで実行されていることを確認します。
docker build -t tasks-mcp-server . docker run -p 8080:8080 tasks-mcp-serverヘルスエンドポイントが応答することを確認してください。
curl http://localhost:8080/health
Azure Container Apps へのデプロイ
アプリケーションをコンテナー化したら、Azure CLI を使用して Azure Container Apps にデプロイします。
az containerapp up コマンドはクラウドにコンテナー イメージをビルドするため、この手順ではマシン上に Docker は必要ありません。
環境変数を設定します。
RESOURCE_GROUP="mcp-tutorial-rg" LOCATION="eastus" ENVIRONMENT_NAME="mcp-env" APP_NAME="tasks-mcp-server-py"リソース グループと Container Apps 環境を作成します。
az group create --name $RESOURCE_GROUP --location $LOCATION az containerapp env create \ --name $ENVIRONMENT_NAME \ --resource-group $RESOURCE_GROUP \ --location $LOCATIONコンテナー アプリをデプロイします。
az containerapp up \ --name $APP_NAME \ --resource-group $RESOURCE_GROUP \ --environment $ENVIRONMENT_NAME \ --source . \ --ingress external \ --target-port 8080CORS を構成します。
az containerapp ingress cors enable \ --name $APP_NAME \ --resource-group $RESOURCE_GROUP \ --allowed-origins "*" \ --allowed-methods "GET,POST,DELETE,OPTIONS" \ --allowed-headers "*"注
運用環境では、ワイルドカードの配信元を特定の信頼できるオリジンに置き換えます。 Container Apps での MCP サーバーのセキュリティ保護に関する情報を参照してください。
デプロイを検証します。
APP_URL=$(az containerapp show \ --name $APP_NAME \ --resource-group $RESOURCE_GROUP \ --query "properties.configuration.ingress.fqdn" -o tsv) curl https://$APP_URL/health
デプロイされたサーバーに GitHub Copilot を接続する
MCP サーバーが Azure で実行されたので、デプロイされたエンドポイントに GitHub Copilot を接続するように VS Code を構成します。
.vscode/mcp.jsonを作成または更新します。{ "servers": { "tasks-mcp-server": { "type": "http", "url": "https://<your-app-fqdn>/mcp" } } }<your-app-fqdn>をデプロイ出力の FQDN に置き換えます。VS Code で、エージェント モードで Copilot チャットを開きます。
tasks-mcp-serverが [ツール] リストに表示されていることを確認します。 必要に応じて [開始] を選択します 。"ステージング環境をデプロイするタスクを作成する" などのプロンプトでテストします。
対話型使用のスケーリングを構成する
既定では、Azure Container Apps は 0 個のレプリカにスケーリングできます。 Copilot などの対話型クライアントにサービスを提供する MCP サーバーの場合、コールド スタートは顕著な遅延を引き起こします。 少なくとも 1 つのインスタンスを実行し続けるために、レプリカの最小数を設定します。
az containerapp update \
--name $APP_NAME \
--resource-group $RESOURCE_GROUP \
--min-replicas 1
セキュリティに関する考慮事項
このチュートリアルでは、簡単にするために認証されていない MCP サーバーを使用します。 運用環境で MCP サーバーを実行する前に、次の推奨事項を確認してください。 大規模言語モデル (LLM) を利用するエージェントが MCP サーバーを呼び出す場合は、 迅速なインジェクション 攻撃に注意してください。
- 認証と承認: Microsoft Entra ID を使用して MCP サーバーをセキュリティで保護します。 Container Apps での MCP サーバーのセキュリティ保護に関する情報を参照してください。
- 入力検証: 常にツール パラメーターを検証します。 Pydantic を使用して、ツール入力にデータ検証を適用します。
- HTTPS: Azure Container Apps では、自動 TLS 証明書を使用して既定で HTTPS が適用されます。
- 最小権限: ユース ケースで必要なツールのみを公開します。 確認なしで破壊的な操作を実行するツールは避けてください。
- CORS: 許可された配信元を運用環境の信頼されたドメインに制限します。
- ログ記録と監視: MCP ツールの呼び出しを監査用にログに記録します。 Azure Monitor と Log Analytics を使用します。
リソースをクリーンアップする
このアプリケーションを引き続き使用する予定がない場合は、リソース グループを削除して、このチュートリアルで作成したすべてのリソースを削除します。
az group delete --resource-group $RESOURCE_GROUP --yes --no-wait