Учебник

MCP-сервер: базы и API к Claude без программиста

У вас есть прайс, база клиентов или внутренний API, но Claude их не видит? MCP-сервер решает это за 20 строк Python. Разбираем, как написать сервер на FastMCP, подключить к Claude Desktop и выкатить на удалёнку. Без курсов, без архитектур - только конкретный шаг для малого бизнеса.

Макс Космов··9 мин чтения

Ваши менеджеры тратят часы на поиск данных по разным системам - прайс в 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-поддержка активно развивается. Серверы работают одинаково на обеих платформах.

Что внедрить прямо сейчас

  1. Установите uv и создайте проект по шагам выше.
  2. Напишите инструмент, который достаёт данные из вашей системы - например, прайс из Excel или список клиентов из CRM.
  3. Подключите сервер к Claude Desktop через claude_desktop_config.json.
  4. Проверьте через MCP Inspector, что инструмент работает.
  5. Если нужно - выкатите на удалённый сервер через Streamable HTTP.

Весь процесс занимает 2-4 часа у вашего менеджера, который хоть раз писал на Python. Никаких курсов на полгода.

AI Компас (t.me/kosmoslab_ai) - канал для предпринимателей в РФ и СНГ, которые применяют AI в своём бизнесе без программиста. Разбираем инструменты и схемы - без курсов и теории.