Zelfstudie: Een Python MCP-server implementeren in Azure Container Apps

In deze zelfstudie bouwt u een MCP-server (Model Context Protocol) die hulpprogramma's voor taakbeheer beschikbaar maakt met behulp van FastAPI en de MCP Python SDK. U implementeert de server in Azure Container Apps en maakt er verbinding mee vanuit GitHub Copilot Chat in VS Code.

In deze handleiding leert u:

  • Een FastAPI-app maken die MCP-hulpprogramma's beschikbaar maakt
  • De MCP-server lokaal testen met GitHub Copilot
  • De app containeriseren en implementeren in Azure Container Apps
  • GitHub Copilot verbinden met de geïmplementeerde MCP-server

Vereiste voorwaarden

De scaffold van de app maken

In deze sectie maakt u een nieuw Python-project met FastAPI en de MCP Python SDK.

  1. Maak de projectmap en stel een virtuele omgeving in:

    mkdir tasks-mcp-server && cd tasks-mcp-server
    python -m venv .venv
    source .venv/bin/activate
    
  2. Maken requirements.txt:

    fastapi>=0.115.0
    uvicorn>=0.30.0
    mcp[cli]>=1.2.0
    
  3. Afhankelijkheden installeren:

    pip install -r requirements.txt
    
  4. Maak task_store.py voor de gegevensopslag in het geheugen aan.

    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()
    

    De TaskItem dataclass definieert het gegevensmodel met een to_dict() methode voor serialisatie. De TaskStore klasse beheert een in-memory lijst die vooraf is ingevuld met voorbeeldgegevens en biedt CRUD-methoden. De singleton op moduleniveau store wordt voor het gemak gedeeld in de toepassing.

Definieer de MCP-hulpprogramma's

In deze sectie definieert u de MCP-hulpprogramma's die het AI-model kan aanroepen en koppelen aan de MCP-server in uw FastAPI-toepassing.

  1. Maken 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."
    

    Belangrijke punten:

    • FastMCP("TasksMCP", stateless_http=True) maakt een MCP-server met behulp van het stateless HTTP-patroon in de Python SDK. Het streambare HTTP-eindpunt wordt standaard ingesteld op het /mcp subpad.
    • Elke @mcp.tool() functie wordt een aanroepbaar hulpmiddel. De functie-documentatie en parameteraantekeningen helpen het AI-model te begrijpen hoe elk hulpprogramma moet worden gebruikt.
  2. Maken app.py. Dit bestand definieert de FastAPI-toepassing die de MCP-server koppelt:

    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"})
    

    De MCP-server-app wordt gemount op de hoofdmap (/). Het streambare HTTP-eindpunt van de SDK is standaard ingesteld op /mcp, dus het volledige eindpuntpad is /mcp.

    Er wordt een afzonderlijk /health eindpunt gebruikt voor gezondheidscontroles van Container Apps. MCP-eindpunten verwachten JSON-RPC POST-aanvragen en zijn niet geschikt als statuscontroles.

De MCP-server lokaal testen

Voordat u implementeert in Azure, controleert u of de MCP-server werkt door deze lokaal uit te voeren en verbinding te maken vanuit GitHub Copilot.

  1. Start de toepassing:

    uvicorn app:app --reload --port 8080
    
  2. Open VS Code en open Copilot Chat en selecteer Agentmodus.

  3. Selecteer de knop Extra en selecteer vervolgens Meer hulpprogramma's toevoegen...>MCP-server toevoegen.

  4. Selecteer HTTP (HTTP of Server-Sent-gebeurtenissen).

  5. Voer de server-URL in: http://localhost:8080/mcp

  6. Voer een server-id in: tasks-mcp

  7. Selecteer Werkruimte-instellingen.

  8. Typ in een nieuwe Copilot-chatprompt: 'Toon mij alle taken'

  9. Selecteer Doorgaan wanneer Copilot vraagt om bevestiging van het MCP-hulpprogramma.

U zou de takenlijst moeten zien die wordt geretourneerd uit uw in-memory opslag.

Aanbeveling

Probeer andere prompts zoals 'Creëer een taak om de PR te controleren', 'Taak 1 markeren als voltooid' of 'Taak 2 verwijderen'.

De toepassing in een container plaatsen

Verpak de toepassing als een Docker-container, zodat u deze lokaal kunt testen voordat u implementeert in Azure.

  1. Maak een 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 maakt gebruik van een slanke basisimage van Python 3.12, installeert afhankelijkheden van requirements.txt, en kopieert vervolgens de toepassingscode. Uvicorn dient de FastAPI applicatie op poort 8080.

  2. Controleer of de container lokaal wordt gebouwd en draait.

    docker build -t tasks-mcp-server .
    docker run -p 8080:8080 tasks-mcp-server
    

    Controleer of het gezondheidseindpunt reageert: curl http://localhost:8080/health

Implementeren in Azure Container Apps

Nadat u de toepassing in een container hebt geplaatst, implementeert u deze in Azure Container Apps met behulp van de Azure CLI. De opdracht az containerapp up bouwt de containerafbeelding in de cloud, zodat u Docker niet nodig hebt op uw machine voor deze stap.

  1. Omgevingsvariabelen instellen:

    RESOURCE_GROUP="mcp-tutorial-rg"
    LOCATION="eastus"
    ENVIRONMENT_NAME="mcp-env"
    APP_NAME="tasks-mcp-server-py"
    
  2. Maak een resourcegroep en een Container Apps-omgeving:

    az group create --name $RESOURCE_GROUP --location $LOCATION
    
    az containerapp env create \
        --name $ENVIRONMENT_NAME \
        --resource-group $RESOURCE_GROUP \
        --location $LOCATION
    
  3. De container-app implementeren:

    az containerapp up \
        --name $APP_NAME \
        --resource-group $RESOURCE_GROUP \
        --environment $ENVIRONMENT_NAME \
        --source . \
        --ingress external \
        --target-port 8080
    
  4. CORS configureren:

    az containerapp ingress cors enable \
        --name $APP_NAME \
        --resource-group $RESOURCE_GROUP \
        --allowed-origins "*" \
        --allowed-methods "GET,POST,DELETE,OPTIONS" \
        --allowed-headers "*"
    

    Opmerking

    Vervang wildcards voor productie door specifieke vertrouwde origins. Zie Beveiligde MCP-servers in Container Apps.

  5. Controleer de implementatie:

    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 verbinden met de geïmplementeerde server

Nu de MCP-server wordt uitgevoerd in Azure, configureert u VS Code om GitHub Copilot te verbinden met het geïmplementeerde eindpunt.

  1. Maken of bijwerken .vscode/mcp.json:

    {
        "servers": {
            "tasks-mcp-server": {
                "type": "http",
                "url": "https://<your-app-fqdn>/mcp"
            }
        }
    }
    

    Vervang <your-app-fqdn> door de FQDN uit de implementatie-uitvoer.

  2. Open Copilot Chat in Agent modus in VS Code.

  3. Controleer of tasks-mcp-server wordt weergegeven in de Hulpmiddelenlijst. Selecteer indien nodig Start.

  4. Test met een prompt zoals 'Een taak maken om de faseringsomgeving te implementeren'.

Configureer schaling voor interactief gebruik

Azure Container Apps kan standaard worden geschaald naar nul replica's. Voor MCP-servers die interactieve clients zoals Copilot bedienen, leidt koud starten tot merkbare vertragingen. Stel een minimumaantal replica's in om ten minste één exemplaar actief te houden:

az containerapp update \
    --name $APP_NAME \
    --resource-group $RESOURCE_GROUP \
    --min-replicas 1

Beveiligingsoverwegingen

In deze zelfstudie wordt gebruikgemaakt van een niet-geverifieerde MCP-server voor het gemak. Voordat u een MCP-server in productie draait, bekijk de volgende aanbevelingen. Wanneer een agent aangedreven door grote taalmodellen (LLM's) uw MCP-server aanroept, houd rekening met promptinjectie-aanvallen.

  • Verificatie en autorisatie: beveilig uw MCP-server met Microsoft Entra-id. Zie Beveiligde MCP-servers in Container Apps.
  • Invoervalidatie: Altijd hulpprogrammaparameters valideren. Gebruik Pydantic om gegevensvalidatie af te dwingen bij invoer van hulpprogramma's.
  • HTTPS: Azure Container Apps dwingt HTTPS standaard af met automatische TLS-certificaten.
  • Minimale bevoegdheid: alleen de hulpprogramma's beschikbaar maken die uw use-case vereist. Vermijd hulpprogramma's die destructieve bewerkingen uitvoeren zonder bevestiging.
  • CORS: Toegestane oorsprongen beperken tot vertrouwde domeinen in productie.
  • Logboekregistratie en bewaking: MCP-hulpprogramma-aanroepen loggen voor auditing. Gebruik Azure Monitor en Log Analytics.

De hulpbronnen opschonen

Als u niet van plan bent om deze toepassing te blijven gebruiken, verwijdert u de resourcegroep om alle resources te verwijderen die u in deze zelfstudie hebt gemaakt:

az group delete --resource-group $RESOURCE_GROUP --yes --no-wait

Volgende stap