Базовая структура проекта MCP-сервера
Мы уже знаем, что такое Model Context Protocol, понимаем его архитектуру и определили необходимые эндпоинты для нашего MCP-сервера. Теперь пришло время перейти от теории к практике — и построить прочный фундамент для нашего агента.
Хорошая структура проекта — это как архитектура дома: если фундамент крепкий, можно строить сколько угодно этажей. Если нет — даже красивый фасад быстро рухнет. Сегодня мы создадим такую структуру, которая будет масштабируемой, понятной и готовой к интеграции с любыми LLM и инструментами.
Почему структура проекта так важна?
Без чёткой структуры код быстро превращается в "спагетти" — функции смешиваются, сложно найти нужное, а добавление нового функционала становится рискованным делом.
В MCP-сервере особенно важно разделять:
- логику обработки запросов,
- реализацию инструментов (tools),
- настройки и конфигурацию.
Это позволяет:
- легко тестировать отдельные компоненты,
- быстро подключать новые инструменты,
- безопасно масштабировать проект.
💡 Совет: даже если вы пока работаете в одиночку, пишите код так, будто над ним будет работать команда. Это сэкономит вам время, когда проект начнёт расти.
Ключевые модули MCP-сервера
На прошлом шаге мы выбрали FastAPI и определили эндпоинты: /prompt, /tool, /capabilities. Теперь нужно организовать код так, чтобы каждый компонент знал своё место.
Мы выделим три основных модуля:
| Модуль | Назначение |
|---|---|
handlers | Обработка HTTP-запросов к эндпоинтам MCP |
tools | Реализация функций, которые может вызывать LLM |
config | Хранение настроек, API-ключей, параметров сервера |
Также понадобится:
main.py— точка входа в приложение,requirements.txtилиpyproject.toml— управление зависимостями,.env— хранение секретов (например, API-ключей).
Файловая структура проекта
Вот как будет выглядеть наша базовая структура:
my-mcp-server/
├── src/
│ ├── handlers/
│ │ └── __init__.py
│ ├── tools/
│ │ └── __init__.py
│ ├── config/
│ │ └── __init__.py
│ └── main.py
├── .env
├── .gitignore
├── requirements.txt
└── README.md
Разберём каждый элемент:
src/— корневая папка с исходным кодом. Удобно выделять её, чтобы отделить код от конфигурации и документации.handlers/— здесь будут функции, отвечающие за обработку/prompt,/toolи других эндпоинтов.tools/— модуль для пользовательских инструментов. Каждый инструмент — это функция, которую LLM может вызвать.config/— настройки сервера, загрузка переменных окружения, конфигурация логирования.main.py— инициализация FastAPI-приложения и подключение маршрутов..env— файл с переменными окружения (например,ANTHROPIC_API_KEY=...).requirements.txt— список зависимостей, включаяfastapi,uvicorn,python-dotenv.
Создаём структуру: пошагово
Откройте терминал и выполните:
mkdir my-mcp-server
cd my-mcp-server
python -m venv venv
source venv/bin/activate # Linux/Mac
# или venv\Scripts\activate # Windows
Создадим папки:
mkdir -p src/handlers src/tools src/config
Теперь добавим базовые файлы.
1. src/main.py — точка входа
from fastapi import FastAPI
from src.handlers import router as handlers_router
app = FastAPI(title="MCP Server", version="0.1.0")
# Подключаем маршруты
app.include_router(handlers_router)
@app.get("/")
def root():
return {"message": "MCP Server is running"}
2. src/handlers/__init__.py — обработчики эндпоинтов
from fastapi import APIRouter
router = APIRouter(prefix="/mcp")
@router.get("/capabilities")
async def get_capabilities():
return {
"version": "0.1.0",
"resources": ["/mcp/tool", "/mcp/prompt"],
"tools": []
}
@router.post("/prompt")
async def handle_prompt(data: dict):
# Пока возвращаем заглушку
return {"response": "Prompt received"}
@router.post("/tool")
async def handle_tool(data: dict):
# Здесь будет вызов инструментов
return {"result": "Tool executed"}
3. src/config/__init__.py — загрузка конфигурации
from dotenv import load_dotenv
import os
load_dotenv()
# Пример чтения переменной окружения
API_KEY = os.getenv("ANTHROPIC_API_KEY")
SERVER_PORT = int(os.getenv("SERVER_PORT", 8000))
4. .env — безопасное хранение данных
ANTHROPIC_API_KEY=your-key-here
SERVER_PORT=8000
🔐 Важно: никогда не коммитьте
.envв Git. Добавьте его в.gitignore.
Принципы организации кода
✅ Типизация
Даже в Python важно использовать аннотации типов. Это делает код понятнее и помогает избежать ошибок.
Пример с типизацией:
from typing import Dict, Any
@router.post("/prompt")
async def handle_prompt(data: Dict[str, Any]) -> Dict[str, str]:
return {"response": f"Received prompt: {data.get('prompt', '')}"}
✅ Асинхронность
Используем async/await, чтобы сервер мог обрабатывать несколько запросов одновременно — особенно важно при работе с внешними API.
import asyncio
@router.post("/tool")
async def handle_tool(data: dict):
await asyncio.sleep(1) # Имитация долгой операции
return {"result": "Tool completed"}
✅ Читаемость
Каждая функция должна решать одну задачу. Название — понятное. Комментарии — только где необходимо.
Практическое задание
Создайте структуру проекта, как описано выше. Убедитесь, что:
- Виртуальное окружение активировано.
- Установлены зависимости:
pip install fastapi uvicorn python-dotenv - Сервер запускается:
uvicorn src.main:app --reload - Эндпоинт
http://localhost:8000/mcp/capabilitiesвозвращает JSON с возможностями.
Проверьте, что всё работает — это ваш первый рабочий MCP-сервер!
Что дальше?
Мы создали каркас, но пока наш агент "не помнит" предыдущих взаимодействий. А ведь именно контекст — сердце любого ИИ-агента.
В следующей теме мы изучим, как сохранять и передавать состояние между запросами. Вы узнаете, как использовать Redis и файловое хранилище, чтобы агент мог "помнить" диалог, настройки и промежуточные результаты.
Это ключевой шаг к созданию по-настоящему умного и полезного агента.
Готовы добавить память своему ИИ? Тогда — вперёд! 🚀