Ваши менеджеры тратят часы на поиск данных по разным системам - прайс в Excel, клиенты в CRM, договоры в папке. Claude мог бы всё это видеть и отвечать мгновенно, но он не подключён к вашим базам. MCP-сервер решает это за вечер: вы пишете 20 строк кода, и Claude сам лезет в ваши данные. Разберём на примере стройфирмы, у которой есть прайс на работы и типовой договор подряда. Пример вымышленный, но схема рабочая.
FastMCP позволяет написать рабочий MCP (Model Context Protocol - протокол подключения внешних инструментов к языковой модели)-сервер с одним инструментом примерно за 20 строк Python. Декораторы сами генерируют JSON-схемы, транспорт настраивается одной строкой, а Claude Desktop начинает видеть ваши инструменты после редактирования одного JSON-файла. Разберём от установки до продакшн-деплоя.
Что такое MCP: открытый протокол Anthropic (ноябрь 2024) для стандартизированного предоставления контекста LLM; к 2026 поддерживают OpenAI, Google, все мажорные вендоры; 97M+ ежемесячных загрузок SDK
Model Context Protocol появился в ноябре 2024 года как ответ Anthropic на проблему изолированных языковых моделей. До MCP каждая интеграция LLM с внешним сервисом писалась по-своему, не было переиспользования.
MCP стандартизировал: любой сервис становится MCP-сервером по единому протоколу, любой LLM-клиент подключается к нему без кастомной интеграции.
Динамика роста показывает, что протокол принят рынком: 97+ миллионов ежемесячных загрузок SDK к началу 2026 года. OpenAI объявил поддержку MCP в своих продуктах. Google добавил в Gemini. Cursor, Zed, VS Code, Claude Code, n8n - все крупные AI-инструменты поддерживают MCP как клиент.
На mcp.so зарегистрировано более 6000 готовых серверов. Но готовый сервер не всегда есть для вашей конкретной задачи: внутренние базы данных, корпоративные API, нестандартная логика. Для этого и нужно уметь писать свой сервер.
Концептуально MCP-сервер - это процесс (или HTTP-сервис), который по запросу клиента:
- Возвращает список доступных инструментов (tools)
- Выполняет вызов инструмента с параметрами
- Возвращает данные (resources) по URI
- Предоставляет шаблоны промптов (prompts)
Подробнее про концепцию и архитектуру - в обзорной статье про MCP.
Установка: uv add 'mcp[cli]' httpx; структура проекта, pyproject.toml с точкой входа
Для разработки MCP-серверов рекомендуется использовать uv - быстрый менеджер пакетов Python.
Установка uv (если нет):
Команды ниже запускаются в терминале и выполняют установку или конфигурацию.
curl -LsSf https://astral.sh/uv/install.sh | sh
Создание проекта:
Команды ниже запускаются в терминале и выполняют установку или конфигурацию.
uv init my-mcp-server
cd my-mcp-server
uv add 'mcp[cli]' httpx
Структура проекта:
Пример ниже показывает, как реализовать описанный шаг на практике.
my-mcp-server/
├── pyproject.toml
├── server.py # основной файл сервера
├── .python-version # версия Python (3.11+)
└── README.md
pyproject.toml с точкой входа:
Пример ниже показывает, как реализовать описанный шаг на практике.
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "my-mcp-server"
version = "0.1.0"
description = "My custom MCP server"
requires-python = ">=3.11"
dependencies = [
"mcp[cli]>=1.9.0",
"httpx>=0.28.0",
]
[project.scripts]
my-mcp-server = "server:main"
Точка входа нужна для того, чтобы uvx мог запустить сервер командой uvx my-mcp-server. Это стандартный способ распространения MCP-серверов - пользователь не устанавливает пакет глобально, uvx делает это во временной среде.
MCP SDK требует Python 3.11+. Проверьте версию: python --version.
FastMCP 3.0: from mcp.server.fastmcp import FastMCP; mcp = FastMCP('my-server'); декоратор @mcp.tool() - автоматическая генерация JSON-схемы из аннотаций типов
FastMCP - высокоуровневый слой над низкоуровневым MCP SDK. Аналог FastAPI для HTTP: декораторы, автоматические схемы, минимум boilerplate.
Минимальный сервер:
Функция ниже выполняет ключевой шаг и возвращает результат для дальнейшей обработки.
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("my-server")
@mcp.tool()
def greet(name: str) -> str:
"""Поздоровайся с пользователем по имени."""
return f"Привет, {name}!"
if __name__ == "__main__":
mcp.run()
Это полноценный MCP-сервер. Запускаете: python server.py, и клиент видит инструмент greet с параметром name: string.
Что происходит автоматически:
@mcp.tool()регистрирует функцию как инструмент- Аннотация типов
name: str-> JSON Schema{"type": "string"} - Docstring
"""Поздоровайся..."""-> description инструмента - Возвращаемое значение автоматически сериализуется
Типы параметров, которые понимает FastMCP:
- Базовые:
str,int,float,bool - Составные:
list[str],dict[str, Any] - Optional:
str | None = None - Pydantic модели: полная поддержка
Если параметр имеет описание через Field() из Pydantic - оно попадёт в JSON-схему:
from pydantic import Field
from typing import Annotated
@mcp.tool()
def search(query: Annotated[str, Field(description="Поисковый запрос")], limit: int = 10) -> list[str]:
"""Поиск по базе данных."""
...
Инструменты: синхронные и async функции, параметры с типами и docstring -> description, возврат str / dict / list, обработка ошибок через raise ValueError
Инструменты - основной примитив MCP. Это функции с side-эффектами, которые LLM может вызывать.
Синхронный инструмент:
Функция ниже выполняет ключевой шаг и возвращает результат для дальнейшей обработки.
@mcp.tool()
def get_current_time(timezone: str = "Europe/Moscow") -> str:
"""Получить текущее время в указанном часовом поясе."""
from datetime import datetime
import zoneinfo
tz = zoneinfo.ZoneInfo(timezone)
now = datetime.now(tz)
return now.strftime("%Y-%m-%d %H:%M:%S %Z")
Async инструмент (для I/O операций):
Асинхронный фрагмент ниже показывает, как запустить операцию параллельно и собрать результат.
import httpx
@mcp.tool()
async def fetch_url(url: str, timeout: int = 10) -> str:
"""
Загрузить содержимое URL.
Args:
url: Адрес страницы для загрузки
timeout: Таймаут в секундах
"""
async with httpx.AsyncClient() as client:
response = await client.get(url, timeout=timeout)
response.raise_for_status()
return response.text[:5000] # Первые 5000 символов
Обработка ошибок. Выбрасывайте ValueError для ожидаемых ошибок - клиент получит понятное сообщение об ошибке:
@mcp.tool()
def divide(a: float, b: float) -> float:
"""Разделить a на b."""
if b == 0:
raise ValueError("Деление на ноль невозможно")
return a / b
Возврат структурированных данных:
Класс ниже инкапсулирует логику и сохраняет состояние между вызовами.
from typing import TypedDict
class UserInfo(TypedDict):
name: str
email: str
created_at: str
@mcp.tool()
def get_user(user_id: int) -> UserInfo:
"""Получить информацию о пользователе."""
# ... запрос к БД
return {"name": "Иван", "email": "ivan@example.com", "created_at": "2024-01-01"}
GPT-4 и Claude отлично работают со структурированными dict-ответами - они понимают поля и могут форматировать ответ пользователю.
Resources: @mcp.resource('resource://path/{param}') - аналог GET-точки API для загрузки данных в контекст; @mcp.resource с return text/json/bytes
Resources - данные только для чтения. LLM может запросить ресурс, чтобы получить контекст для ответа. Идентифицируются через URI.
Статический ресурс:
Функция ниже выполняет ключевой шаг и возвращает результат для дальнейшей обработки.
@mcp.resource("config://app/settings")
def get_settings() -> str:
"""Настройки приложения."""
return """
version: 2.1.0
max_users: 100
maintenance_mode: false
"""
Параметризованный ресурс (через URI template):
Функция ниже выполняет ключевой шаг и возвращает результат для дальнейшей обработки.
@mcp.resource("users://{user_id}/profile")
def get_user_profile(user_id: str) -> dict:
"""Профиль пользователя по ID."""
# {user_id} автоматически извлекается из URI
return {
"id": user_id,
"name": "Иван Иванов",
"role": "admin"
}
Клиент запрашивает: users://42/profile -> FastMCP вызывает функцию с user_id="42".
Бинарный ресурс:
Функция ниже выполняет ключевой шаг и возвращает результат для дальнейшей обработки.
@mcp.resource("files://{filename}")
def get_file(filename: str) -> bytes:
"""Прочитать файл из рабочей директории."""
with open(f"./data/{filename}", "rb") as f:
return f.read()
Разница Resources vs Инструменты: Resources не имеют side-эффектов и аналогичны GET-запросам. Инструменты могут менять состояние (отправить письмо, создать файл). LLM сам решает, что использовать, основываясь на описании.
Prompts: @mcp.prompt() - параметризованные шаблоны, которые Claude может вызывать для типовых задач
Prompts - готовые шаблоны для повторяющихся задач. Пользователь выбирает шаблон в интерфейсе (например, в Claude Desktop), заполняет параметры - и Claude получает готово сформированный запрос.
from mcp.types import PromptMessage, TextContent
@mcp.prompt()
def code_review(language: str, code: str, strictness: str = "medium") -> list[PromptMessage]:
"""
Шаблон для ревью кода.
Args:
language: Язык программирования
code: Код для ревью
strictness: Строгость ревью (low/medium/high)
"""
levels = {
"low": "укажи только критические проблемы",
"medium": "укажи проблемы безопасности и логики",
"high": "детальное ревью: стиль, производительность, безопасность"
}
return [
PromptMessage(
role="user",
content=TextContent(
type="text",
text=f"Проведи ревью этого {language}-кода ({levels[strictness]}):\n\n```{language}\n{code}\n```"
)
)
]
Промпты появляются в палитре команд Claude Desktop - пользователь выбирает «code_review», вводит параметры через форму.
В реальности Prompts используются редко - большинство MCP-серверов ограничиваются инструментами и иногда ресурсами.
Транспорты: stdio (claude_desktop_config.json - mcpServers.my-server.command + args) для локальных серверов; Streamable HTTP (заменил SSE с ноябрь-2025 спек) для удалённого деплоя
stdio транспорт - локальный сервер:
Claude Desktop запускает ваш сервер как дочерний процесс и общается через stdin/stdout.
Конфигурация в claude_desktop_config.json (macOS: ~/Library/Application Support/Claude/claude_desktop_config.json, Windows: %APPDATA%\Claude\claude_desktop_config.json):
{
"mcpServers": {
"my-server": {
"command": "uv",
"args": ["--directory", "/path/to/my-mcp-server", "run", "server.py"]
}
}
}
Или через uvx, если опубликовали пакет:
Конфигурация ниже описывает структуру данных, которая передаётся в инструмент.
{
"mcpServers": {
"my-server": {
"command": "uvx",
"args": ["my-mcp-server"]
}
}
}
Env переменные для секретов:
Конфигурация ниже описывает структуру данных, которая передаётся в инструмент.
{
"mcpServers": {
"my-server": {
"command": "uvx",
"args": ["my-mcp-server"],
"env": {
"DATABASE_URL": "postgresql://...",
"API_KEY": "sk-..."
}
}
}
}
Streamable HTTP - удалённый деплой:
C ноября 2025 (обновление спецификации) вместо SSE используется Streamable HTTP. FastMCP поддерживает оба транспорта:
if __name__ == "__main__":
import sys
if "--http" in sys.argv:
# Удалённый HTTP-сервер
mcp.run(transport="streamable-http", host="0.0.0.0", port=8000)
else:
# Локальный stdio по умолчанию
mcp.run()
Деплой на сервер:
Команды ниже запускаются в терминале и выполняют установку или конфигурацию.
pip install 'mcp[cli]'
python server.py --http
Подключение клиента к HTTP-серверу:
Конфигурация ниже описывает структуру данных, которая передаётся в инструмент.
{
"mcpServers": {
"remote-server": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/mcp-remote", "http://your-server:8000/mcp"]
}
}
}
Дебаггинг: mcp dev server.py - встроенный MCP Inspector; логирование через mcp.get_context().info('...')
Отладка MCP-серверов имеет свою специфику: сервер запускается Claude Desktop как дочерний процесс, stdout занят протоколом. Нельзя просто print() для отладки.
MCP Inspector - основной инструмент:
Команды ниже запускаются в терминале и выполняют установку или конфигурацию.
mcp dev server.py
Эта команда запускает сервер и открывает web-интерфейс Inspector на http://localhost:5173. В Inspector вы видите:
- Список всех зарегистрированных инструментов
- JSON-схему каждого инструмента
- Форму для ручного вызова инструмента
- Лог запросов и ответов
Inspector - самый быстрый способ проверить, что инструмент зарегистрирован правильно и возвращает ожидаемые данные.
Логирование внутри инструментов:
Асинхронный фрагмент ниже показывает, как запустить операцию параллельно и собрать результат.
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("my-server")
@mcp.tool()
async def process_data(data: str) -> str:
"""Обработать данные."""
ctx = mcp.get_context()
await ctx.info(f"Начинаем обработку, длина данных: {len(data)}")
# ... обработка
result = data.upper()
await ctx.info(f"Обработка завершена")
return result
Логи из ctx.info(), ctx.warning(), ctx.error() передаются клиенту через MCP-протокол и отображаются в Claude Desktop в разделе инструментов.
Fallback для разработки: если нужен быстрый print-отладчик - пишите в stderr:
import sys
print("Debug message", file=sys.stderr)
Stderr не перехватывается MCP-протоколом и будет виден в терминале, где запущен сервер.
Типичные проблемы при запуске:
- Claude Desktop не видит инструменты: проверьте
claude_desktop_config.json- правильный ли путь к серверу, нет ли опечаток в JSON. - Сервер запускается, но падает: проверьте зависимости через
uv run python server.pyвручную. - Инструмент не выполняется: проверьте через MCP Inspector - возможно ошибка в проверке параметров.
Частые вопросы
Чем MCP отличается от обычного function calling / tool use?
Function Calling встроен в API-запрос: определения функций передаются в каждом запросе, клиентский код отвечает за исполнение и возврат результата. MCP выносит инструменты в отдельный протокол: сервер существует независимо от LLM. Может обновлять список инструментов, один сервер подключается к любому MCP-совместимому клиенту без изменений кода.
Можно ли использовать один MCP-сервер из разных LLM одновременно?
Да. MCP - протокол-агностичный. Claude Desktop, n8n, Cursor - все подключаются к одному серверу независимо. Каждое подключение - отдельная сессия. Сервер обрабатывает несколько клиентов одновременно. Для Streamable HTTP это естественно. Для stdio каждый клиент запускает отдельный экземпляр процесса.
Как защитить MCP-сервер с Streamable HTTP аутентификацией?
MCP спецификация поддерживает OAuth 2.0 для HTTP-серверов. Простой вариант - API ключ через заголовок: проверяйте Authorization: Bearer <token> в middleware. FastMCP позволяет добавить middleware через стандартный Starlette механизм. Для продакшна рассмотрите OAuth через mcp-proxy или разместите сервер за API Gateway.
Как передавать binary-данные (изображения) через MCP resource?
FastMCP автоматически кодирует bytes в base64 при возврате из resource. На стороне клиента Claude получает BlobResourceContents с типом MIME и base64-данными. Claude 3 и Claude 4 понимают изображения, переданные через Resources, и могут их анализировать. Максимальный размер файла ограничен контекстным окном модели.
Поддерживает ли Claude Code MCP-серверы или только Claude Desktop?
Claude Code поддерживает MCP-серверы начиная с версии 1.0. Конфигурация аналогична Claude Desktop - через claude_mcp_config.json или CLI-команду claude mcp add. Claude Code - основная платформа для разработчиков и там MCP-поддержка активно развивается. Серверы работают одинаково на обеих платформах.
Что внедрить прямо сейчас
- Установите uv и создайте проект по шагам выше.
- Напишите инструмент, который достаёт данные из вашей системы - например, прайс из Excel или список клиентов из CRM.
- Подключите сервер к Claude Desktop через
claude_desktop_config.json. - Проверьте через MCP Inspector, что инструмент работает.
- Если нужно - выкатите на удалённый сервер через Streamable HTTP.
Весь процесс занимает 2-4 часа у вашего менеджера, который хоть раз писал на Python. Никаких курсов на полгода.
AI Компас (t.me/kosmoslab_ai) - канал для предпринимателей в РФ и СНГ, которые применяют AI в своём бизнесе без программиста. Разбираем инструменты и схемы - без курсов и теории.