Udostępnij przez


Integrowanie aplikacji usługi App Service jako serwera MCP na potrzeby czatu Copilot w usłudze GitHub (Python)

W tym samouczku dowiesz się, jak uwidocznić funkcjonalność aplikacji FastAPI za pomocą protokołu MCP (Model Context Protocol), dodać ją jako narzędzie do aplikacji GitHub Copilot i korzystać z niej przy użyciu języka naturalnego w trybie agenta Copilot Chat.

Zrzut ekranu przedstawiający odpowiedź z wywołania narzędzia MCP w oknie czatu Copilot w usłudze GitHub.

Jeśli aplikacja internetowa ma już przydatne funkcje, takie jak zakupy, rezerwacja hotelowa lub zarządzanie danymi, można łatwo udostępnić te możliwości:

Dodając serwer MCP do aplikacji internetowej, możesz zezwolić agentowi na zrozumienie możliwości aplikacji i korzystanie z nich podczas odpowiadania na monity użytkownika. Oznacza to, że wszystko, co może zrobić twoja aplikacja, agent może też zrobić.

  • Dodaj serwer MCP do aplikacji internetowej.
  • Przetestuj serwer MCP lokalnie w trybie agenta czatu Copilot w usłudze GitHub.
  • Wdróż serwer MCP w usłudze Azure App Service i połącz się z nim w aplikacji GitHub Copilot Chat.

Wymagania wstępne

W tym samouczku założono, że pracujesz z przykładem używanym w temacie Deploy a Python FastAPI web app with PostgreSQL in Azure (Wdrażanie aplikacji internetowej FastAPI w języku Python za pomocą bazy danych PostgreSQL na platformie Azure).

Otwórz co najmniej przykładową aplikację w usłudze GitHub Codespaces i wdróż aplikację, uruchamiając polecenie azd up.

Otwieranie w usłudze GitHub Codespaces

Dodawanie serwera MCP do aplikacji internetowej

  1. W eksploratorze codespace otwórz plik src/pyproject.toml, dodaj mcp[cli] do listy zależności, jak pokazano w poniższym przykładzie:

    dependencies = [
        ...
        "mcp[cli]",
    ]
    
  2. W pliku src/fastapi_app utwórz plik o nazwie mcp_server.py i wklej następujący kod inicjowania serwera MCP do pliku:

    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)
    

    Inicjator FastMCP() tworzy serwer MCP przy użyciu wzorca trybu bezstanowego w zestawie SDK języka Python MCP. Domyślnie jego przesyłany strumieniowo punkt końcowy HTTP jest ustawiony na ścieżkę podrzędną /mcp .

    • Dekorator @mcp.tool() dodaje narzędzie do serwera MCP z jego implementacją.
    • Opis funkcji narzędzia pomaga agentowi wywołującego zrozumieć, jak używać narzędzia i jego parametrów.

    Narzędzia duplikować istniejące funkcje przeglądów restauracji w aplikacji internetowej FastAPI opartej na formularzach. Jeśli chcesz, możesz dodać więcej narzędzi do aktualizowania i usuwania funkcji.

  3. W pliku src/fastapi_app/app.py znajdź wiersz ( app = FastAPI() wiersz 24) i zastąp go następującym kodem:

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

    Ten kod instaluje przesyłany strumieniowo punkt końcowy HTTP serwera MCP do istniejącej aplikacji FastAPI w ścieżce /mcp. Wraz ze ścieżką domyślną punktu końcowego HTTP przesyłanego strumieniowo pełna ścieżka to /mcp/mcp.

Testowanie serwera MCP lokalnie

  1. W terminalu usługi Codespace uruchom aplikację przy użyciu następujących poleceń:

    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. Wybierz pozycję Otwórz w przeglądarce, a następnie dodaj kilka restauracji i recenzji.

    Pozostaw uruchomione uvicorn . Serwer MCP działa teraz http://localhost:8000/mcp/mcp .

  3. Wróć do obszaru codespace, otwórz czat copilot, a następnie wybierz pozycję Tryb agenta w wierszu polecenia.

  4. Wybierz przycisk Narzędzia , a następnie wybierz ikonę Dodaj serwer MCP w prawym górnym rogu okna podręcznego.

    Zrzut ekranu przedstawiający sposób dodawania serwera MCP w trybie agenta czatu Copilot w usłudze GitHub.

  5. Wybierz pozycję HTTP (HTTP lub Server-Sent Events).

  6. W polu Wprowadź adres URL serwera wpisz http://localhost:8000/mcp/mcp.

  7. W polu Wprowadź identyfikator serwera wpisz restaurant_ratings lub dowolną nazwę.

  8. Wybierz pozycję Ustawienia obszaru roboczego.

  9. W nowym oknie Copilot Chat wpisz coś takiego jak "Pokaż mi oceny restauracji".

  10. Domyślnie narzędzie GitHub Copilot wyświetla potwierdzenie zabezpieczeń podczas wywoływania serwera MCP. Wybierz Kontynuuj.

    Zrzut ekranu przedstawiający domyślny komunikat zabezpieczeń z wywołania MCP w usłudze GitHub Copilot Chat.

    Powinna zostać wyświetlona odpowiedź wskazująca, że wywołanie narzędzia MCP zakończy się pomyślnie.

    Zrzut ekranu przedstawiający odpowiedź z wywołania narzędzia MCP w oknie czatu Copilot w usłudze GitHub.

Wdrażanie serwera MCP w usłudze App Service

  1. W terminalu usługi Codespace wdróż zmiany, zatwierdzając zmiany (metodę GitHub Actions) lub uruchamiając azd up (metoda interfejsu wiersza polecenia dewelopera platformy Azure).

  2. W danych wyjściowych usługi AZD znajdź adres URL aplikacji. Adres URL wygląda następująco w danych wyjściowych usługi AZD:

     Deploying services (azd deploy)
    
       (✓) Done: Deploying service web
       - Endpoint: <app-url>
     
  3. Po zakończeniu azd up otwórz plik vscode/mcp.json. Zmień adres URL na <app-url>/mcp/mcp.

  4. Powyżej zmodyfikowanej konfiguracji serwera MCP wybierz pozycję Uruchom.

    Zrzut ekranu przedstawiający sposób ręcznego uruchamiania serwera MCP z lokalnego pliku mcp.json.

  5. Uruchom nowe okno czatu Copilot w usłudze GitHub. Powinno być możliwe wyświetlanie ocen restauracji, a także tworzenie nowych restauracji i nowych ocen w agencie Copilot.

Najlepsze rozwiązania dotyczące zabezpieczeń

Gdy serwer MCP jest wywoływany przez agenta obsługiwanego przez duże modele językowe (LLM), należy pamiętać o atakach polegających na wstrzyknięciu monitu . Rozważ następujące najlepsze rozwiązania w zakresie zabezpieczeń:

  • Uwierzytelnianie i autoryzacja: zabezpiecz serwer MCP za pomocą uwierzytelniania firmy Microsoft Entra, aby zapewnić dostęp tylko autoryzowanym użytkownikom lub agentom. Aby zapoznać się z przewodnikiem krok po kroku, zobacz Secure Model Context Protocol calls to Azure App Service from Visual Studio Code with Microsoft Entra authentication (Bezpieczne wywołania protokołu kontekstowego modelu do usługi Azure App Service z programu Visual Studio Code z uwierzytelnianiem entra firmy Microsoft ).
  • Walidacja danych wejściowych i oczyszczanie: zawsze weryfikuj dane przychodzące, aby zapobiec nieprawidłowym lub złośliwym danym wejściowym. W przypadku aplikacji języka Python użyj bibliotek, takich jak Pydantic , aby wymusić reguły walidacji danych z dedykowanymi modelami wejściowymi (takimi jak RestaurantCreate i ReviewCreate). Zapoznaj się z ich dokumentacją, aby uzyskać szczegółowe informacje na temat najlepszych rozwiązań i implementacji.
  • HTTPS: Przykład opiera się na usłudze Azure App Service, która domyślnie wymusza protokół HTTPS i udostępnia bezpłatne certyfikaty TLS/SSL do szyfrowania danych przesyłanych.
  • Zasada najniższych uprawnień: uwidacznia tylko niezbędne narzędzia i dane wymagane do użycia. Unikaj ujawniania poufnych operacji, chyba że jest to konieczne.
  • Ograniczanie szybkości i ograniczanie przepustowości: użyj usługi API Management lub niestandardowego oprogramowania pośredniczącego, aby zapobiec nadużyciom i atakom typu "odmowa usługi".
  • Rejestrowanie i monitorowanie: dostęp do dzienników i użycie punktów końcowych MCP na potrzeby inspekcji i wykrywania anomalii. Monitorowanie podejrzanych działań.
  • Konfiguracja mechanizmu CORS: ogranicz żądania między źródłami do zaufanych domen, jeśli serwer MCP jest uzyskiwany z przeglądarek. Aby uzyskać więcej informacji, zobacz Włączanie mechanizmu CORS.
  • Regularne aktualizacje: zapewnij aktualność zależności, aby wyeliminować znane luki w zabezpieczeniach.

Więcej zasobów

Integrowanie sztucznej inteligencji z aplikacjami usługi Azure App Service