Partilhar via


Integrar um aplicativo do Serviço de Aplicativo como um Servidor MCP para o Chat Copilot do GitHub (Python)

Neste tutorial, você aprenderá como expor a funcionalidade de um aplicativo FastAPI por meio do MCP (Model Context Protocol), adicioná-lo como uma ferramenta ao GitHub Copilot e interagir com seu aplicativo usando linguagem natural no modo de agente do Copilot Chat.

Captura de tela mostrando que a resposta da ferramenta MCP chama na janela GitHub Copilot Chat.

Se o seu aplicativo Web já tiver recursos úteis, como compras, reservas de hotéis ou gerenciamento de dados, é fácil disponibilizar esses recursos para:

Ao adicionar um servidor MCP ao seu aplicativo Web, você permite que um agente compreenda e use os recursos do seu aplicativo quando ele responde às solicitações do usuário. Isso significa que tudo o que seu aplicativo pode fazer, o agente também pode fazer.

  • Adicione um servidor MCP ao seu aplicativo Web.
  • Teste o servidor MCP localmente no modo de agente do GitHub Copilot Chat.
  • Implante o servidor MCP no Serviço de Aplicativo do Azure e conecte-se a ele no Chat do Copiloto do GitHub.

Pré-requisitos

Este tutorial pressupõe que você esteja trabalhando com o exemplo usado em Implantar um aplicativo Web Python FastAPI com PostgreSQL no Azure.

No mínimo, abra o aplicativo de exemplo no GitHub Codespaces e implante o aplicativo executando azd up.

Abrir no GitHub Codespaces

Adicionar servidor MCP ao seu aplicativo Web

  1. No explorador de codespace, abra src/pyproject.toml, adicione mcp[cli] à lista de dependências, conforme mostrado no exemplo a seguir:

    dependencies = [
        ...
        "mcp[cli]",
    ]
    
  2. No src/fastapi_app, crie um arquivo chamado mcp_server.py e cole o seguinte código de inicialização do servidor MCP no arquivo:

    import asyncio
    import contextlib
    from contextlib import asynccontextmanager
    
    from mcp.server.fastmcp import FastMCP
    from sqlalchemy.sql import func
    from sqlmodel import Session, select
    
    from .models import Restaurant, Review, engine
    
    # Create a FastMCP server. Use stateless_http=True for simple mounting. Default path is .../mcp
    mcp = FastMCP("RestaurantReviewsMCP", stateless_http=True)
    
    # Lifespan context manager to start/stop the MCP session manager with the FastAPI app
    @asynccontextmanager
    async def mcp_lifespan(app):
        async with contextlib.AsyncExitStack() as stack:
            await stack.enter_async_context(mcp.session_manager.run())
            yield
    
    # MCP tool: List all restaurants with their average rating and review count
    @mcp.tool()
    async def list_restaurants_mcp() -> list[dict]:
        """List restaurants with their average rating and review count."""
    
        def sync():
            with Session(engine) as session:
                statement = (
                    select(
                        Restaurant,
                        func.avg(Review.rating).label("avg_rating"),
                        func.count(Review.id).label("review_count"),
                    )
                    .outerjoin(Review, Review.restaurant == Restaurant.id)
                    .group_by(Restaurant.id)
                )
                results = session.exec(statement).all()
                rows = []
                for restaurant, avg_rating, review_count in results:
                    r = restaurant.dict()
                    r["avg_rating"] = float(avg_rating) if avg_rating is not None else None
                    r["review_count"] = review_count
                    r["stars_percent"] = (
                        round((float(avg_rating) / 5.0) * 100) if review_count > 0 and avg_rating is not None else 0
                    )
                    rows.append(r)
                return rows
    
        return await asyncio.to_thread(sync)
    
    # MCP tool: Get a restaurant and all its reviews by restaurant_id
    @mcp.tool()
    async def get_details_mcp(restaurant_id: int) -> dict:
        """Return the restaurant and its related reviews as objects."""
    
        def sync():
            with Session(engine) as session:
                restaurant = session.exec(select(Restaurant).where(Restaurant.id == restaurant_id)).first()
                if restaurant is None:
                    return None
                reviews = session.exec(select(Review).where(Review.restaurant == restaurant_id)).all()
                return {"restaurant": restaurant.dict(), "reviews": [r.dict() for r in reviews]}
    
        return await asyncio.to_thread(sync)
    
    # MCP tool: Create a new review for a restaurant
    @mcp.tool()
    async def create_review_mcp(restaurant_id: int, user_name: str, rating: int, review_text: str) -> dict:
        """Create a new review for a restaurant and return the created review dict."""
    
        def sync():
            with Session(engine) as session:
                review = Review()
                review.restaurant = restaurant_id
                review.review_date = __import__("datetime").datetime.now()
                review.user_name = user_name
                review.rating = int(rating)
                review.review_text = review_text
                session.add(review)
                session.commit()
                session.refresh(review)
                return review.dict()
    
        return await asyncio.to_thread(sync)
    
    # MCP tool: Create a new restaurant
    @mcp.tool()
    async def create_restaurant_mcp(restaurant_name: str, street_address: str, description: str) -> dict:
        """Create a new restaurant and return the created restaurant dict."""
    
        def sync():
            with Session(engine) as session:
                restaurant = Restaurant()
                restaurant.name = restaurant_name
                restaurant.street_address = street_address
                restaurant.description = description
                session.add(restaurant)
                session.commit()
                session.refresh(restaurant)
                return restaurant.dict()
    
        return await asyncio.to_thread(sync)
    

    O inicializador FastMCP() cria um servidor MCP usando o padrão de modo sem estado no MCP Python SDK. Por padrão, seu ponto de extremidade HTTP streamable é definido como o /mcp subcaminho.

    • O @mcp.tool() decorador adiciona uma ferramenta ao servidor MCP com a sua implementação.
    • A descrição da função de ferramenta ajuda o agente chamador a entender como usar a ferramenta e seus parâmetros.

    As ferramentas duplicam a funcionalidade existente de avaliações de restaurantes no aplicativo Web FastAPI baseado em formulário. Se desejar, você pode adicionar mais ferramentas para atualizar e excluir funcionalidades.

  3. Em src/fastapi_app/app.py, localize a linha para app = FastAPI() (linha 24) e substitua-a pelo seguinte código:

    from .mcp_server import mcp, mcp_lifespan
    app = FastAPI(lifespan=mcp_lifespan)
    app.mount("/mcp", mcp.streamable_http_app())
    

    Esse código monta o ponto de extremidade HTTP streamable do servidor MCP para o aplicativo FastAPI existente no caminho /mcp. Juntamente com o caminho padrão do ponto de extremidade HTTP streamable, o caminho completo é /mcp/mcp.

Teste o servidor MCP localmente

  1. No terminal codespace, execute o aplicativo com os seguintes comandos:

    python3 -m venv .venv
    source .venv/bin/activate
    pip install -r src/requirements.txt
    pip install -e src
    python3 src/fastapi_app/seed_data.py
    python3 -m uvicorn fastapi_app:app --reload --port=8000
    
  2. Selecione Abrir no navegador e adicione alguns restaurantes e avaliações.

    Deixe correr uvicorn . Seu servidor MCP está em execução no http://localhost:8000/mcp/mcp momento.

  3. De volta ao espaço de código, abra o Copilot Chat e selecione Modo de agente na caixa de prompt.

  4. Selecione o botão Ferramentas e, em seguida, selecione o ícone Adicionar Servidor MCP no canto superior direito do pop-up.

    Captura de tela mostrando como adicionar um servidor MCP no modo de agente do GitHub Copilot Chat.

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

  6. Em Inserir URL do Servidor, digite http://localhost:8000/mcp/mcp.

  7. Em Inserir ID do Servidor, digite restaurant_ratings ou qualquer nome que desejar.

  8. Selecione Configurações do espaço de trabalho.

  9. Em uma nova janela do Copilot Chat, digite algo como "Mostrar as classificações do restaurante".

  10. Por padrão, o GitHub Copilot mostra uma confirmação de segurança quando você invoca um servidor MCP. Selecione Continuar.

    Captura de tela mostrando a mensagem de segurança padrão de uma invocação MCP no GitHub Copilot Chat.

    Agora você verá uma resposta que indica que a chamada da ferramenta MCP foi bem-sucedida.

    Captura de tela mostrando que a resposta da ferramenta MCP chama na janela GitHub Copilot Chat.

Implantar seu servidor MCP no Serviço de Aplicativo

  1. De volta ao terminal do codespace, implante suas alterações confirmando suas alterações (método GitHub Actions) ou executando azd up (método Azure Developer CLI).

  2. Na saída AZD, localize o URL do seu aplicativo. O URL tem esta aparência na saída AZD:

     Deploying services (azd deploy)
    
       (✓) Done: Deploying service web
       - Endpoint: <app-url>
     
  3. Quando azd up terminar, abra .vscode/mcp.json. Altere o URL para <app-url>/mcp/mcp.

  4. Acima da configuração modificada do servidor MCP, selecione Iniciar.

    Captura de ecrã a mostrar como iniciar manualmente um servidor MCP a partir do ficheiro mcp.json local.

  5. Inicie uma nova janela de bate-papo do GitHub Copilot. Você deve ser capaz de visualizar as classificações de restaurantes, bem como criar novos restaurantes e novas classificações no agente Copilot.

Práticas recomendadas de segurança

Quando o seu servidor MCP é chamado por um agente alimentado por modelos de linguagem grandes (LLM), esteja ciente de ataques de injeção imediata . Considere as seguintes práticas recomendadas de segurança:

  • Autenticação e Autorização: Proteja o seu servidor MCP com autenticação Microsoft Entra para garantir que apenas utilizadores ou agentes autorizados possam aceder às suas ferramentas. Consulte as chamadas do protocolo de contexto de modelo seguro para o Azure App Service a partir do Visual Studio Code, utilizando a autenticação Microsoft Entra para um guia passo a passo.
  • Validação e Limpeza de Entrada: Sempre valide os dados recebidos para evitar entradas inválidas ou maliciosas. Para aplicativos Python, use bibliotecas como Pydantic para impor regras de validação de dados com modelos de entrada dedicados (como RestaurantCreate e ReviewCreate). Consulte a documentação para obter práticas recomendadas e detalhes de implementação.
  • HTTPS: O exemplo depende do Serviço de Aplicativo do Azure, que impõe HTTPS por padrão e fornece certificados TLS/SSL gratuitos para criptografar dados em trânsito.
  • Princípio do Privilégio Mínimo: Exponha apenas as ferramentas e os dados necessários para o seu caso de uso. Evite expor operações sensíveis, a menos que seja necessário.
  • Limitação e limitação de taxa: use o Gerenciamento de API ou middleware personalizado para evitar abusos e ataques de negação de serviço.
  • Registro e monitoramento: acesso e uso de pontos de extremidade MCP para auditoria e deteção de anomalias. Monitore atividades suspeitas.
  • Configuração do CORS: restrinja solicitações de origem cruzada a domínios confiáveis se o servidor MCP for acessado a partir de navegadores. Para obter mais informações, consulte Habilitar CORS.
  • Atualizações regulares: mantenha suas dependências atualizadas para mitigar vulnerabilidades conhecidas.

Mais recursos

Integre a IA em seus aplicativos do Serviço de Aplicativo do Azure