Teilen über


Integrieren einer App Service-App als MCP-Server für GitHub Copilot Chat (Python)

In diesem Tutorial erfahren Sie, wie Sie die Funktionen einer FastAPI-App über Model Context Protocol (MCP) verfügbar machen, sie GitHub Copilot als Tool hinzufügen und mit Ihrer App im Agent-Modus von Copilot-Chat interagieren.

Ein Screenshot, der die Antwort des MCP-Toolaufrufs im GitHub Copilot Chat-Fenster zeigt.

Wenn Ihre Webanwendung bereits nützliche Funktionen wie Shopping, Hotelbuchung oder Datenverwaltung aufweist, ist es einfach, diese Funktionen verfügbar zu machen für:

Indem Sie Ihrer Web-App einen MCP-Server hinzufügen, können Sie einen Agent aktivieren, um die Funktionen Ihrer App zu verstehen und zu verwenden, wenn er auf Benutzeraufforderungen reagiert. Dies bedeutet, dass ihre App alles tun kann, was der Agent auch tun kann.

  • Fügen Sie Ihrer Web-App einen MCP-Server hinzu.
  • Testen Sie den MCP-Server lokal im GitHub Copilot Chat-Agent-Modus.
  • Stellen Sie den MCP-Server in Azure App Service bereit und stellen Sie eine Verbindung mit dem Server in GitHub Copilot Chat her.

Voraussetzungen

In diesem Lernprogramm wird davon ausgegangen, dass Sie mit dem Beispiel arbeiten, das in der Bereitstellung einer Python FastAPI-Web-App mit PostgreSQL in Azure verwendet wird.

Öffnen Sie mindestens die Beispielanwendung in GitHub Codespaces, und stellen Sie die App durch Ausführen azd upbereit.

In GitHub Codespaces öffnen

Hinzufügen eines MCP-Servers zu Ihrer Web-App

  1. Öffnen Sie im Codespace-Explorer src/pyproject.toml, fügen Sie mcp[cli] der Liste der Abhängigkeiten hinzu, wie im folgenden Beispiel gezeigt:

    dependencies = [
        ...
        "mcp[cli]",
    ]
    
  2. Erstellen Sie in src/fastapi_app eine Datei namens mcp_server.py , und fügen Sie den folgenden MCP-Serverinitialisierungscode in die Datei ein:

    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)
    

    Der FastMCP()-Initialisierer erstellt einen MCP-Server mithilfe des Zustandslosen Modusmusters im MCP Python SDK. Standardmäßig ist der streambare HTTP-Endpunkt auf den /mcp Unterpfad festgelegt.

    • Der @mcp.tool() Dekorator fügt dem MCP-Server mit seiner Implementierung ein Tool hinzu.
    • Die Beschreibung der Toolfunktion hilft dem aufrufenden Agent zu verstehen, wie das Tool und seine Parameter verwendet werden.

    Die Tools duplizieren die funktionalität der vorhandenen Restaurantbewertungen in der formularbasierten FastAPI-Webanwendung. Wenn Sie möchten, können Sie weitere Tools zum Aktualisieren und Löschen von Funktionen hinzufügen.

  3. Suchen Sie in src/fastapi_app/app.py die Zeile für app = FastAPI() (Zeile 24), und ersetzen Sie sie durch den folgenden Code:

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

    Dieser Code stellt den streambaren HTTP-Endpunkt des MCP-Servers in die vorhandene FastAPI-App am Pfad /mcp bereit. Zusammen mit dem Standardpfad des streambaren HTTP-Endpunkts ergibt sich der vollständige Pfad /mcp/mcp.

Testen des MCP-Servers lokal

  1. Führen Sie im Codespace-Terminal die Anwendung mit den folgenden Befehlen aus:

    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. Wählen Sie "Im Browser öffnen" aus, und fügen Sie dann ein paar Restaurants und Rezensionen hinzu.

    Lassen Sie uvicorn weiterlaufen. Ihr MCP-Server wird zurzeit http://localhost:8000/mcp/mcp ausgeführt.

  3. Öffnen Sie im Codespace den Copilot-Chat, und wählen Sie dann im Eingabeaufforderungsfeld den Agent-Modus aus.

  4. Wählen Sie die Schaltfläche "Extras " und dann das Symbol "MCP-Server hinzufügen " in der oberen rechten Ecke des Popups aus.

    Screenshot, der zeigt, wie Sie einen MCP-Server im GitHub Copilot Chat-Agent-Modus hinzufügen.

  5. Wählen Sie HTTP (HTTP- oder Server-Sent-Ereignisse) aus.

  6. Geben Sie unter Server-URL eingebenhttp://localhost:8000/mcp/mcp ein.

  7. Geben Sie in "Server-ID eingeben" restaurant_ratings oder einen beliebigen Namen ein.

  8. Wählen Sie "Arbeitsbereichseinstellungen" aus.

  9. Geben Sie in einem neuen Copilot-Chatfenster etwas wie "Restaurantbewertungen anzeigen" ein.

  10. Standardmäßig zeigt GitHub Copilot eine Sicherheitsbestätigung an, wenn Sie einen MCP-Server aufrufen. Wählen Sie "Weiter" aus.

    Screenshot der Standardsicherheitsmeldung eines MCP-Aufrufs in GitHub Copilot Chat.

    Nun sollte eine Antwort angezeigt werden, die angibt, dass der MCP-Toolaufruf erfolgreich ist.

    Ein Screenshot, der die Antwort des MCP-Toolaufrufs im GitHub Copilot Chat-Fenster zeigt.

MCP-Server auf App Service bereitstellen

  1. Stellen Sie Ihre Änderungen wieder im Codespace-Terminal bereit, indem Sie Ihre Änderungen commiten (GitHub Actions-Methode) oder azd up ausführen (Azure Developer CLI-Methode).

  2. Suchen Sie in der AZD-Ausgabe die URL Ihrer App. Die URL sieht in der AZD-Ausgabe wie folgt aus:

     Deploying services (azd deploy)
    
       (✓) Done: Deploying service web
       - Endpoint: <app-url>
     
  3. Öffnen Sie nach Abschluss von azd up.vscode/mcp.json. Ändern Sie die URL in <app-url>/mcp/mcp.

  4. Wählen Sie oberhalb der geänderten MCP-Serverkonfiguration "Start" aus.

    Screenshot, der zeigt, wie Sie einen MCP-Server manuell aus der lokalen mcp.json Datei starten.

  5. Starten Sie ein neues GitHub Copilot-Chatfenster. Sie sollten in der Lage sein, Restaurantbewertungen anzuzeigen sowie neue Restaurants und neue Bewertungen im Copilot-Agenten zu erstellen.

Bewährte Sicherheitsmethoden

Wenn Ihr MCP-Server von einem Agent aufgerufen wird, der von großen Sprachmodellen (LLM) unterstützt wird, sollten Sie sich über Eingabeaufforderungsangriffe bewusst sein. Berücksichtigen Sie die folgenden bewährten Sicherheitsmethoden:

  • Authentifizierung und Autorisierung: Sichern Sie Ihren MCP-Server mit der Microsoft Entra-Authentifizierung, um sicherzustellen, dass nur autorisierte Benutzer oder Agents auf Ihre Tools zugreifen können. Eine schrittweise Anleitung finden Sie unter Secure Model Context Protocol-Aufrufe an Azure App Service von Visual Studio Code mit microsoft Entra-Authentifizierung .
  • Eingabeüberprüfung und Bereinigung: Überprüfen Sie eingehende Daten immer, um ungültige oder böswillige Eingaben zu verhindern. Verwenden Sie für Python-Apps Bibliotheken wie Pydantic , um Datenüberprüfungsregeln mit dedizierten Eingabemodellen (z. B. RestaurantCreate und ReviewCreate) zu erzwingen. Weitere Informationen zu bewährten Methoden und Implementierungsdetails finden Sie in der Dokumentation.
  • HTTPS: Das Beispiel basiert auf Azure App Service, der STANDARDMÄßIG HTTPS erzwingt und kostenlose TLS/SSL-Zertifikate zum Verschlüsseln von Daten während der Übertragung bereitstellt.
  • Prinzip der geringsten Rechte: Machen Sie nur die erforderlichen Tools und Daten verfügbar, die für Ihren Anwendungsfall erforderlich sind. Vermeiden Sie das Verfügbarmachen vertraulicher Vorgänge, es sei denn, dies ist erforderlich.
  • Rate Limiting and Throttling: Verwenden Sie API-Verwaltung oder benutzerdefinierte Middleware, um Missbrauch und Denial-of-Service-Angriffe zu verhindern.
  • Protokollierung und Überwachung: Protokollieren des Zugriffs und der Verwendung von MCP-Endpunkten für die Überwachung und Anomalieerkennung. Überwachen Sie verdächtige Aktivitäten.
  • CORS-Konfiguration: Beschränken Sie ursprungsübergreifende Anforderungen auf vertrauenswürdige Domänen, wenn auf Ihren MCP-Server über Browser zugegriffen wird. Weitere Informationen finden Sie unter Aktivieren von CORS.
  • Regelmäßige Updates: Halten Sie Ihre Abhängigkeiten auf dem neuesten Stand, um bekannte Sicherheitsrisiken zu mindern.

Weitere Ressourcen

Integrieren von KI in Ihre Azure App Service-Anwendungen