Создание собственного MCP-сервера
Подключить готовый сервер — несколько команд. Но рано или поздно нужный инструмент окажется только внутри корпоративной системы, старого REST API или самописного скрипта. Именно тогда создание собственного MCP-сервера из опции превращается в необходимость.
Эта статья — путь от чистого листа до рабочего сервера. Мы разберём FastMCP (Python), официальный TypeScript SDK, отладку через MCP Inspector и паттерн code execution, который меняет архитектуру агентных систем.
Когда нужен собственный сервер
Готовых серверов для GitHub, Postgres, Slack и десятков других сервисов уже хватает — их мы разбирали в следующей статье. Собственный сервер нужен когда:
- Система внутренняя (корпоративный API, легаси-бэкенд, внутренняя БД)
- Готовый сервер существует, но не даёт нужный уровень контроля (своя авторизация, кастомная трансформация данных)
- Нужна бизнес-логика на стороне сервера — агрегация из нескольких источников, кэширование, фильтрация чувствительных полей
Выбор стека: FastMCP или TypeScript SDK
Для Python-проектов выбор очевиден — FastMCP. Это высокоуровневый фреймворк, который в 2024 году вошёл в официальный Python MCP SDK и сегодня, по оценкам авторов, лежит в основе ~70% всех MCP-серверов. Декоратором @mcp.tool вы получаете автоматическую генерацию JSON Schema из аннотаций типов, валидацию параметров и документацию — без единой строки бойлерплейта.
Для TypeScript-проектов — официальный @modelcontextprotocol/sdk от Anthropic. Чуть больше кода вручную, зато полный контроль над протоколом и нативная интеграция с Node.js-экосистемой.
flowchart TD
A[Идея интеграции] --> B{Существует\nготовый сервер?}
B -- Да --> C[claude mcp add]
B -- Нет --> D[Создать сервер]
D --> E{Стек?}
E -- Python --> F[FastMCP\npip install fastmcp]
E -- TypeScript --> G[@modelcontextprotocol/sdk\nnpm install]
F --> H[Определить @mcp.tool\n@mcp.resource @mcp.prompt]
G --> H2[Определить registerTool\nс Zod-схемой]
H --> I[MCP Inspector\nnpx @modelcontextprotocol/inspector]
H2 --> I
I -- Ошибки --> H
I -- OK --> J[claude mcp add\nили .mcp.json]
J --> K[Сервер доступен в Claude Code]FastMCP: от нуля до работающего сервера
Установка — одна строка:
pip install fastmcp
# или через uv (рекомендуется для изоляции)
uv add fastmcpМинимальный сервер с одним инструментом:
from fastmcp import FastMCP
mcp = FastMCP("my-server")
@mcp.tool
def search_docs(query: str, limit: int = 10) -> list[dict]:
"""Ищет документы во внутренней базе знаний."""
# Здесь реальная логика — запрос к БД, Elasticsearch и т.д.
return [{"id": 1, "title": f"Результат для '{query}'", "score": 0.95}]
if __name__ == "__main__":
mcp.run() # stdio по умолчаниюОбратите внимание: query: str и limit: int = 10 — это всё, что нужно для генерации схемы. FastMCP извлечёт типы из аннотаций и docstring станет описанием инструмента для модели.
#### Инструменты (tools)
Инструменты — основной примитив для действий. Правила хорошего инструмента:
- Имя — глагол:
search_,create_,update_,delete_ - Docstring — что делает и когда использовать (это читает Claude, а не человек)
- Типы — точные:
str,int,list[str],Literal["asc", "desc"] - Возвращаемое значение — JSON-сериализуемое (dict, list, str, int)
from typing import Literal
from pydantic import Field
@mcp.tool
def query_database(
sql: str = Field(description="SELECT-запрос. Только чтение."),
timeout_ms: int = Field(default=5000, ge=100, le=30000)
) -> dict:
"""Выполняет SQL-запрос к аналитической БД (read-only).
Используйте для получения агрегированных данных и отчётов.
Не поддерживает INSERT/UPDATE/DELETE — только SELECT.
"""
# Реализация
return {"rows": [], "count": 0}Field из Pydantic позволяет добавить описание к каждому параметру отдельно — это особенно важно для инструментов с неочевидной семантикой.
#### Ресурсы (resources)
Ресурсы — read-only данные, которые Claude может прочитать по URI. В отличие от инструментов, они не выполняют действия — только отдают контент.
import json
# Статический ресурс — фиксированный URI
@mcp.resource("config://app/settings")
def get_settings() -> str:
"""Текущие настройки приложения в формате JSON."""
return json.dumps({"env": "production", "version": "2.1.0"})
# Динамический ресурс — параметр в URI
@mcp.resource("user://{user_id}/profile")
def get_user_profile(user_id: str) -> str:
"""Профиль пользователя по ID."""
# Запрос к базе
return json.dumps({"id": user_id, "name": "Alice"})Когда использовать ресурс вместо инструмента? Простое правило: если операция — чтение постоянно существующих данных (конфиг, профиль, документ), используйте ресурс. Если операция меняет состояние или требует параметров, влияющих на логику выполнения, — инструмент.
#### Промпты (prompts)
Промпты — шаблоны сообщений, которые сервер предлагает клиентам. Удобны для стандартизации запросов к вашей системе:
from fastmcp import FastMCP
from fastmcp.prompts import Message
@mcp.prompt
def analyze_ticket(ticket_id: str, language: str = "ru") -> list[Message]:
"""Шаблон для анализа тикета поддержки."""
return [
Message(role="user", content=f"Проанализируй тикет #{ticket_id} на языке {language}")
]#### Запуск: stdio vs HTTP
# stdio — для локальных claude mcp add
mcp.run() # или mcp.run(transport="stdio")
# HTTP — для облачного деплоя
mcp.run(transport="streamable-http", host="0.0.0.0", port=8000)Для подключения stdio-сервера к Claude Code:
claude mcp add --transport stdio my-server -- python /path/to/server.pyДля HTTP:
claude mcp add --transport http my-server http://localhost:8000/mcpTypeScript SDK: альтернатива для Node.js-стека
Если проект уже на TypeScript, официальный SDK даёт нативную интеграцию:
npm install @modelcontextprotocol/sdkimport { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({ name: "my-ts-server", version: "1.0.0" });
server.registerTool(
"fetch_metrics",
{
description: "Получить метрики сервиса за период",
inputSchema: z.object({
service: z.string().describe("Имя сервиса"),
period: z.enum(["1h", "24h", "7d"]).default("24h")
})
},
async ({ service, period }) => {
// Логика запроса к Prometheus, Datadog и т.д.
return { content: [{ type: "text", text: JSON.stringify({ service, p99: 45 }) }] };
}
);
const transport = new StdioServerTransport();
await server.connect(transport);Zod используется для валидации — та же экосистема, что в большинстве TypeScript-проектов. Схема генерируется автоматически из Zod-объекта.
MCP Inspector: отладка до подключения к Claude
Прежде чем подключать сервер к Claude Code, нужно убедиться, что он работает правильно. MCP Inspector — интерактивный отладчик, запускаемый через npx без установки:
# Для Python-сервера
npx @modelcontextprotocol/inspector uv run python server.py
# Для TypeScript-сервера
npx @modelcontextprotocol/inspector node dist/index.js
# Для PyPI-пакета
npx @modelcontextprotocol/inspector uvx my-mcp-serverInspector открывает браузерный UI с несколькими вкладками:
- Tools — список всех инструментов, их схемы, форма для ручного вызова и результат. Здесь проверяем правильность схем и поведение при edge cases
- Resources — список ресурсов, возможность прочитать содержимое каждого
- Prompts — список шаблонов, вызов с параметрами
- Notifications — все логи сервера в реальном времени, включая ошибки протокола
Типичный workflow отладки:
1. Запустить Inspector с сервером
2. Открыть вкладку Tools и вызвать каждый инструмент с тестовыми данными
3. Проверить схему: правильные типы, описания, обязательность полей
4. Подать невалидные входные данные — убедиться, что сервер возвращает нормальные ошибки, а не крашится
5. Только потом подключать через claude mcp add
Инспектор особенно полезен при работе с ресурсами-шаблонами: можно напрямую передать user://alice/profile и увидеть, что отдаёт сервер.
Code execution with MCP: другой уровень
Это архитектурный паттерн, который Anthropic описывает в отдельной статье. Суть: вместо того чтобы вызывать каждый MCP-инструмент по одному через агента, дать агенту инструмент выполнения кода — и пусть он сам пишет программы, которые оркеструют вызовы.
Стандартный подход (N вызовов инструментов):
[Claude] → tool_call: get_transcript(meeting_id) → [150k tokens ответа]
[Claude] → tool_call: create_salesforce_record(data) → ...Code execution подход (2 вызова):
[Claude] → tool_call: execute_code("""
import servers.google_drive as drive
import servers.salesforce as sf
transcript = drive.get_transcript(meeting_id)
summary = transcript[:500] # фильтрация ДО возврата в контекст
sf.create_record(summary=summary, ...)
""")Результат, описанный Anthropic: реальный воркфлоу с передачей транскрипта и записью в Salesforce сократил потребление токенов с ~150 000 до ~2 000 — экономия 98.7%. Промежуточные данные не покидают среду выполнения и не засоряют контекстное окно.
Для реализации MCP-сервер экспонирует инструмент execute_code, который принимает Python/TypeScript-строку и выполняет её в изолированной среде. Остальные серверы организованы как модули (папка ./servers/google-drive/, ./servers/salesforce/) с импортируемыми функциями. Агент может прогрессивно обнаруживать их — читать директории, загружать только нужные определения.
Паттерн подходит не везде: нужна изолированная среда выполнения (sandbox), явный контроль над тем, какой код разрешён, и серьёзная работа по безопасности. Но для внутренних корпоративных агентов, работающих с большими объёмами данных, — это принципиальное изменение модели.
Путь к production
Когда сервер проверен через Inspector и работает локально, следующие шаги:
Докеризация и деплой:
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY server.py .
EXPOSE 8000
CMD ["python", "server.py"]Коммандный доступ через .mcp.json:
{
"mcpServers": {
"my-server": {
"type": "streamable-http",
"url": "${MY_SERVER_URL:-http://localhost:8000}/mcp"
}
}
}URL через переменную окружения — каждый разработчик подставляет свой адрес (локальный или staging), конфиг коммитится в git. Это тот же паттерн из предыдущей статьи.
Для аутентификации в production рекомендуется OAuth 2.0 (сервер реализует PKCE-флоу) или статические API-ключи через Authorization: Bearer ${TOKEN} в заголовках. FastMCP имеет встроенную поддержку обоих вариантов.
Логирование и мониторинг: MCP Inspector показывает логи в реальном времени при разработке. В production логируйте каждый вызов инструмента с параметрами и временем выполнения — это поможет отлаживать агентное поведение, которое иначе очень сложно воспроизвести.
See also
- Model Context Protocol: архитектура и основы — примитивы tools/resources/prompts и транспорты на уровне спецификации
- Подключение MCP-серверов в Claude Code — как зарегистрировать собственный сервер через
claude mcp addи.mcp.json - Практика: GitHub, базы данных и веб-API через MCP — реальные интеграции и паттерны безопасности
- Субагенты и контекстная изоляция — когда вместо MCP правильнее использовать субагента
- Claude Agent SDK: программная сборка агентов — как Agent SDK взаимодействует с MCP-серверами программно
- Промпт-кэширование, батчи и оптимизация затрат — code execution with MCP как способ снизить токен-расходы