Tutorial: Menyebarkan server Python MCP ke Azure Container Apps

Dalam tutorial ini, Anda membangun server Protokol Konteks Model (MCP) yang mengekspos alat manajemen tugas dengan menggunakan FastAPI dan MCP Python SDK. Anda menyebarkan server ke Azure Container Apps dan menghubungkannya dari GitHub Copilot Chat di VS Code.

Di tutorial ini, Anda akan:

  • Membuat aplikasi FastAPI yang mengekspos alat MCP
  • Menguji server MCP secara lokal dengan GitHub Copilot
  • Kontainerisasi dan sebarkan aplikasi ke Azure Container Apps
  • Menyambungkan GitHub Copilot ke server MCP yang disebarkan

Prasyarat

Membuat perancah aplikasi

Di bagian ini, Anda membuat proyek Python baru dengan FastAPI dan MCP Python SDK.

  1. Buat direktori proyek dan siapkan lingkungan virtual:

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

    fastapi>=0.115.0
    uvicorn>=0.30.0
    mcp[cli]>=1.2.0
    
  3. Instal dependensi:

    pip install -r requirements.txt
    
  4. Buat task_store.py untuk penyimpanan data dalam memori:

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

    Klasifikasi TaskItem data menentukan model data dengan to_dict() metode untuk serialisasi. Kelas TaskStore mengelola daftar dalam memori yang telah diisi sebelumnya dengan data sampel dan menyediakan metode CRUD. Singleton level modul store ini dibagikan di seluruh aplikasi untuk memudahkan pengelolaan atau penggunaan.

Tentukan alat MCP

Di bagian ini, Anda menentukan alat MCP yang dapat dipanggil model AI dan memasang server MCP di aplikasi FastAPI Anda.

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

    Poin utama:

    • FastMCP("TasksMCP", stateless_http=True) membuat server MCP menggunakan pola HTTP stateless di Python SDK. Titik akhir HTTP yang dapat dialirkan mengarah secara default ke subjalur /mcp.
    • Setiap @mcp.tool() fungsi menjadi alat yang dapat dipanggil. Fungsi docstring dan anotasi parameter membantu model AI memahami cara menggunakan setiap alat.
  2. Buat app.py. File ini mendefinisikan aplikasi FastAPI yang memasang server MCP:

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

    Aplikasi server MCP dipasang di root (/). Titik akhir HTTP SDK yang dapat dialirkan default ke /mcp, sehingga jalur titik akhir lengkap adalah /mcp.

    Titik akhir terpisah /health digunakan untuk pemeriksaan kesehatan Container Apps. Titik akhir MCP mengharapkan permintaan JSON-RPC POST dan tidak cocok sebagai pemeriksaan kesehatan.

Menguji server MCP secara lokal

Sebelum menyebarkan ke Azure, verifikasi server MCP berfungsi dengan menjalankannya secara lokal dan menyambungkan dari GitHub Copilot.

  1. Mulai aplikasi:

    uvicorn app:app --reload --port 8080
    
  2. Buka Visual Studio Code, lalu buka Obrolan Copilot dan pilih Mode agen .

  3. Pilih tombol Alat , lalu pilih Tambahkan Alat Lainnya...>Tambahkan Server MCP.

  4. Pilih HTTP (HTTP atau Event Server-Sent).

  5. Masukkan URL server: http://localhost:8080/mcp

  6. Masukkan ID server: tasks-mcp

  7. Pilih Pengaturan Ruang Kerja.

  8. Dalam permintaan Obrolan Copilot baru, ketik: "Tampilkan saya semua tugas"

  9. Pilih Lanjutkan saat Copilot meminta konfirmasi alat MCP.

Anda akan melihat daftar tugas yang dikembalikan dari penyimpanan dalam memori Anda.

Petunjuk / Saran

Coba perintah lain seperti "Buat tugas untuk meninjau PR", "Tandai tugas 1 sebagai selesai", atau "Hapus tugas 2".

Menerapkan kontainer pada aplikasi

Kemas aplikasi sebagai kontainer Docker sehingga Anda dapat mengujinya secara lokal sebelum menyebarkan ke Azure.

  1. Buat 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 menggunakan gambar dasar ramping Python 3.12, menginstal dependensi dari requirements.txt, lalu menyalin kode aplikasi. Uvicorn melayani aplikasi FastAPI di port 8080.

  2. Verifikasi pembangunan dan menjalankan kontainer secara lokal:

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

    Pastikan endpoint kesehatan merespons: curl http://localhost:8080/health

Menyebarkan ke Azure Container Apps

Setelah Anda membuat kontainer aplikasi, sebarkan ke Azure Container Apps dengan menggunakan Azure CLI. Perintah ini az containerapp up membangun gambar kontainer di cloud, sehingga Anda tidak memerlukan Docker di komputer Anda untuk langkah ini.

  1. Atur variabel lingkungan:

    RESOURCE_GROUP="mcp-tutorial-rg"
    LOCATION="eastus"
    ENVIRONMENT_NAME="mcp-env"
    APP_NAME="tasks-mcp-server-py"
    
  2. Buat grup sumber daya dan lingkungan Container Apps:

    az group create --name $RESOURCE_GROUP --location $LOCATION
    
    az containerapp env create \
        --name $ENVIRONMENT_NAME \
        --resource-group $RESOURCE_GROUP \
        --location $LOCATION
    
  3. Sebarkan aplikasi kontainer:

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

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

    Nota

    Untuk produksi, ganti asal wildcard dengan asal tepercaya yang spesifik. Lihat Mengamankan server MCP di Container Apps.

  5. Verifikasi penyebaran:

    APP_URL=$(az containerapp show \
        --name $APP_NAME \
        --resource-group $RESOURCE_GROUP \
        --query "properties.configuration.ingress.fqdn" -o tsv)
    
    curl https://$APP_URL/health
    

Menyambungkan GitHub Copilot ke server yang disebarkan

Sekarang setelah server MCP berjalan di Azure, konfigurasikan VS Code untuk menyambungkan GitHub Copilot ke endpoint yang telah diterapkan.

  1. Buat atau perbarui .vscode/mcp.json:

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

    Ganti <your-app-fqdn> dengan FQDN dari output penyebaran.

  2. Di Visual Studio Code, buka Copilot Chat dalam mode Agent.

  3. Verifikasi tasks-mcp-server muncul di daftar Alat. Pilih Mulai jika diperlukan.

  4. Mengujilah dengan perintah seperti "Buat tugas untuk mengimplementasikan lingkungan penahapan".

Mengonfigurasi penskalaan untuk penggunaan interaktif

Secara default, Azure Container Apps dapat menskalakan ke nol replika. Untuk server MCP yang melayani klien interaktif seperti Copilot, cold start menyebabkan penundaan yang nyata. Atur jumlah replika minimum untuk menjaga setidaknya satu instans tetap berjalan:

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

Pertimbangan keamanan

Tutorial ini menggunakan server MCP yang tidak diaauthenticated untuk kesederhanaan. Sebelum menjalankan server MCP dalam produksi, tinjau rekomendasi berikut. Ketika agen yang didukung oleh model bahasa besar (LLM) memanggil server MCP Anda, waspadai serangan injeksi yang cepat .

  • Autentikasi dan otorisasi: Amankan server MCP Anda dengan ID Microsoft Entra. Lihat Mengamankan server MCP di Container Apps.
  • Validasi input: Selalu validasi parameter alat. Gunakan Pydantic untuk menerapkan validasi data pada input alat.
  • HTTPS: Azure Container Apps memberlakukan HTTPS secara default dengan sertifikat TLS otomatis.
  • Hak istimewa terkecil: Hanya mengekspos alat yang diperlukan kasus penggunaan Anda. Hindari alat yang melakukan operasi destruktif tanpa konfirmasi.
  • CORS: Membatasi asal yang diizinkan ke domain tepercaya di lingkungan produksi.
  • Pencatatan dan Pemantauan: Pemanggilan alat MCP dicatat untuk audit. Gunakan Azure Monitor dan Analitik Log.

Membersihkan sumber daya

Jika Anda tidak berencana untuk terus menggunakan aplikasi ini, hapus grup sumber daya untuk menghapus semua sumber daya yang Anda buat dalam tutorial ini:

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

Langkah selanjutnya