Compartir vía


Integración de una aplicación de App Service como servidor MCP para GitHub Copilot Chat (Python)

En este tutorial, aprenderá a exponer la funcionalidad de una aplicación FastAPI a través del Protocolo de contexto de modelo (MCP), agregarla como una herramienta a GitHub Copilot e interactuar con la aplicación mediante lenguaje natural en el modo del agente de chat de Copilot.

Captura de pantalla que muestra la respuesta de la llamada a la herramienta MCP en la ventana Chat de GitHub Copilot.

Si la aplicación web ya tiene características útiles, como compras, reservas de hoteles o administración de datos, es fácil hacer que esas funcionalidades estén disponibles para:

Al agregar un servidor MCP a la aplicación web, se habilita un agente para comprender y usar las funcionalidades de la aplicación cuando responde a las solicitudes del usuario. Esto significa que cualquier cosa que pueda hacer la aplicación, el agente también puede hacerlo.

  • Agregue un servidor MCP a la aplicación web.
  • Pruebe el servidor MCP localmente en el modo del agente de Chat de Copilot de GitHub.
  • Implemente el servidor MCP en Azure App Service y conéctese a él en GitHub Copilot Chat.

Prerrequisitos

En este tutorial se supone que está trabajando con el ejemplo que se usa en Implementación de una aplicación web fastAPI de Python con PostgreSQL en Azure.

Como mínimo, abra la aplicación de ejemplo en GitHub Codespaces e implemente la aplicación mediante la ejecución azd upde .

Abrir en GitHub Codespaces

Adición de un servidor MCP a la aplicación web

  1. En el explorador de codespace, abra src/pyproject.toml, agregue mcp[cli] a la lista de dependencias, como se muestra en el ejemplo siguiente:

    dependencies = [
        ...
        "mcp[cli]",
    ]
    
  2. En src/fastapi_app, cree un archivo denominado mcp_server.py y pegue el siguiente código de inicialización del servidor MCP en el archivo:

    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)
    

    El inicializador FastMCP() crea un servidor MCP mediante el patrón de modo sin estado en el SDK de Python de MCP. De forma predeterminada, su punto de conexión HTTP que se puede transmitir se establece en la /mcp subpath.

    • El @mcp.tool() decorador agrega una herramienta al servidor MCP con su implementación.
    • La descripción de la función de herramienta ayuda al agente de llamada a comprender cómo usar la herramienta y sus parámetros.

    Las herramientas duplican la funcionalidad existente de revisiones de restaurantes en la aplicación web fastAPI basada en formularios. Si lo desea, puede agregar más herramientas para actualizar y eliminar funcionalidad.

  3. En src/fastapi_app/app.py, busque la línea de app = FastAPI() (línea 24) y reemplácela por el código siguiente:

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

    Este código monta el punto de conexión HTTP que se puede transmitir del servidor MCP a la aplicación FastAPI existente en la ruta de acceso /mcp. Junto con la ruta de acceso predeterminada del punto de conexión HTTP que se puede transmitir, la ruta de acceso completa es /mcp/mcp.

Probar el servidor MCP localmente

  1. En el terminal de codespace, ejecute la aplicación con los siguientes 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. Seleccione Abrir en el explorador y agregue algunos restaurantes y opiniones.

    Deje uvicorn en ejecución. El servidor MCP se está ejecutando ahora http://localhost:8000/mcp/mcp .

  3. De nuevo en el espacio de código, abra Copilot Chat y, a continuación, seleccione Modo de agente en el cuadro de símbolo del sistema.

  4. Seleccione el botón Herramientas y, a continuación, seleccione el icono Agregar servidor MCP en la esquina superior derecha del menú emergente.

    Captura de pantalla que muestra cómo agregar un servidor MCP en el modo del agente de Chat de Copilot de GitHub.

  5. Seleccione HTTP (HTTP o eventos Server-Sent).

  6. En Escriba dirección URL del servidor, escriba http://localhost:8000/mcp/mcp.

  7. En Escriba id. de servidor, escriba restaurant_ratings o cualquier nombre que desee.

  8. Seleccione Configuración del área de trabajo.

  9. En una nueva ventana de Chat de Copilot, escriba algo parecido a "Mostrarme las clasificaciones de restaurantes".

  10. De forma predeterminada, GitHub Copilot muestra una confirmación de seguridad al invocar un servidor MCP. Selecciona Continuar.

    Captura de pantalla que muestra el mensaje de seguridad predeterminado de una invocación de MCP en El chat de GitHub Copilot.

    Ahora debería ver una respuesta que indica que la llamada a la herramienta MCP es correcta.

    Captura de pantalla que muestra la respuesta de la llamada a la herramienta MCP en la ventana Chat de GitHub Copilot.

Implementación del servidor MCP en App Service

  1. De nuevo en el terminal de codespace, implemente los cambios confirmando los cambios (método acciones de GitHub) o ejecutándose azd up (método de la CLI para desarrolladores de Azure).

  2. En la salida de AZD, busque la dirección URL de la aplicación. La dirección URL tiene este aspecto en la salida de AZD:

     Deploying services (azd deploy)
    
       (✓) Done: Deploying service web
       - Endpoint: <app-url>
     
  3. Una vez azd up finalizado, abra .vscode/mcp.json. Cambie la dirección URL a <app-url>/mcp/mcp.

  4. Encima de la configuración modificada del servidor MCP, seleccione Iniciar.

    Captura de pantalla que muestra cómo iniciar manualmente un servidor MCP desde el archivo mcp.json local.

  5. Inicie una nueva ventana de Chat de GitHub Copilot. Debería poder ver las clasificaciones de restaurantes, así como crear nuevos restaurantes y nuevas clasificaciones en el agente de Copilot.

Procedimientos recomendados de seguridad

Cuando un agente con tecnología de modelos de lenguaje grande (LLM) llama al servidor MCP, tenga en cuenta los ataques de inyección de mensajes . Tenga en cuenta los siguientes procedimientos recomendados de seguridad:

  • Autenticación y autorización: proteja el servidor MCP con la autenticación de Microsoft Entra para asegurarse de que solo los usuarios o agentes autorizados puedan acceder a sus herramientas. Consulte Secure Model Context Protocol calls to Azure App Service from Visual Studio Code with Microsoft Entra authentication (Protocolo de contexto de modelo seguro de llamadas a Azure App Service desde Visual Studio Code con autenticación de Microsoft Entra ) para obtener una guía paso a paso.
  • Validación y saneamiento de entrada: valide siempre los datos entrantes para evitar entradas no válidas o malintencionadas. En el caso de las aplicaciones de Python, use bibliotecas como Pydantic para aplicar reglas de validación de datos con modelos de entrada dedicados (como RestaurantCreate y ReviewCreate). Consulte su documentación para conocer los procedimientos recomendados y los detalles de implementación.
  • HTTPS: El ejemplo se basa en Azure App Service, que aplica HTTPS de forma predeterminada y proporciona certificados TLS/SSL gratuitos para cifrar los datos en tránsito.
  • Principio de privilegios mínimos: exponga solo las herramientas y los datos necesarios para su caso de uso. Evite exponer operaciones confidenciales a menos que sea necesario.
  • Limitación de velocidad y limitación: use API Management o middleware personalizado para evitar ataques por denegación de servicio y abuso.
  • Registro y supervisión: registro de acceso y uso de puntos de conexión de MCP para la auditoría y la detección de anomalías. Supervisar la actividad sospechosa.
  • Configuración de CORS: restrinja las solicitudes entre orígenes a dominios de confianza si se accede al servidor MCP desde exploradores. Para obtener más información, vea Habilitar CORS.
  • Actualizaciones periódicas: mantenga actualizadas las dependencias para mitigar las vulnerabilidades conocidas.

Más recursos

Integración de la inteligencia artificial en las aplicaciones de Azure App Service