Поделиться через


Руководство по развертыванию сервера Python MCP в приложениях контейнеров Azure

В этом руководстве вы создадите сервер протокола Model Context Protocol (MCP), открывающий средства управления задачами с помощью FastAPI и пакета MCP Python SDK. Вы развертываете сервер в приложениях контейнеров Azure и подключаетесь к нему из чата GitHub Copilot в VS Code.

Изучив это руководство, вы:

  • Создание приложения FastAPI, предоставляющего средства MCP
  • Тестирование сервера MCP локально с помощью GitHub Copilot
  • Контейнеризация и развертывание приложения в приложениях контейнеров Azure
  • Подключите GitHub Copilot к развернутому серверу MCP

Предпосылки

Создайте каркас приложения

В этом разделе вы создаете новый проект на Python с FastAPI и SDK MCP для Python.

  1. Создайте каталог проекта и настройте виртуальную среду:

    mkdir tasks-mcp-server && cd tasks-mcp-server
    python -m venv .venv
    source .venv/bin/activate
    
  2. Создание requirements.txt:

    fastapi>=0.115.0
    uvicorn>=0.30.0
    mcp[cli]>=1.2.0
    
  3. Установите зависимости:

    pip install -r requirements.txt
    
  4. Создайте task_store.py для хранилища данных в памяти:

    from dataclasses import dataclass, field
    from datetime import datetime, timezone
    
    
    @dataclass
    class TaskItem:
        id: int
        title: str
        description: str
        is_complete: bool = False
        created_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
    
        def to_dict(self) -> dict:
            return {
                "id": self.id,
                "title": self.title,
                "description": self.description,
                "is_complete": self.is_complete,
                "created_at": self.created_at.isoformat(),
            }
    
    
    class TaskStore:
        def __init__(self):
            self._tasks: list[TaskItem] = [
                TaskItem(1, "Buy groceries", "Milk, eggs, bread"),
                TaskItem(2, "Write docs", "Draft the MCP tutorial", True),
            ]
            self._next_id = 3
    
        def get_all(self) -> list[dict]:
            return [t.to_dict() for t in self._tasks]
    
        def get_by_id(self, task_id: int) -> dict | None:
            task = next((t for t in self._tasks if t.id == task_id), None)
            return task.to_dict() if task else None
    
        def create(self, title: str, description: str) -> dict:
            task = TaskItem(self._next_id, title, description)
            self._next_id += 1
            self._tasks.append(task)
            return task.to_dict()
    
        def toggle_complete(self, task_id: int) -> dict | None:
            task = next((t for t in self._tasks if t.id == task_id), None)
            if task is None:
                return None
            task.is_complete = not task.is_complete
            return task.to_dict()
    
        def delete(self, task_id: int) -> bool:
            task = next((t for t in self._tasks if t.id == task_id), None)
            if task is None:
                return False
            self._tasks.remove(task)
            return True
    
    
    # For demonstration only — not thread-safe.
    store = TaskStore()
    

    Класс TaskItem данных определяет модель данных с методом to_dict() сериализации. Класс TaskStore управляет списком в памяти, предварительно заполненным примерами данных и предоставляет методы CRUD. Одноэлементный объект уровня модуля store используется во всем приложении для простоты.

Определение средств MCP

В этом разделе описаны средства MCP, которые модель ИИ может вызывать и подключать сервер MCP в приложении FastAPI.

  1. Создание mcp_server.py:

    from mcp.server.fastmcp import FastMCP
    from task_store import store
    
    mcp = FastMCP("TasksMCP", stateless_http=True)
    
    
    @mcp.tool()
    async def list_tasks() -> list[dict]:
        """List all tasks with their ID, title, description, and completion status."""
        return store.get_all()
    
    
    @mcp.tool()
    async def get_task(task_id: int) -> dict | None:
        """Get a single task by its numeric ID.
    
        Args:
            task_id: The numeric ID of the task to retrieve.
        """
        return store.get_by_id(task_id)
    
    
    @mcp.tool()
    async def create_task(title: str, description: str) -> dict:
        """Create a new task with the given title and description. Returns the created task.
    
        Args:
            title: A short title for the task.
            description: A detailed description of what the task involves.
        """
        return store.create(title, description)
    
    
    @mcp.tool()
    async def toggle_task_complete(task_id: int) -> str:
        """Toggle a task's completion status between complete and incomplete.
    
        Args:
            task_id: The numeric ID of the task to toggle.
        """
        task = store.toggle_complete(task_id)
        if task:
            status = "complete" if task["is_complete"] else "incomplete"
            return f"Task {task['id']} is now {status}."
        return f"Task with ID {task_id} not found."
    
    
    @mcp.tool()
    async def delete_task(task_id: int) -> str:
        """Delete a task by its numeric ID.
    
        Args:
            task_id: The numeric ID of the task to delete.
        """
        if store.delete(task_id):
            return f"Task {task_id} deleted."
        return f"Task with ID {task_id} not found."
    

    Основные моменты:

    • FastMCP("TasksMCP", stateless_http=True) создает сервер MCP с помощью шаблона HTTP без отслеживания состояния в пакете SDK для Python. По умолчанию потоковое подключение HTTP указывает на /mcp подпуть.
    • Каждая @mcp.tool() функция становится вызываемым инструментом. Документация по функциям и заметки параметров помогают модели ИИ понять, как использовать каждое средство.
  2. Создайте app.py. Этот файл определяет приложение FastAPI, которое подключает сервер MCP:

    from contextlib import AsyncExitStack, asynccontextmanager
    
    from fastapi import FastAPI
    from fastapi.responses import JSONResponse
    
    from mcp_server import mcp
    
    
    @asynccontextmanager
    async def lifespan(app: FastAPI):
        async with AsyncExitStack() as stack:
            await stack.enter_async_context(mcp.session_manager.run())
            yield
    
    
    app = FastAPI(lifespan=lifespan)
    app.mount("/", mcp.streamable_http_app())
    
    
    @app.get("/health")
    async def health():
        return JSONResponse({"status": "healthy"})
    

    Приложение сервера MCP монтируется на корневой директории (/). Потоковая конечная точка HTTP пакета SDK по умолчанию имеет /mcpзначение, поэтому полный путь к конечной точке равен /mcp.

    /health Отдельная конечная точка используется для проверок работоспособности контейнерных приложений. Конечные точки MCP ожидают JSON-RPC запросов POST и не подходят для проверки работоспособности.

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

Перед развертыванием в Azure убедитесь, что сервер MCP работает, запустив его локально и подключившись к нему с помощью GitHub Copilot.

  1. Запустите приложение:

    uvicorn app:app --reload --port 8080
    
  2. Откройте VS Code, а затем откройте чат Copilot и выберите режим агента .

  3. Нажмите кнопку "Сервис" , а затем нажмите кнопку "Добавить дополнительные инструменты" ...>Добавьте СЕРВЕР MCP.

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

  5. Введите URL-адрес сервера: http://localhost:8080/mcp

  6. Введите идентификатор сервера: tasks-mcp

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

  8. В новом запросе чата Copilot введите "Показать мне все задачи"

  9. Нажмите кнопку "Продолжить", когда Copilot запрашивает подтверждение средства MCP.

Вы увидите список задач, возвращенный из выделенного в памяти хранилища.

Подсказка

Попробуйте использовать другие запросы, такие как "Создать задачу для проверки pr", "Пометить задачу 1 как завершенную" или "Удалить задачу 2".

Помещение приложения в контейнер

Упаковайте приложение как контейнер Docker, чтобы протестировать его локально перед развертыванием в Azure.

  1. DockerfileСоздайте:

    FROM python:3.12-slim
    
    WORKDIR /app
    
    COPY requirements.txt .
    RUN pip install --no-cache-dir -r requirements.txt
    
    COPY . .
    
    EXPOSE 8080
    CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8080"]
    

    Dockerfile использует базовый образ Python 3.12, устанавливает зависимости из requirements.txt, а затем копирует код приложения. Uvicorn обслуживает приложение FastAPI через порт 8080.

  2. Проверьте сборку контейнера и выполняется локально:

    docker build -t tasks-mcp-server .
    docker run -p 8080:8080 tasks-mcp-server
    

    Убедитесь, что точка проверки работоспособности отвечает: curl http://localhost:8080/health

Развертывание в приложениях контейнеров Azure

После контейнеризации приложения разверните его в приложениях контейнеров Azure с помощью Azure CLI. Команда az containerapp up создает образ контейнера в облаке, поэтому для этого шага не требуется Docker.

  1. Задайте переменные среды:

    RESOURCE_GROUP="mcp-tutorial-rg"
    LOCATION="eastus"
    ENVIRONMENT_NAME="mcp-env"
    APP_NAME="tasks-mcp-server-py"
    
  2. Создайте группу ресурсов и среду для контейнерных приложений:

    az group create --name $RESOURCE_GROUP --location $LOCATION
    
    az containerapp env create \
        --name $ENVIRONMENT_NAME \
        --resource-group $RESOURCE_GROUP \
        --location $LOCATION
    
  3. Разверните приложение контейнера:

    az containerapp up \
        --name $APP_NAME \
        --resource-group $RESOURCE_GROUP \
        --environment $ENVIRONMENT_NAME \
        --source . \
        --ingress external \
        --target-port 8080
    
  4. Настройка CORS:

    az containerapp ingress cors enable \
        --name $APP_NAME \
        --resource-group $RESOURCE_GROUP \
        --allowed-origins "*" \
        --allowed-methods "GET,POST,DELETE,OPTIONS" \
        --allowed-headers "*"
    

    Замечание

    Для продакшна замените подстановочные знаки определенными доверенными источниками. См. раздел "Безопасные серверы MCP" в приложениях контейнеров.

  5. Проверьте развертывание:

    APP_URL=$(az containerapp show \
        --name $APP_NAME \
        --resource-group $RESOURCE_GROUP \
        --query "properties.configuration.ingress.fqdn" -o tsv)
    
    curl https://$APP_URL/health
    

Подключите GitHub Copilot к развернутому серверу

Теперь, когда сервер MCP запущен в Azure, настройте VS Code для подключения GitHub Copilot к развернутой конечной точке.

  1. Создание или обновление .vscode/mcp.json:

    {
        "servers": {
            "tasks-mcp-server": {
                "type": "http",
                "url": "https://<your-app-fqdn>/mcp"
            }
        }
    }
    

    Замените <your-app-fqdn> полным доменным именем из выходных данных развертывания.

  2. В VS Code откройте чат Copilot в режиме агента.

  3. Убедитесь, что tasks-mcp-server отображается в списке инструментов. При необходимости нажмите кнопку "Пуск ".

  4. Проверьте запрос, например "Создать задачу для развертывания промежуточной среды".

Настройка масштабирования для интерактивного использования

По умолчанию приложения контейнеров Azure могут масштабироваться до нуля реплик. Для серверов MCP, которые обслуживают интерактивных клиентов, таких как Copilot, холодные запуски вызывают заметные задержки. Задайте минимальное число реплик для поддержания по крайней мере одного экземпляра:

az containerapp update \
    --name $APP_NAME \
    --resource-group $RESOURCE_GROUP \
    --min-replicas 1

Вопросы безопасности

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

  • Проверка подлинности и авторизация. Защита сервера MCP с помощью идентификатора Microsoft Entra. См. раздел "Безопасные серверы MCP" в приложениях контейнеров.
  • Проверка входных данных: всегда проверяйте параметры средства. Используйте Pydantic для принудительной валидации входных данных инструментов.
  • HTTPS: приложения контейнеров Azure по умолчанию применяют ПРОТОКОЛ HTTPS с автоматическими сертификатами TLS.
  • Минимальные привилегии: предоставляйте только те инструменты, которые требуются для вашего случая. Избегайте средств, выполняющих разрушительные операции без подтверждения.
  • CORS: ограничение разрешенных источников доверенным доменам в рабочей среде.
  • Ведение журнала и мониторинг: Запись вызовов инструментов MCP для аудита. Используйте Azure Monitor и Log Analytics.

Очистите ресурсы

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

az group delete --resource-group $RESOURCE_GROUP --yes --no-wait

Следующий шаг