Tutorial: Implantar um servidor MCP do Python nos Aplicativos de Contêiner do Azure

Neste tutorial, você criará um servidor MCP (Protocolo de Contexto de Modelo) que expõe as ferramentas de gerenciamento de tarefas usando o FastAPI e o SDK do Python do MCP. Implante o servidor no Aplicativos de Contêiner do Azure e conecte-o ao GitHub Copilot Chat no VS Code.

Neste tutorial, você:

  • Criar um aplicativo FastAPI que expõe ferramentas MCP
  • Testar o servidor MCP localmente com o GitHub Copilot
  • Conteinerizar e implantar o aplicativo nos Aplicativos de Contêiner do Azure
  • Conectar o GitHub Copilot ao servidor MCP implantado

Pré-requisitos

Criar a estrutura do aplicativo

Nesta seção, você criará um novo projeto do Python com o FastAPI e o SDK do Python do MCP.

  1. Crie o diretório do projeto e configure um ambiente virtual:

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

    fastapi>=0.115.0
    uvicorn>=0.30.0
    mcp[cli]>=1.2.0
    
  3. Instale as dependências:

    pip install -r requirements.txt
    
  4. Crie task_store.py para o armazenamento de dados na memória:

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

    A TaskItem classe de dados define o modelo de dados com um to_dict() método para serialização. A TaskStore classe gerencia uma lista na memória pré-preenchida com dados de exemplo e fornece métodos CRUD. O singleton store no nível do módulo é compartilhado em todo o aplicativo para simplificar.

Definir as ferramentas do MCP

Nesta seção, você definirá as ferramentas MCP que o modelo de IA pode invocar e montar o servidor MCP em seu aplicativo FastAPI.

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

    Pontos principais:

    • FastMCP("TasksMCP", stateless_http=True) cria um servidor MCP usando o padrão HTTP sem estado no SDK do Python. O endpoint HTTP que pode ser transmitido usa como padrão o subcaminho /mcp.
    • Cada @mcp.tool() função se torna uma ferramenta invocada. A docstring da função a as anotações de parâmetro ajudam o modelo de IA a entender como usar cada ferramenta.
  2. Criar app.py. Esse arquivo define o aplicativo FastAPI que monta o servidor 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"})
    

    O aplicativo do servidor MCP é montado na raiz (/). O endpoint HTTP transmissível do SDK usa como padrão /mcp, portanto, o caminho completo do endpoint é /mcp.

    Um ponto de extremidade /health separado é usado para investigações de integridade de Aplicativos de Contêiner. Os pontos de extremidade MCP esperam solicitações POST JSON-RPC e não são adequados como investigações de integridade.

Testar o servidor MCP localmente

Antes de implantar no Azure, verifique se o servidor MCP funciona executando-o localmente e conectando-se do GitHub Copilot.

  1. Inicie o aplicativo:

    uvicorn app:app --reload --port 8080
    
  2. Abra o VS Code e, em seguida, abra o Chat do Copilot e selecione o modo Agente .

  3. Selecione o botão Ferramentas e, em seguida, selecione Adicionar Mais Ferramentas...>Adicionar servidor MCP.

  4. Selecione HTTP (HTTP ou Eventos Server-Sent).

  5. Insira a URL do servidor: http://localhost:8080/mcp

  6. Insira uma ID do servidor: tasks-mcp

  7. Selecione Configurações do Workspace.

  8. Em um novo prompt de Chat do Copilot, digite: "Mostrar-me todas as tarefas"

  9. Selecione Continuar quando o Copilot solicitar a confirmação da ferramenta MCP.

Você deverá ver a lista de tarefas retornada do repositório na memória.

Tip

Tente outros prompts como "Criar uma tarefa para examinar a PR", "Marcar tarefa 1 como concluída" ou "Excluir tarefa 2".

Colocar o aplicativo em um contêiner

Empacote o aplicativo como um contêiner do Docker para que você possa testá-lo localmente antes de implantar no Azure.

  1. Criar um 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"]
    

    O Dockerfile utiliza uma imagem base slim do Python 3.12, instala dependências a partir de requirements.txt, e então copia o código do aplicativo. O Uvicorn serve o aplicativo FastAPI na porta 8080.

  2. Verifique se o contêiner compila e é executado localmente:

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

    Confirme se o endpoint de saúde responde: curl http://localhost:8080/health

Implantar nos Aplicativos de Contêiner do Azure

Depois de colocar o aplicativo em contêiner, implante-o nos Aplicativos de Contêiner do Azure usando a CLI do Azure. O az containerapp up comando cria a imagem de contêiner na nuvem, portanto, você não precisa do Docker em seu computador para esta etapa.

  1. Definir variáveis de ambiente:

    RESOURCE_GROUP="mcp-tutorial-rg"
    LOCATION="eastus"
    ENVIRONMENT_NAME="mcp-env"
    APP_NAME="tasks-mcp-server-py"
    
  2. Criar um grupo de recursos e um ambiente de Aplicativos de Contêiner:

    az group create --name $RESOURCE_GROUP --location $LOCATION
    
    az containerapp env create \
        --name $ENVIRONMENT_NAME \
        --resource-group $RESOURCE_GROUP \
        --location $LOCATION
    
  3. Implante o aplicativo de contêiner:

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

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

    Observação

    Para produção, substitua as origens curinga por origens confiáveis específicas. Consulte servidores MCP seguros em Aplicativos de Contêiner.

  5. Verifique a implantação:

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

Conectar o GitHub Copilot ao servidor implantado

Agora que o servidor MCP está em execução no Azure, configure o VS Code para conectar o GitHub Copilot ao ponto de extremidade implantado.

  1. Criar ou atualizar .vscode/mcp.json:

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

    Substitua <your-app-fqdn> pelo FQDN da saída de implantação.

  2. Abra o Copilot Chat no modo Agente dentro do VS Code.

  3. Verifique se tasks-mcp-server está na lista de ferramentas. Selecione Iniciar , se necessário.

  4. Teste com um prompt como "Criar uma tarefa para implantar o ambiente de teste".

Configurar o dimensionamento para uso interativo

Por padrão, os Aplicativos de Contêiner do Azure podem ser dimensionados para zero réplicas. Para servidores MCP que atendem clientes interativos como o Copilot, inicializações a frio causam atrasos perceptíveis. Defina uma contagem mínima de réplicas para manter pelo menos uma instância em execução:

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

Considerações de segurança

Este tutorial usa um servidor MCP não autenticado para simplificar. Antes de executar um servidor MCP em produção, examine as recomendações a seguir. Quando um agente alimentado por grandes modelos de linguagem (LLMs) chamar seu servidor MCP, fique atento aos ataques de injeção de prompt.

  • Autenticação e autorização: proteja o servidor MCP com a ID do Microsoft Entra. Consulte servidores MCP seguros em Aplicativos de Contêiner.
  • Validação de entrada: sempre valide os parâmetros da ferramenta. Use Pydantic para impor a validação de dados em entradas de ferramenta.
  • HTTPS: Os Aplicativos de Contêiner do Azure impõem HTTPS por padrão com certificados TLS automáticos.
  • Privilégio mínimo: exponha apenas as ferramentas necessárias para seu caso de uso. Evite ferramentas que executam operações destrutivas sem confirmação.
  • CORS: restringir as origens permitidas a domínios confiáveis na produção.
  • Registro e monitoramento de logs: registre as invocações da ferramenta MCP para auditoria. Use o Azure Monitor e o Log Analytics.

Limpar os recursos

Se você não planeja continuar usando este aplicativo, exclua o grupo de recursos para remover todos os recursos criados neste tutorial:

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

Próxima etapa