До MCP у каждого ИИ-инструмента была собственная система плагинов. У Claude Code — своя. У Cursor — другая. Ваш инструмент для работы с базой данных работал в Claude Code, но не в Cursor, поэтому вы писали его дважды. Смена инструмента означала переписывание всех интеграций. Таково было состояние ИИ-инструментов в 2024 году — каждый инструмент — остров, каждая интеграция — собственная.
MCP — Model Context Protocol — изменил это в 2025 году, а в 2026-м он повсюду. Его использует Claude Code. Его поддерживает Cursor. LangChain, LiteLLM и все крупные ИИ-платформы добавили поддержку MCP. Официальная спецификация MCP определяет протокол — это открытый стандарт (на основе JSON-RPC), который стандартизирует то, как ИИ-модели обнаруживают и вызывают инструменты. Напишите один MCP-сервер. Используйте его с любым совместимым с MCP клиентом.
Что такое MCP — и почему это важно
Протокол, а не маркетинг. MCP — протокол на основе JSON-RPC 2.0. MCP-сервер предоставляет инструменты, ресурсы и промпты. MCP-клиент (встроенный в ИИ-приложение) подключается к серверу, обнаруживает доступное и вызывает инструменты от имени LLM. Транспортный уровень подключаемый — stdio для локальных инструментов, HTTP с SSE для удалённых сервисов.
MCP против function calling — дополняют друг друга, не конкурируют. Function calling: LLM вызывает инструмент в рамках одного API-запроса. Определение инструмента отправляется в API-вызове. MCP: LLM обнаруживает доступные инструменты через стандартизированный протокол, а затем вызывает их — потенциально в нескольких сессиях и с несколькими инструментами. MCP стандартизирует обнаружение и описание инструментов. Function calling их исполняет. Вы можете использовать MCP для управления каталогом инструментов и function calling для их вызова — они работают вместе. За подробным сравнением реализаций function calling в OpenAI, Anthropic, Google и DeepSeek обратитесь к нашему руководству по function calling и использованию инструментов.
Почему MCP важен для разработчиков API. До MCP: вы написали инструмент для запросов к базе данных. Чтобы использовать его в Claude Code, вы написали плагин под Claude Code. Чтобы использовать в Cursor, вы написали интеграцию под Cursor. Чтобы использовать в своём приложении, вы написали собственный код. После MCP: напишите один MCP-сервер. Его сможет использовать каждый совместимый с MCP клиент. Это аналогия «USB-C для ИИ-инструментов» — она не идеальна, но это стандарт, который победил.
Архитектура MCP: серверы, клиенты и транспорты
MCP-сервер. Предоставляет инструменты, ресурсы и промпты. Пишется на Python, Node.js или Go — на чём угодно. Работает локально (транспорт stdio) или удалённо (транспорт HTTP+SSE). Сервер — это программа. Она запускается. Она слушает соединения. Она отвечает на запросы вызова инструментов. Она останавливается, когда клиент отключается.
MCP-клиент. Подключается к MCP-серверам, обнаруживает доступные инструменты, отправляет запросы вызова инструментов от LLM и возвращает результаты. Встроен в ИИ-приложения — Claude Code, Cursor, ваше приложение. Клиент — это мост между решением LLM («мне нужно запросить базу данных») и исполнением инструмента (SELECT * FROM users).
Транспорты. stdio: сервер запускается как подпроцесс клиента. Для связи используются стандартный ввод/вывод. Лучший выбор для локальных инструментов разработки — ноль сетевой настройки, ноль задержки. Транспорт stdio добавляет менее 1 мс издержек на круг сообщения на современной машине. HTTP+SSE: сервер работает как удалённый сервис. HTTP POST для запросов, Server-Sent Events для потоковых ответов. Лучший выбор для продакшн-сервисов — могут подключаться несколько клиентов, сервер можно обновлять независимо, не перезапуская IDE каждого разработчика.
Схема архитектуры:
LLM → MCP Client → Transport (stdio/HTTP) → MCP Server → External Resources (DB, API, Filesystem)
Создание вашего первого MCP-сервера
MCP-сервер погоды + цены акций. 15 минут. Два инструмента.
# mcp_server.py
import json
import sys
from typing import Any
class MCPServer:
def __init__(self):
self.tools = {
"get_weather": {
"description": "Get current weather for a city. Returns temperature in Celsius.",
"inputSchema": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "City name, e.g. 'Tokyo'"}
},
"required": ["city"]
}
},
"get_stock_price": {
"description": "Get current stock price for a ticker symbol.",
"inputSchema": {
"type": "object",
"properties": {
"symbol": {"type": "string", "description": "Stock ticker, e.g. AAPL"}
},
"required": ["symbol"]
}
}
}
def handle_request(self, request: dict) -> dict:
method = request.get("method")
if method == "tools/list":
return {"tools": list(self.tools.values())}
elif method == "tools/call":
tool_name = request["params"]["name"]
tool_args = request["params"]["arguments"]
result = self._execute_tool(tool_name, tool_args)
return {"content": [{"type": "text", "text": str(result)}]}
else:
return {"error": f"Unknown method: {method}"}
def _execute_tool(self, name: str, args: dict) -> Any:
if name == "get_weather":
return f"Weather in {args['city']}: 22°C, partly cloudy"
elif name == "get_stock_price":
return f"{args['symbol']}: $185.50"
else:
return f"Unknown tool: {name}"
# stdio transport —reads JSON-RPC from stdin, writes to stdout
if __name__ == "__main__":
server = MCPServer()
for line in sys.stdin:
request = json.loads(line)
response = server.handle_request(request)
sys.stdout.write(json.dumps(response) + "\n")
sys.stdout.flush()
Подключение к Claude Desktop. Краткое руководство по MCP от Anthropic проводит через всю настройку. Добавьте это в ваш claude_desktop_config.json:
{
"mcpServers": {
"my-tools": {
"command": "python",
"args": ["mcp_server.py"]
}
}
}
Перезапустите Claude Desktop. Ваши два инструмента — get_weather и get_stock_price — теперь доступны. Claude обнаруживает их автоматически. Попробуйте: «Какая погода в Токио и какова цена акций Apple?» Claude вызовет оба инструмента параллельно и синтезирует ответ.
За полным руководством по Claude API — аутентификация, стриминг, использование инструментов и обработка ошибок — обратитесь к нашему руководству разработчика по Claude API.
Создание настоящего MCP-сервера: инструмент запросов к базе данных
Сервер погоды доказывает, что MCP работает на 50 строках Python. Продакшен-инструментам нужно больше: параметризованные запросы, пул соединений и сообщения об ошибках, из которых LLM может самокорректироваться. Вот полный MCP-сервер, который запрашивает базу данных SQLite, построенный на официальном Python SDK MCP (pip install mcp). Три инструмента — query_users, get_user_by_id, create_user. Копируйте, адаптируйте, разворачивайте.
# db_mcp_server.py —production-grade MCP database server
import sqlite3
import asyncio
from mcp.server import Server, NotificationOptions
from mcp.server.models import InitializationCapabilities
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent
server = Server("database-tools")
# Database connection —created once, reused across all tool calls
_connection: sqlite3.Connection | None = None
def get_db():
global _connection
if _connection is None:
_connection = sqlite3.connect("app.db")
_connection.row_factory = sqlite3.Row
# WAL mode: concurrent reads without locking. Without this,
# two simultaneous query_users calls serialize —the second
# blocks until the first releases the read lock.
_connection.execute("PRAGMA journal_mode=WAL")
return _connection
@server.list_tools()
async def list_tools() -> list[Tool]:
return [
Tool(
name="query_users",
description="Run a SELECT query against the users table. "
"Use for searching, filtering, or counting users. "
"Returns results as JSON array. "
"Column names: id, name, email, role, status, created_at.",
inputSchema={
"type": "object",
"properties": {
"where_clause": {
"type": "string",
"description": "SQL WHERE clause without 'WHERE' keyword. "
"Example: \"age > 25 AND status = 'active'\". "
"Leave empty for all users."
},
"limit": {
"type": "integer",
"description": "Max rows to return. Default 50.",
"default": 50
}
}
}
),
Tool(
name="get_user_by_id",
description="Fetch a single user by primary key. Returns user object or null.",
inputSchema={
"type": "object",
"properties": {
"user_id": {
"type": "integer",
"description": "The user's ID in the database."
}
},
"required": ["user_id"]
}
),
Tool(
name="create_user",
description="Insert a new user into the database. "
"Returns the created user with their assigned ID.",
inputSchema={
"type": "object",
"properties": {
"name": {"type": "string", "description": "User's full name."},
"email": {"type": "string", "description": "Valid email address."},
"role": {
"type": "string",
"enum": ["admin", "editor", "viewer"],
"description": "User role. Default: viewer."
}
},
"required": ["name", "email"]
}
)
]
@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
db = get_db()
if name == "query_users":
where = arguments.get("where_clause", "")
limit = arguments.get("limit", 50)
query = "SELECT * FROM users"
params = []
if where.strip():
query += " WHERE " + where
query += " LIMIT ?"
params.append(limit)
try:
rows = db.execute(query, params).fetchall()
except sqlite3.OperationalError as e:
# Return column names so the LLM can self-correct bad WHERE clauses
return [TextContent(
type="text",
text=f"Query failed: {e}. Valid columns: id, name, email, role, status, created_at."
)]
return [TextContent(
type="text",
text=str([dict(r) for r in rows])
)]
elif name == "get_user_by_id":
user_id = arguments["user_id"]
row = db.execute(
"SELECT * FROM users WHERE id = ?", [user_id]
).fetchone()
if row is None:
return [TextContent(type="text", text=f"No user found with id={user_id}")]
return [TextContent(type="text", text=str(dict(row)))]
elif name == "create_user":
name_val = arguments["name"]
email = arguments["email"]
role = arguments.get("role", "viewer")
try:
cursor = db.execute(
"INSERT INTO users (name, email, role) VALUES (?, ?, ?)",
[name_val, email, role]
)
db.commit()
new_id = cursor.lastrowid
except sqlite3.IntegrityError as e:
return [TextContent(
type="text",
text=f"Insert failed: {e}. Email may already exist."
)]
new_user = db.execute(
"SELECT * FROM users WHERE id = ?", [new_id]
).fetchone()
return [TextContent(type="text", text=str(dict(new_user)))]
return [TextContent(type="text", text=f"Unknown tool: {name}")]
async def main():
async with stdio_server() as (read_stream, write_stream):
await server.run(
read_stream,
write_stream,
InitializationCapabilities(
sampling={},
experimental={},
roots={}
),
notification_options=NotificationOptions()
)
if __name__ == "__main__":
asyncio.run(main())
Четыре решения в этом коде предотвращают ночные звонки в 3 часа утра.
Описания инструментов с примерами. LLM читает ваше описание инструмента, чтобы решить, какой инструмент вызвать. Голое "description": "Query the database" не даёт модели возможности различить инструменты — для запроса SELECT COUNT(*) она часто попытается вызвать get_user_by_id. Без явных инструкций модели часто выбирают не тот инструмент, когда у нескольких инструментов похожие описания. Описание выше включает, что делает инструмент, что возвращает и точные имена колонок. Такая конкретность устраняет неоднозначность.
Параметризованные запросы — никаких f-строк. db.execute("SELECT * FROM users WHERE id = ?", [user_id]) никогда не превращается в db.execute(f"SELECT * FROM users WHERE id = {user_id}"). Одна f-строка в MCP-сервере базы данных, и LLM, генерирующая user_id = "1 OR 1=1", утечёт все строки вашей таблицы users. LLM сгенерирует такой ввод. Это случалось в продакшне. Параметризованные запросы — жёсткое требование, а не приятный бонус.
Сообщения об ошибках с подсказками для восстановления. Когда query_users падает, потому что LLM угадала несуществующее имя колонки, ошибка включает список валидных колонок: "Valid columns: id, name, email, role, status, created_at." LLM читает это, исправляет запрос и повторяет вызов инструмента — ноль вмешательства человека. Голое "OperationalError: no such column" вызывает у Claude пожимание плечами и заблокированного пользователя.
Переиспользование соединения с режимом журнала WAL. PRAGMA journal_mode=WAL включает параллельные чтения без блокировок. Без него два одновременных вызова query_users сериализуются — второй ждёт 50 мс, пока первый освободит блокировку чтения. В цепочке инструментов, где Claude вызывает query_users, читает результаты, а затем вызывает get_user_by_id для каждой строки, ожидание блокировки накапливается через 5 последовательных вызовов до 250 мс задержки, видимой пользователю. Режим WAL устраняет узкое место.
MCP у провайдеров LLM
Поддержка MCP сильно различается по провайдерам. Anthropic создал протокол и имеет самую глубокую интеграцию. Остальные догоняют с разной скоростью. Вот где находится каждый крупный провайдер в середине 2026 года.
| Поставщик | Уровень поддержки MCP | Поддержка транспортов | Готовность к продакшну | Качество SDK |
|---|---|---|---|---|
| Anthropic | Нативный — автор протокола | stdio + HTTP/SSE | Продакшен-готовность для обоих транспортов | Официальные SDK для Python и TypeScript с полным покрытием спецификации |
| OpenAI | Через Agents SDK и адаптеры сообщества | stdio (Agents SDK), HTTP через адаптеры | Agents SDK: продакшн. Адаптеры: бета-качество | Поддерживаются сообществом; официального MCP SDK нет |
| Только адаптеры сообщества | stdio, ограниченный HTTP/SSE | Только для разработки | Ранние пакеты сообщества; скудная документация | |
| DeepSeek | Путь, совместимый с OpenAI | Через тулчейн адаптеров OpenAI | Работает, но не поддерживается | Нет выделенного MCP SDK; наследует особенности адаптера OpenAI |
| Aggregation Platforms (инфраструктурный слой — маршрутизирует ко всем провайдерам ниже) | MCP-шлюз — подключается один раз, маршрутизирует везде | HTTP/SSE с единой аутентификацией | Продакшен с управляемой аутентификацией, ограничением скорости и журналированием | Платформенные SDK для Python, JavaScript, LangChain |
Паттерн MCP-шлюза. Агрегационная платформа действует как MCP-клиент, подключается к вашим MCP-серверам и предоставляет инструменты любому LLM через единую точку API. Вы пишете один MCP-сервер. Вы используете его с GPT-5.5, Claude Opus 4, Gemini 3 и DeepSeek V3 — все через POST https://api.tokspan.com/v1/chat/completions с одним и тем же API-ключом. Платформа обрабатывает перевод протокола, поэтому ваши MCP-инструменты работают независимо от активной модели. Одно определение сервера питает 6+ моделей. За 5-минутной настройкой обратитесь к краткому руководству TokSpan.
MCP против function calling: когда что использовать
Решение сводится к одному вопросу: должны ли ваши инструменты работать с несколькими LLM-клиентами?
Используйте только function calling, когда вы вызываете один LLM API напрямую — OpenAI, Anthropic или Google — и ваши инструменты специфичны для одного приложения. Вы передаёте определения инструментов прямо в массив tools запроса chat completions. Инфраструктура нулевая: нет отдельного серверного процесса, нет транспортного слоя, нет согласования протокола. Бот поддержки клиентов, который проверяет статус заказа через один внутренний REST-endpoint GET /orders/:id, нуждается в function calling, а не в MCP. Не переусложняйте — MCP добавляет серверный процесс, транспорт и рукопожатие протокола без единой выгоды, когда у вас один клиент.
Используйте MCP + function calling вместе, когда у вас несколько ИИ-клиентов — Claude Code на вашем ноутбуке, Cursor у коллеги и собственный дашборд на стейджинг-сервере — которым нужны одни и те же инструменты. Ваш инструмент запросов к базе данных, инструмент деплоя и просмотрщик логов определяются один раз в MCP-сервере. Каждый клиент обнаруживает их через tools/list и вызывает через function calling в рантайме.
Ваш каталог инструментов централизован. Обновления описаний и схем инструментов мгновенно распространяются на каждый клиент без переразвёртывания. Внутренняя платформенная команда в инженерной организации на 200 человек, использующая этот паттерн, сократила сопровождение интеграции инструментов с 8 часов на новый инструмент до 30 минут.
Используйте MCP как шлюз, когда вы маршрутизируете вызовы инструментов к нескольким LLM-провайдерам через агрегационную платформу. Одно определение MCP-сервера питает инструментами GPT-5.5, Claude Opus 4 и Gemini 3 одновременно. Платформа переводит между протоколом обнаружения инструментов MCP и API function calling каждого провайдера. Вы никогда не пишете определения инструментов под конкретного провайдера. Вы никогда не отлаживаете, почему инструмент работает с Claude, но молча падает с GPT-5.5. Одно определение сервера питает 6+ моделей через единую точку — конфигурация инструментов под каждого провайдера не требуется.
Неправильный выбор — по опыту. Рассмотрим типичный сценарий: разработчик в небольшом стартапе тратит две недели на настройку MCP-серверов с транспортом HTTP/SSE, middleware аутентификации и пулом соединений для трёх внутренних инструментов, используемых исключительно с Claude Code. Function calling заняло бы два часа. Две недели инженерного времени обменены на инфраструктуру, обслужившую ровно один клиент. Начинайте с function calling. Мигрируйте на MCP, когда у вас появится второй клиент — не раньше.
MCP в продакшне: аутентификация, ограничение скорости и обработка ошибок
stdio для разработки. HTTP+SSE для продакшна. Транспорт stdio запускает ваш MCP-сервер как подпроцесс — без аутентификации, без сетевой безопасности, один клиент на серверный процесс. Годится для claude_desktop_config.json на вашем ноутбуке. Не годится для сервиса, обслуживающего 50 разработчиков в вашей организации. Транспорт HTTP+SSE позволяет развернуть сервер как отдельный сервис за балансировщиком нагрузки, с правильной аутентификацией, мониторингом и независимыми циклами развёртывания.
Middleware аутентификации. Спецификация HTTP-транспорта MCP на середину 2026 года ещё не определяет стандартный механизм аутентификации. Вы добавляете свой. Общий паттерн: проверяйте API-ключ в заголовке Authorization: Bearer <key> до обработки любого MCP-сообщения. Для развёртывания на базе TokSpan платформа обрабатывает аутентификацию на уровне API-шлюза — ваш MCP-сервер получает предварительно аутентифицированные запросы на https://api.tokspan.com/v1/. Если вы самохостите, прикрутите middleware аутентификации перед обработчиком MCP.
Просроченный токен на глубине 3 цепочки инструментов — где tool_a вызывает tool_b, который вызывает tool_c — порождает непонятную JSON-RPC ошибку, на отслеживание до слоя аутентификации уходит 45 минут. Сначала сделайте аутентификацию правильно. За полным чеклистом безопасности обратитесь к нашему чеклисту безопасности продакшна.
# Auth middleware for MCP HTTP server (FastAPI pattern)
from fastapi import FastAPI, Request, HTTPException
app = FastAPI()
VALID_TOKENS = {"mcp-secret-abc123", "mcp-prod-xyz789"} # Replace with DB lookup
@app.middleware("http")
async def auth_middleware(request: Request, call_next):
if request.url.path.startswith("/mcp/"):
token = request.headers.get("Authorization", "").removeprefix("Bearer ")
if token not in VALID_TOKENS:
raise HTTPException(status_code=401, detail="Invalid MCP API key")
return await call_next(request)
Ограничение скорости предотвращает инциденты, усиленные LLM. Один расплывчато описанный инструмент — run_sql без принудительного LIMIT — и LLM генерирует запрос, сканирующий 10 миллионов строк в 2 часа ночи, потому что пользователь спросил «покажи мне всё». Токен-бакитный ограничитель на стороне сервера ограничивает query_users 60 вызовами в минуту и create_user 10 вызовами в минуту. Используйте Redis для распределённого ограничения скорости между несколькими экземплярами сервера — INCR + EXPIRE на имя инструмента на API-ключ.
Без ограничения скорости один восторженный промпт LLM становится первопричиной продакшн-отказа базы данных. Вас разбудят в 3 часа ночи. Вы проследите это до определения инструмента, написанного за 15 минут и забытого шесть спринтов назад.
Обработка ошибок, помогающая LLM восстановиться. Не возвращайте "Error: something went wrong." LLM читает ваше сообщение об ошибке и пробует снова. Возвращайте три вещи: тип ошибки, конкретный параметр, который не прошёл, и валидные альтернативы. Эта ошибка — мёртвый груз: "Invalid input." А эта позволяет Claude самокорректироваться за одну попытку: "create_user failed: role must be one of [admin, editor, viewer]. Received: 'superadmin'." Разница между этими двумя сообщениями — разница между самовосстанавливающейся цепочкой инструментов и застрявшим пользователем, пялящимся на спиннер, пока он не сдастся и не откроет тикет.
Частые ловушки MCP: что ломается на практике
Вот четыре режима отказа, с которыми MCP-разработчики сталкиваются в первый месяц. Каждый можно избежать, если знать о нём до развёртывания.
Расплывчатые описания инструментов. Это причина номер один багов неверного выбора инструмента. "description": "Fetch data" ничего не говорит LLM. Claude Opus 4 угадает, какой инструмент использовать, и часто ошибается, когда у нескольких инструментов пересекающиеся описания. Пишите описания инструментов так, будто объясняете инструмент разработчику, который никогда не видел вашу кодовую базу. Включайте, что инструмент делает, что возвращает, пример валидного ввода и когда его НЕ использовать.
Описание query_users в сервере базы данных выше не многословно — оно ровно настолько точное, чтобы устранить неоднозначность.
Лимиты буфера stdio. Транспорт stdio использует OS-пайпы. Буфер пайпа по умолчанию в Linux: 64 КБ. Ваш инструмент query_large_dataset возвращает 2 МБ JSON. Вызов write() блокируется. Клиент зависает. Вы пялитесь на спиннер 30 секунд, затем kill -9 процесса.
Для любого инструмента, который может вернуть больше 64 КБ, реализуйте чанкинг ответа — разбивайте большие результаты на страницы с параметрами page и page_size. Или переходите на HTTP-транспорт, где лимиты размера ответа настраиваются на уровне фреймворка сервера. Установите жёсткий потолок ответа 512 КБ для каждого инструмента. Ни один инструмент в MCP-сервере не должен возвращать больше данных, чем LLM может полезно обработать в одном контекстном окне.
Коллизии имён инструментов между серверами. Вы подключаете два MCP-сервера: один от команды баз данных (get_status — лаг репликации), другой от команды эксплуатации (get_status — здоровье деплоя). Оба предоставляют инструмент с именем get_status. Claude Code добавляет префикс имени сервера: database-tools_get_status и ops-tools_get_status. Но некоторые MCP-клиенты, включая недавние версии Cursor, молча выбирают сервер, который первым ответил на tools/list. Исправление до смешного простое и повсеместно игнорируется: неймспейсите имя каждого инструмента с первого дня. db_query_users. ops_deploy_service. logs_search_errors. Префикс из двух символов предотвращает целую категорию молчаливых отказов.
Пробелы в валидации схем. LLM отправит "user_id": "42" (строку), когда ваша схема говорит "type": "integer". Она отправит "limit": -5, когда схема говорит "minimum": 1. Она отправит "role": "superadmin", когда схема говорит "enum": ["admin", "editor", "viewer"]. Ваш обработчик call_tool должен валидировать каждый аргумент, приводить типы, где безопасно (int("42") — 42), и возвращать конкретные ошибки валидации для всего остального. Никогда не предполагайте, что LLM уважает вашу JSON Schema. Она этого не делает. Ваш сервер — последняя линия обороны между галлюцинированным аргументом инструмента и вашей продакшн-базой данных.
FAQ
Нужен ли мне MCP, если я уже использую function calling?
Они дополняют друг друга, а не взаимоисключают. Function calling исполняет инструменты внутри API-вызова. MCP стандартизирует обнаружение и описание инструментов между приложениями. Используйте MCP для определения каталога инструментов — единого источника истины о том, какие инструменты существуют и как их вызывать. Используйте function calling в рантайме, чтобы фактически вызывать эти инструменты. MCP — слой интерфейса. Function calling — слой исполнения. Если у вас ровно одно клиентское приложение и три инструмента, пропустите MCP и используйте function calling напрямую. Вы поймёте, когда вам нужен MCP: в тот момент, когда обнаружите, что копируете определения инструментов между конфигами Claude Code и массивом tools вашего приложения.
Могу ли я использовать MCP с моделями OpenAI?
Да — через адаптеры или OpenAI Agents SDK. Нативная поддержка менее зрелая, чем у Anthropic, но функциональна для распространённых паттернов инструментов. Если вы используете агрегационную платформу с поддержкой MCP-шлюза, платформа обрабатывает перевод: ваш MCP-сервер работает с GPT-5.5 и GPT-5.5-mini без какого-либо OpenAI-специфичного MCP-кода с вашей стороны. Компромисс: адаптеры сообщества отстают от официального SDK Anthropic примерно на 3–6 месяцев по новым фичам спецификации MCP.
Готов ли MCP к продакшну в 2026 году?
Для локальных инструментов через транспорт stdio: да, и stdio-клиент Claude Code проверен в бою на миллионах часов разработчиков. Для удалённых сервисов через HTTP: да, с оговоркой, что вы должны добавить собственный middleware аутентификации — стандарт аутентификации HTTP-транспорта всё ещё развивается. Ядро протокола (формат JSON-RPC сообщений, обнаружение инструментов, исполнение инструментов) стабильно и, на момент написания, не имело ломающих изменений с обновления спецификации 2025-03-26. Если вы самохостите HTTP MCP-сервер, заложите 2–3 дня на настройку аутентификации, ограничения скорости и логирования.
Как MCP соотносится с агрегационными API-платформами?
Агрегационные платформы могут выступать как MCP-шлюзы: вы подключаете свои MCP-серверы к платформе один раз. Платформа маршрутизирует вызовы инструментов к любому LLM — GPT-5.5, Claude Opus 4, Gemini 3, DeepSeek V3. Вы получаете мультимодельное использование инструментов без конфигурации MCP под каждого провайдера. Один MCP-сервер. Все модели. За подробностями о том, как агрегационные платформы реализуют маршрутизацию MCP-шлюза на практике, обратитесь к документации интеграции MCP TokSpan.
Могу ли я подключить несколько MCP-серверов к одному клиенту?
Да. Claude Code, Cursor и большинство MCP-клиентов поддерживают одновременное подключение к нескольким серверам — добавьте каждый сервер в ваш конфиг с уникальным именем, и все инструменты всех серверов появятся в списке доступных инструментов LLM. Острый край: коллизии имён инструментов между серверами. Именуйте инструменты с префиксом неймспейса (db_query, ops_deploy) с первого дня. Не полагайтесь на то, что клиент разрешит неоднозначность — не каждый клиент это делает, а те, что делают, обрабатывают это непоследовательно.
Что происходит, когда два MCP-сервера предоставляют инструменты с одинаковым именем?
Зависит от клиента — и в этом проблема. Claude Code добавляет имя сервера, создавая уникальные идентификаторы: database-tools_get_status и ops-tools_get_status. Cursor молча дедуплицирует — побеждает сервер, первым ответивший на tools/list. Вообще не полагайтесь на разрешение неоднозначности на стороне клиента. Добавляйте серверно-специфичный неймспейс к имени каждого инструмента. db_get_status и ops_get_status устраняют неоднозначность в источнике.
Как отлаживать MCP-вызовы инструментов, когда что-то идёт не так?
Проверьте три места по порядку. Первое — логи клиента: Claude Code пишет MCP-сообщения в свою директорию логов. Второе — stderr вашего сервера: транспорт stdio отправляет весь вывод stderr в консоль клиента (stdin и stdout зарезервированы для сообщений протокола MCP, но stderr свободен для логирования). Третье — добавьте структурированное JSON-логирование на ваш сервер: {"event": "tool_call", "tool": "query_users", "args": {...}, "duration_ms": 45, "error": null}. Структурированные логи ловят те 80% MCP-багов, которые всплывают только во время он-колл дежурств, когда вы уже 15 минут в инциденте и понятия не имеете, какой вызов инструмента упал и почему.
Поддерживает ли MCP потоковые ответы от инструментов?
Нет в текущей спецификации. MCP-вызовы инструментов возвращают единый массив content — никаких частичных ответов, никаких обновлений прогресса, никаких Server-Sent Events от исполнения инструментов. Для долго выполняющихся операций вроде миграций базы данных или генерации отчётов немедленно возвращайте ID задания и предоставляйте отдельный инструмент get_job_status. LLM его опрашивает. Этот паттерн ID-задания плюс опрос — де-факто стандарт, используемый каждым крупным продакшн MCP-сервером на середину 2026 года. Прямая поддержка стриминга в дорожной карте спецификации MCP, но без подтверждённой даты релиза.
MCP унифицировал интеграцию ИИ-инструментов в 2025–2026 годах — один сервер, любой клиент, любая модель. Но стандартизация приглашает конкуренцию. Протокол Agent-to-Agent (A2A) от Google набирает обороты среди корпоративных команд, которым нужно меж-агентное взаимодействие за пределами простого вызова инструментов. OpenAI строит собственную экосистему агентного SDK с более глубокой платформенной интеграцией. Вопрос, за которым стоит следить до 2027 года, не в том, выживет ли MCP — сам протокол солиден, а спецификация стабильна — а в том, переживёт ли «один протокол, чтобы править всеми» аппетит индустрии к платформо-специфичным интеграциям, предлагающим более тесную связку и лучшую производительность ценой привязки.
MCP-сервер, который вы построили в первой секции этой статьи, работает с одним клиентом — Claude Desktop. MCP-шлюз подключает тот же сервер к каждому LLM в вашем стеке, поэтому определения инструментов, написанные один раз, питают каждую используемую вами модель. Документация интеграции MCP TokSpan покрывает настройку шлюза, конфигурацию аутентификации и продакшн-чеклист из секции выше — превращая 50-строчный stdio-сервер в мультимодельный слой инструментов, переживающий смену провайдеров без касания к строке кода инструментов.