Выбор фреймворка (FastAPI) и определение эндпоинтов

Вы уже видели, как MCP-агенты автоматизируют рутину в n8n — обрабатывают письма, генерируют отчёты, обновляют задачи. Теперь настал момент перейти от пользователя к создателю. 🚀

Мы создаём свой MCP-сервер на Python — основу, на которой будет строиться любой ИИ-агент. И первый шаг — выбор правильного фреймворка. От него зависит скорость разработки, надёжность и возможность быстро масштабироваться.

Почему FastAPI — лучший выбор для MCP-сервера в 2026 году

Когда речь идёт о создании сервера для ИИ-агента, важно не просто "запустить API", а обеспечить быструю, надёжную и типизированную коммуникацию между клиентом (например, Cursor или n8n) и LLM.

Долгое время разработчики использовали Flask — простой и понятный фреймворк. Но он не поддерживает асинхронность по умолчанию, требует ручной настройки валидации данных и документации. Это замедляет разработку и увеличивает риск ошибок.

FastAPI — современная альтернатива, идеально подходящая для MCP. Вот почему:

  • Асинхронная обработка запросов — сервер может одновременно обрабатывать множество запросов от LLM и клиентов.
  • Встроенная валидация через Pydantic — автоматически проверяет формат входящих и исходящих данных.
  • Автоматическая OpenAPI-документация — по адресу /docs вы сразу видите, какие эндпоинты доступны и как с ними работать.
  • Поддержка WebSocket и SSE — пригодится в будущем для потоковой генерации от LLM.

💡 FastAPI использует Uvicorn как ASGI-сервер, что делает его в разы быстрее, чем традиционные WSGI-приложения вроде Flask. Это критично при работе с ИИ, где задержки напрямую влияют на пользовательский опыт.

Обязательные эндпоинты MCP-сервера

MCP — это протокол, а значит, сервер должен предоставлять строго определённые точки взаимодействия. Без них клиент (например, Cursor) просто не сможет с вами работать.

Мы сосредоточимся на трёх ключевых эндпоинтах:

/capabilities

Это "визитная карточка" вашего сервера. Когда клиент подключается, он сначала запрашивает /capabilities, чтобы понять, что умеет делать агент.

В ответ сервер возвращает JSON с описанием доступных инструментов (tools), поддерживаемых функций и метаданных.

Пример ответа:

{
  "name": "my-personal-agent",
  "description": "Агент для генерации и анализа учебных курсов",
  "tools": []
}

⚠️ Пока у нас нет инструментов — список пуст. Мы добавим их позже. Но эндпоинт должен быть.

/prompt

Это основной канал общения с LLM. Клиент отправляет промпт, контекст и, возможно, инструменты. Сервер передаёт это в LLM, обрабатывает ответ и возвращает результат.

Важно: этот эндпоинт должен уметь:

  • Принимать JSON с полем prompt
  • Сохранять и передавать контекст
  • Обрабатывать вызовы инструментов (о чём позже)

Пока реализуем заглушку — echo-ответ.

/tool

Когда LLM решает, что нужно выполнить действие (например, получить данные или сгенерировать PDF), он вызывает инструмент через этот эндпоинт.

Сервер получает запрос с именем инструмента и параметрами, выполняет логику и возвращает результат.

Пока — просто заглушка. Но структура должна быть.

Минимальный MCP-сервер на FastAPI

Создадим базовый сервер с тремя эндпоинтами. Это будет наш "рабочий скелет".

# main.py
from fastapi import FastAPI
from pydantic import BaseModel
from typing import List, Optional

app = FastAPI(title="MCP Agent Server", version="0.1.0")

# Модель для /capabilities
class ToolModel(BaseModel):
    name: str
    description: str
    input_schema: dict

class CapabilitiesResponse(BaseModel):
    name: str
    description: str
    tools: List[ToolModel] = []

# Эндпоинт /capabilities
@app.get("/capabilities", response_model=CapabilitiesResponse)
async def get_capabilities():
    return {
        "name": "course-generator-agent",
        "description": "Агент для создания персональных курсов",
        "tools": []
    }

# Модель для /prompt
class PromptRequest(BaseModel):
    prompt: str
    context: Optional[dict] = None

class PromptResponse(BaseModel):
    response: str
    context: dict

# Эндпоинт /prompt
@app.post("/prompt", response_model=PromptResponse)
async def handle_prompt(request: PromptRequest):
    # Пока просто возвращаем echo + пустой контекст
    return {
        "response": f"Промпт получен: {request.prompt}",
        "context": request.context or {}
    }

# Модель для /tool
class ToolCallRequest(BaseModel):
    tool_name: str
    parameters: dict

class ToolCallResponse(BaseModel):
    result: dict
    error: Optional[str] = None

# Эндпоинт /tool
@app.post("/tool", response_model=ToolCallResponse)
async def handle_tool(tool_call: ToolCallRequest):
    return {
        "result": {
            "message": f"Инструмент {tool_call.tool_name} вызван с параметрами: {tool_call.parameters}"
        }
    }

Запустить сервер можно так:

uvicorn main:app --reload

После запуска:

  • Документация доступна по http://localhost:8000/docs
  • /capabilities — GET-запрос
  • /prompt и /tool — POST-запросы с JSON

Практическое упражнение

  1. Создайте файл main.py и вставьте код выше.
  2. Убедитесь, что у вас установлены:
    pip install fastapi uvicorn
    
  3. Запустите сервер:
    uvicorn main:app --reload
    
  4. Проверьте эндпоинты:

Проверка /capabilities:

curl http://localhost:8000/capabilities

Проверка /prompt:

curl -X POST http://localhost:8000/prompt \
  -H "Content-Type: application/json" \
  -d '{"prompt": "Создай курс по Python", "context": {"user_id": 123}}'

Проверка /tool:

curl -X POST http://localhost:8000/tool \
  -H "Content-Type: application/json" \
  -d '{"tool_name": "generate_pdf", "parameters": {"title": "Курс по MCP"}}'

Если все запросы возвращают валидный JSON — вы молодцы! 🎯

💡 Pydantic автоматически валидирует входящие данные. Если вы отправите неверный JSON — FastAPI вернёт понятную ошибку. Это спасёт вас от множества багов в будущем.

Что дальше?

У нас есть рабочий сервер с тремя обязательными эндпоинтами. Но код пока находится в одном файле — так не делают в реальных проектах.

В следующей теме — Базовая структура проекта MCP-сервера — мы организуем код: вынесем эндпоинты, инструменты и конфигурацию в отдельные модули. Это сделает сервер масштабируемым и удобным для поддержки.

Вы уже создали основу. Теперь — превратим её в настоящую систему. 💪