Интеграция приложения Службы приложений в качестве СЕРВЕРА MCP для GitHub Copilot Chat (Python)

В этом руководстве вы узнаете, как предоставить функциональные возможности приложения FastAPI через протокол MCP, добавить его в GitHub Copilot и взаимодействовать с приложением с помощью естественного языка в режиме агента Copilot Chat.

Снимок экрана: ответ от вызова средства MCP в окне чата GitHub Copilot.

Если веб-приложение уже имеет полезные функции, такие как покупки, бронирование отелей или управление данными, это легко сделать эти возможности доступными для:

Добавив сервер MCP в веб-приложение, вы можете агенту понять и использовать возможности приложения при реагировании на запросы пользователей. Это означает, что ваше приложение может сделать все, что может сделать агент.

  • Добавьте сервер MCP в веб-приложение.
  • Протестируйте сервер MCP локально в режиме агента чата GitHub Copilot.
  • Разверните сервер MCP в Службе приложений Azure и подключитесь к нему в GitHub Copilot Chat.

Предпосылки

В этом руководстве предполагается, что вы работаете с примером, используемым в развертывании веб-приложения Python FastAPI с помощью PostgreSQL в Azure.

По крайней мере откройте пример приложения в GitHub Codespaces и разверните приложение, выполнив его azd up.

Открыть в GitHub Codespaces

Добавление сервера MCP в веб-приложение

  1. В обозревателе пространства кода откройте src/pyproject.toml, добавьте mcp[cli] в список зависимостей, как показано в следующем примере:

    dependencies = [
        ...
        "mcp[cli]",
    ]
    
  2. В src/fastapi_app создайте файл с именем mcp_server.py и вставьте следующий код инициализации сервера MCP в файл:

    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)
    

    Инициализатор FastMCP() создает сервер MCP с помощью шаблона режима без отслеживания состояния в пакете SDK для Python MCP. По умолчанию ее потоковая конечная точка HTTP устанавливается в /mcp подпат.

    • Декоратор @mcp.tool() добавляет средство на сервер MCP с его реализацией.
    • Описание функции инструмента помогает вызывающей агенту понять, как использовать средство и его параметры.

    Средства дублируют существующие функции отзывов ресторанов в веб-приложении FastAPI на основе форм. Если вы хотите, вы можете добавить дополнительные средства для обновления и удаления функций.

  3. В src/fastapi_app/app.py найдите строку (строка app = FastAPI() 24) и замените ее следующим кодом:

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

    Этот код подключает потоковую конечную точку HTTP сервера MCP к существующему приложению FastAPI по пути /mcp. Вместе с путьом по умолчанию к потоковой конечной точке HTTP полный путь /mcp/mcp.

Тестирование сервера MCP локально

  1. В терминале пространства кода запустите приложение со следующими командами:

    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. Выберите "Открыть в браузере", а затем добавьте несколько ресторанов и отзывов.

    Оставьте uvicorn работу. Сервер MCP работает сейчас http://localhost:8000/mcp/mcp .

  3. Вернитесь в пространство кода, откройте Copilot Chat, а затем выберите режим агента в поле запроса.

  4. Нажмите кнопку "Сервис" , а затем щелкните значок "Добавить СЕРВЕР MCP" в правом верхнем углу всплывающего окна.

    Снимок экрана: добавление сервера MCP в режиме агента чата GitHub Copilot.

  5. Выберите HTTP (HTTP или Server-Sent события).

  6. Введите http://localhost:8000/mcp/mcp.

  7. Введите идентификатор сервера,введите restaurant_ratings или любое нужное имя.

  8. Выберите параметры рабочей области.

  9. В новом окне чата Copilot введите примерно следующее : "Показать мне рейтинги ресторана".

  10. По умолчанию GitHub Copilot показывает подтверждение безопасности при вызове сервера MCP. Нажмите Продолжить.

    Снимок экрана: сообщение о безопасности по умолчанию из вызова MCP в GitHub Copilot Chat.

    Теперь вы увидите ответ, указывающий на успешное выполнение вызова средства MCP.

    Снимок экрана: ответ от вызова средства MCP в окне чата GitHub Copilot.

Развертывание сервера MCP в службе приложений

  1. Вернитесь в терминал пространства кода, разверните изменения, зафиксировав изменения (метод GitHub Actions) или запустите azd up (метод Azure Developer CLI).

  2. В выходных данных AZD найдите URL-адрес приложения. URL-адрес выглядит следующим образом в выходных данных AZD:

     Deploying services (azd deploy)
    
       (✓) Done: Deploying service web
       - Endpoint: <app-url>
     
  3. После azd up завершения откройте vscode/mcp.json. Измените URL-адрес <app-url>/mcp/mcpна .

  4. Над измененной конфигурацией сервера MCP нажмите кнопку "Пуск".

    Снимок экрана: запуск сервера MCP вручную из локального mcp.json файла.

  5. Запустите новое окно чата GitHub Copilot. Вы должны иметь возможность просматривать рейтинги ресторанов, а также создавать новые рестораны и новые рейтинги в агенте Copilot.

Рекомендации по обеспечению безопасности

Когда сервер MCP вызывается агентом, управляемым большими языковыми моделями (LLM), учитывайте атаки на внедрение запросов . Рассмотрим следующие рекомендации по обеспечению безопасности:

  • Аутентификация и авторизация: Защитите сервер MCP с помощью аутентификации Microsoft Entra, чтобы доступ получали только авторизованные пользователи или агенты. См. пошаговое руководство по вызовам протокола контекста безопасной модели к Службе приложений Azure из Visual Studio Code с аутентификацией Microsoft Entra.
  • Проверка входных данных и санитизация. Всегда проверяйте входящие данные, чтобы предотвратить недопустимые или вредоносные входные данные. Для приложений Python используйте такие библиотеки, как Pydantic , чтобы применить правила проверки данных с выделенными входными моделями (например, RestaurantCreate и ReviewCreate). Ознакомьтесь с документацией по рекомендациям и сведениям о реализации.
  • HTTPS: В примере используется служба приложений Azure, которая по умолчанию применяет ПРОТОКОЛ HTTPS и предоставляет бесплатные СЕРТИФИКАТЫ TLS/SSL для шифрования данных во время передачи.
  • Принцип наименьшей привилегии: предоставление только необходимых средств и данных, необходимых для вашего варианта использования. Избегайте предоставления конфиденциальных операций, если это не необходимо.
  • Ограничение скорости и регулирование. Используйте управление API или пользовательское ПО промежуточного слоя для предотвращения злоупотреблений и атак типа "отказ в обслуживании".
  • Ведение журнала и мониторинг. Доступ к журналам и использование конечных точек MCP для аудита и обнаружения аномалий. Отслеживайте подозрительные действия.
  • Конфигурация CORS: ограничить запросы между источниками доверенным доменам, если сервер MCP обращается из браузеров. Дополнительные сведения см. в разделе "Включение CORS".
  • Регулярные обновления: обновляйте зависимости, чтобы устранить известные уязвимости.

Дополнительные ресурсы

Интеграция ИИ в приложения Службы приложений Azure