Выбор фреймворка (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
Практическое упражнение
- Создайте файл
main.pyи вставьте код выше. - Убедитесь, что у вас установлены:
pip install fastapi uvicorn - Запустите сервер:
uvicorn main:app --reload - Проверьте эндпоинты:
Проверка /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-сервера — мы организуем код: вынесем эндпоинты, инструменты и конфигурацию в отдельные модули. Это сделает сервер масштабируемым и удобным для поддержки.
Вы уже создали основу. Теперь — превратим её в настоящую систему. 💪