MCP-сервер поверх существующего API - MCP-сервер, который даёт языковой модели доступ к сервису через уже существующий интерфейс этого сервиса: REST, GraphQL или RPC. Сервер не копирует API один к одному. Он отбирает операции, которые стоит открыть, превращает их в инструменты (tools), которые модель может правильно выбрать и заполнить, и приводит результаты к виду, удобному для контекста модели.
Спецификация MCP не определяет, как проектировать такой сервер. Она задаёт формат инструментов, разделение ошибок, подсказки кеширования и требования безопасности; остальное - рекомендации по проектированию. Статья опирается на версию спецификации 2026-07-28 и руководство «Writing effective tools for agents», которое Anthropic опубликовала в сентябре 2025 года. Базовые понятия протокола описаны в статье Что такое MCP, поля определения инструмента - в статье Инструменты в MCP.
Кратко
| Параметр | Значение |
|---|---|
| Объект | MCP-сервер, который служит обёрткой над API существующего сервиса |
| Основной примитив | Инструменты; ресурсы и промпты по желанию |
| Требования | Спецификация 2026-07-28: инструменты, кеширование, авторизация, Security Best Practices |
| Рекомендации по проектированию | «Writing effective tools for agents», Anthropic, сентябрь 2025 года |
| Ключевые решения | Набор инструментов, описания, параметры, размер ответа, ошибки, авторизация, состояние |
| Версия спецификации | 2026-07-28 |
Почему API не открывают как есть
API рассчитан на программы. Разработчик один раз читает документацию и пишет код, который вызывает эндпоинты в заданном порядке, хранит идентификаторы, проходит все страницы списка и переводит числовые коды в значения.
Модель выбирает инструмент в каждом разговоре заново - по именам, описаниям и схемам входных параметров в своём контексте. Каждое определение инструмента и каждый результат занимают место в этом контексте. Длинный список похожих инструментов усложняет выбор, страница сырого JSON расходует токены, а непрозрачный идентификатор легко спутать с другим.
Руководство Anthropic называет инструменты, которые просто оборачивают существующие эндпоинты, частой ошибкой и советует несколько инструментов под конкретные важные сценарии. MCP-сервер становится слоем адаптации между моделью и неизменным API.
История
В таблице - изменения спецификации, важные для проектирования инструментов.
| Дата | Событие |
|---|---|
| 26 марта 2025 | Спецификация 2025-03-26: аннотации инструментов, которые описывают их поведение |
| 18 июня 2025 | Спецификация 2025-06-18: структурированный результат инструмента (outputSchema, structuredContent), ссылки на ресурсы в результатах, поле title |
| 11 сентября 2025 | Anthropic публикует руководство «Writing effective tools for agents» |
| 25 ноября 2025 | Спецификация 2025-11-25: ошибки проверки входных данных возвращаются как ошибки выполнения инструмента (SEP-1303) |
| 28 июля 2026 | Спецификация 2026-07-28: протокол без состояния, явные идентификаторы состояния, подсказки кеширования ttlMs и cacheScope, детерминированный порядок инструментов |
Выбор операций
Сценарии вместо эндпоинтов
Инструмент закрывает задачу, о которой просит пользователь, а не отдельный HTTP-вызов. В примерах руководства Anthropic один инструмент schedule_event находит свободное время и создаёт событие вместо отдельных инструментов для списка пользователей, списка событий и создания события, а search_logs возвращает только нужные строки вместо read_logs.
Инструмент, который объединяет несколько вызовов API, переносит логику с модели на сервер: сервер выполняет вызовы в правильном порядке и сводит данные. Руководство также предупреждает, что слишком много инструментов или инструменты с пересекающимся назначением отвлекают модель от эффективных стратегий. Поиск с фильтрами оно предпочитает полному списку, который заставляет модель читать записи, не нужные для задачи.
Чтение и запись
Операции чтения и операции, которые меняют данные, несут разные риски. Сервер может широко открывать чтение и только те операции записи, которые нужны целевым сценариям. Редкая, необратимая или административная операция может остаться вне сервера: пользователь выполняет её в собственном интерфейсе сервиса.
Последнее слово остаётся за хостом: спецификация рекомендует, чтобы человек всегда мог отклонить вызов инструмента.
Аннотации для разрушающих операций
Определение инструмента может содержать аннотации - необязательные свойства, которые описывают его поведение. Схема версии 2026-07-28 определяет пять аннотаций:
| Аннотация | Значение | По умолчанию |
|---|---|---|
title |
Название инструмента для человека | нет |
readOnlyHint |
Инструмент не меняет своё окружение | false |
destructiveHint |
Инструмент может выполнять разрушающие изменения; false означает только добавление |
true |
idempotentHint |
Повторный вызов с теми же аргументами не даёт дополнительного эффекта | false |
openWorldHint |
Инструмент работает с открытым миром внешних сущностей, например с вебом | true |
destructiveHint и idempotentHint имеют смысл, только когда readOnlyHint равно false. Значения по умолчанию описывают самый осторожный случай, поэтому сервер ставит readOnlyHint: true инструментам, которые только читают, и destructiveHint: false инструментам, которые только добавляют данные.
Аннотации - подсказки. Клиенты обязаны считать их недоверенными, если сервер не доверенный, а аннотация не заменяет контроль доступа на сервере.
Описания и имена
Имена инструментов
Два подключённых сервера могут предоставлять инструменты с одинаковым именем, например search, и спецификация допускает, что клиенты добавляют к имени префикс с идентификатором сервера. Руководство Anthropic советует использовать пространства имён (namespacing) и на стороне сервера: по сервису (asana_search, jira_search) и по ресурсу (asana_projects_search, asana_users_search). По данным руководства, выбор между префиксами и суффиксами заметно влиял на результаты его оценок и различается от модели к модели.
Описания инструментов
Описание - главный источник, из которого модель узнаёт, когда применять инструмент; схема называет его подсказкой для модели. Руководство Anthropic советует описывать инструмент так, как его объясняли бы новому сотруднику команды. То, что опытный пользователь API знает неявно, записывается явно: форматы запросов, термины предметной области, связи между сущностями, ограничения. По данным руководства, даже небольшие уточнения описаний могут заметно улучшить то, как модель пользуется инструментами.
Сведения о сервере в целом, а не об одном инструменте, можно поместить в поле instructions результата server/discover. Схема описывает его как указания на естественном языке, которые помогают модели работать с сервером и не должны повторять описания инструментов.
Имена параметров
Имена параметров - часть описания: по данным руководства, параметр user_id понятнее, чем user. У каждого параметра есть своё поле description с ожидаемым форматом и примером. Схема с additionalProperties: false отклоняет неизвестные аргументы.
Параметры
Перечисления и значения по умолчанию
API часто принимает коды и флаги, понятные только с документацией. В инструменте они становятся явным выбором: статус, который API кодирует числом, превращается в enum из понятных значений, а сервер переводит их в коды. Модель выбирает из закрытого списка, а не угадывает.
Параметры, которые не нужны в большинстве вызовов, получают значения по умолчанию. Ключевое слово JSON Schema default указывает значение в схеме, а применяет его сервер, когда аргумент не передан.
Понятные идентификаторы
API обозначают объекты внутренними идентификаторами, часто UUID. По данным руководства Anthropic, замена UUID осмысленными названиями или даже простой нумерацией заметно повышает точность модели в задачах поиска и уменьшает число галлюцинаций.
Поэтому сервер поверх API принимает идентификаторы, которыми пользуются люди: ключ проекта, ключ задачи вида WEB-142, имя пользователя, слово «me» для текущего пользователя. Во внутренние идентификаторы их переводит сам сервер.
Размер ответа
Фильтры и ограничения
Инструмент, который может вернуть много данных, ограничивает их: постраничной выдачей, выбором диапазона, фильтрами или усечением, с разумными значениями по умолчанию. По данным руководства, в Claude Code ответы инструментов по умолчанию ограничены 25 000 токенов.
Протокол определяет постраничную выдачу по курсору только для списочных операций, таких как tools/list, а не для содержимого результата tools/call. Поэтому у инструмента поиска свои параметры, например лимит и курсор, который результат возвращает для следующего вызова.
Усечение с подсказкой
Если результат обрезан, инструмент сообщает об этом и объясняет, как получить остальное: сузить фильтр, запросить следующую страницу, найти конкретный элемент. Руководство советует такой текст, чтобы направить модель к небольшим точным поискам вместо одного широкого.
Краткий и подробный форматы
Руководство описывает параметр response_format со значениями "concise" и "detailed". Краткий формат возвращает только то, что нужно модели для ответа, например текст сообщений. Подробный добавляет идентификаторы и поля, нужные для следующих вызовов. В примере из руководства краткий ответ занял около трети токенов подробного.
Поля ответа выбираются для модели, а не копируются из API: руководство предпочитает name и file_type полям uuid и mime_type.
Ошибки, понятные модели
Спецификация отделяет ошибки протокола, например неизвестный инструмент, от ошибок выполнения инструмента: сбоя API, ошибки бизнес-логики, неверных аргументов. Вторые возвращаются в результате инструмента с isError: true, и спецификация описывает их как обратную связь, по которой модель может исправить вызов.
Поэтому сервер поверх API переводит ошибки API. HTTP-статус с внутренним кодом мало что говорит модели; текст, который называет неверное значение и допустимые, позволяет ей исправиться. Руководство советует конкретные сообщения с указанием, что делать, вместо непрозрачных кодов ошибок и трассировок стека.
{
"jsonrpc": "2.0",
"id": 7,
"result": {
"resultType": "complete",
"content": [
{
"type": "text",
"text": "Project \"WEBB\" not found. Available project keys: WEB, API, MOBILE. Call tracker_search_issues again with one of these keys in the project parameter."
}
],
"isError": true
}
}Авторизация и пользователь
Как MCP-сервер по HTTP получает и проверяет токены, описано в статье Авторизация в MCP. Для сервера поверх API важнее всего два правила спецификации.
Первое: сервер принимает только токены, выданные для него, и проверяет каждый входящий токен. Пользователь определяется по проверенному токену, а не по аргументу инструмента. Список инструментов может зависеть от авторизации запроса, например от выданных прав (scopes).
Второе: сервер не должен пересылать в API токен, полученный от клиента (token passthrough). Обращаясь к API, сервер может выступать OAuth-клиентом этого API и использует отдельный токен, выданный собственным сервером авторизации API. Как сервер получает этот токен и как связывает пользователя с учётной записью в API, спецификация не определяет.
Сервер, который подключает клиентов к стороннему API с одним статическим идентификатором клиента, в документе Security Best Practices называется MCP-прокси-сервером (MCP proxy server). Такой сервер подвержен проблеме confused deputy, и документ требует от него хранить согласие пользователя для каждого клиента отдельно и проверять его до начала авторизации у третьей стороны. Документ также советует минимальный начальный набор прав; дополнительные права запрашиваются, когда впервые нужна привилегированная операция. Риски безопасности описаны в статье Безопасность MCP. Локальный сервер на stdio берёт учётные данные, например ключ API, из своего окружения.
Состояние между вызовами
С версии 2026-07-28 в протоколе нет сессии; изменения описаны в статье MCP без состояния. Сервер, которому нужно состояние между вызовами, например черновик или открытая транзакция, возвращает явный идентификатор состояния (handle) из инструмента, который это состояние создаёт, и принимает его как аргумент следующих вызовов. Передавать идентификатор дальше - задача модели.
Спецификация даёт ненормативные рекомендации для идентификаторов состояния:
- Авторизация. На сервере с аутентификацией идентификатор - имя, а не право доступа: сервер при каждом вызове проверяет права вызывающего на этот идентификатор.
- Непрозрачность. Идентификатор не кодирует внутреннюю структуру, которую можно разобрать или угадать.
- Срок жизни. Срок хранения указывается в описании инструмента, который создаёт состояние, например «drafts expire after 24 hours of inactivity», чтобы модель его видела.
- Истечение. Вызов с просроченным или неизвестным идентификатором возвращает ошибку выполнения инструмента с объяснением, и модель может создать новый идентификатор.
Security Best Practices добавляет, что обладание идентификатором не является аутентификацией. Идентификаторы создаются криптостойким генератором случайных чисел и привязываются на сервере к пользователю из проверенного токена, например через хранение состояния под ключом <user_id>:<handle>.
Структурированный результат
Инструмент может объявить outputSchema - JSON Schema своего результата. Структурированный результат возвращается в structuredContent и обязан соответствовать схеме; для совместимости инструменту следует вернуть и сериализованный JSON в текстовом блоке. Сами поля описаны в статье Инструменты в MCP.
Для сервера поверх API схема результата - отдельный контракт, а не копия ответа API: в ней поля, которые инструмент возвращает после отбора и переименования.
Подсказки кеширования
С версии 2026-07-28 результаты server/discover, списочных методов и resources/read несут подсказки кеширования: ttlMs - время в миллисекундах, в течение которого результат считается свежим, и cacheScope. Результатов tools/call в этом списке нет.
cacheScope: "public" означает, что в результате нет данных конкретного пользователя и любой общий кеш может отдавать его любому пользователю. "private" разрешает повторное использование только в том же контексте авторизации, поэтому список инструментов, который зависит от прав токена, помечается "private". cacheScope не заменяет контроль доступа. Инструменты возвращаются в детерминированном порядке; по данным спецификации, это ещё и повышает долю попаданий в кеш промптов модели.
Пример с API трекера задач
В примере - вымышленный API трекера задач по адресу api.tracker.example. Это не настоящий сервис. У API четыре эндпоинта:
| Эндпоинт | Что делает |
|---|---|
GET /v2/projects/{project_id}/issues |
Список задач; фильтры state (код 1, 2 или 3) и assignee_id (UUID); page, per_page; каждая задача со всеми полями |
POST /v2/projects/{project_id}/issues |
Создаёт задачу |
POST /v2/issues/{issue_id}/transitions |
Меняет статус по идентификатору перехода |
DELETE /v2/issues/{issue_id} |
Удаляет задачу |
До переработки
Прямое зеркало даёт по инструменту на эндпоинт. Имена повторяют пути, описания повторяют HTTP-метод, а параметры требуют UUID и числовых кодов, которые модель может только угадать.
[
{
"name": "get_v2_projects_project_id_issues",
"description": "GET /v2/projects/{project_id}/issues",
"inputSchema": {
"type": "object",
"properties": {
"project_id": { "type": "string" },
"state": { "type": "integer" },
"assignee_id": { "type": "string" },
"page": { "type": "integer" },
"per_page": { "type": "integer" }
},
"required": ["project_id"]
}
},
{
"name": "post_v2_issues_issue_id_transitions",
"description": "POST /v2/issues/{issue_id}/transitions",
"inputSchema": {
"type": "object",
"properties": {
"issue_id": { "type": "string" },
"transition_id": { "type": "string" }
},
"required": ["issue_id", "transition_id"]
}
},
{
"name": "delete_v2_issues_issue_id",
"description": "DELETE /v2/issues/{issue_id}",
"inputSchema": {
"type": "object",
"properties": {
"issue_id": { "type": "string" }
},
"required": ["issue_id"]
}
}
]После переработки
У переработанного сервера три инструмента с префиксом tracker_. Они принимают ключи проектов и задач, понятные статусы и имена пользователей; сервер сам переводит их в идентификаторы, коды и переходы и сам проходит страницы API. Удаление не открыто и остаётся в собственном интерфейсе трекера.
[
{
"name": "tracker_search_issues",
"title": "Search issues",
"description": "Search issues in one project of the tracker. Returns at most `limit` issues, newest first. Use the concise format to answer questions and the detailed format when you need issue fields for a follow-up call. If more issues match, the result says so; narrow the query instead of raising the limit.",
"inputSchema": {
"type": "object",
"properties": {
"project": {
"type": "string",
"description": "Project key, for example WEB"
},
"status": {
"type": "string",
"enum": ["open", "in_progress", "done"],
"description": "Filter by status. Omit to search all statuses"
},
"assignee": {
"type": "string",
"description": "Username of the assignee, or me for the current user"
},
"query": {
"type": "string",
"description": "Words to find in the title or description"
},
"limit": {
"type": "integer",
"minimum": 1,
"maximum": 50,
"default": 20
},
"response_format": {
"type": "string",
"enum": ["concise", "detailed"],
"default": "concise"
}
},
"required": ["project"],
"additionalProperties": false
},
"outputSchema": {
"type": "object",
"properties": {
"issues": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": { "type": "string" },
"title": { "type": "string" },
"status": { "type": "string" },
"assignee": { "type": "string" }
},
"required": ["key", "title", "status"]
}
},
"truncated": { "type": "boolean" }
},
"required": ["issues", "truncated"]
},
"annotations": {
"readOnlyHint": true,
"openWorldHint": false
}
},
{
"name": "tracker_create_issue",
"title": "Create issue",
"description": "Create an issue in a project. Returns the key of the new issue, for example WEB-143. Each call creates a new issue, so do not repeat a call that succeeded.",
"inputSchema": {
"type": "object",
"properties": {
"project": { "type": "string", "description": "Project key, for example WEB" },
"title": { "type": "string", "maxLength": 200 },
"description": { "type": "string" },
"priority": {
"type": "string",
"enum": ["low", "medium", "high"],
"default": "medium"
}
},
"required": ["project", "title"],
"additionalProperties": false
},
"annotations": {
"readOnlyHint": false,
"destructiveHint": false,
"idempotentHint": false,
"openWorldHint": false
}
},
{
"name": "tracker_change_issue_status",
"title": "Change issue status",
"description": "Move an issue to another status. The previous status is replaced. Moving an issue to the status it already has changes nothing.",
"inputSchema": {
"type": "object",
"properties": {
"issue": { "type": "string", "description": "Issue key, for example WEB-142" },
"status": { "type": "string", "enum": ["open", "in_progress", "done"] }
},
"required": ["issue", "status"],
"additionalProperties": false
},
"annotations": {
"readOnlyHint": false,
"destructiveHint": true,
"idempotentHint": true,
"openWorldHint": false
}
}
]Аннотации следуют определениям схемы. Создание только добавляет данные и не идемпотентно, потому что каждый вызов создаёт новую задачу. Смена статуса заменяет прежнее значение, а это не добавление, поэтому destructiveHint остаётся true; повторный вызов с тем же статусом ничего не меняет, поэтому инструмент идемпотентен. Трекер - закрытая предметная область, поэтому openWorldHint равно false.
Эндпоинт REST API и инструмент MCP
Таблица сравнивает типичный эндпоинт существующего API с инструментом сервера, спроектированного поверх него.
| Эндпоинт REST API | Инструмент MCP поверх API | |
|---|---|---|
| Потребитель | Код, написанный разработчиком | Модель, которая выбирает в каждом разговоре |
| Единица | Один ресурс и один HTTP-метод | Одна задача, возможно несколько вызовов API |
| Описание интерфейса | Документация, OpenAPI | Имя, описание, inputSchema, которые читает модель |
| Идентификаторы | Внутренние, часто UUID | Понятные ключи и имена; переводит сервер |
| Объём результатов | Полные объекты, страницы выбирает клиент | Отобранные поля, лимиты, краткий и подробный форматы |
| Ошибки | HTTP-статус и код ошибки | isError: true с текстом, который объясняет исправление |
| Побочные эффекты | Описаны в документации | Аннотации readOnlyHint, destructiveHint, idempotentHint |
| Учётные данные | Токен API | Токен, выданный для MCP-сервера; отдельный токен для API |
| Состояние | Ресурсы и идентификаторы API | Явные идентификаторы, передаваемые как аргументы |
Эндпоинт остаётся основой: логика сервиса, проверки и контроль доступа остаются в API. Инструмент добавляет слой, который нужен модели: выбор операций, формулировки, упрощение и ограничения.
Два интерфейса развиваются раздельно: новое описание инструмента может изменить поведение модели без единого изменения в API, поэтому инструменты проверяют на моделях, как описано в статье Тестирование MCP-серверов. Общие методы оценки приложений на языковых моделях описаны в статье Тестирование приложений на LLM.
Термины
| Термин | Значение |
|---|---|
| MCP-сервер поверх существующего API | MCP-сервер, который даёт модели доступ к сервису через его существующий API и превращает его операции в инструменты |
| Аннотации инструмента | Необязательные подсказки о поведении инструмента в его определении: readOnlyHint, destructiveHint, idempotentHint, openWorldHint |
| Пространство имён инструментов (namespacing) | Общий префикс или суффикс в именах инструментов, который группирует их по сервису или ресурсу |
| Формат ответа | Параметр инструмента, с которым модель выбирает краткий или подробный результат |
| Ошибка выполнения инструмента | Результат инструмента с isError: true и текстом, по которому модель может исправить вызов |
| Передача токена дальше (token passthrough) | Пересылка токена клиента в другой сервис; запрещена спецификацией |
| MCP-прокси-сервер | MCP-сервер, который подключает клиентов к стороннему API и выступает единственным OAuth-клиентом этого API |
| Идентификатор состояния | Идентификатор, который возвращает инструмент и который модель передаёт в следующих вызовах |
Источники
- Спецификация Model Context Protocol, версия 2026-07-28(откроется в новой вкладке) (на английском)
- Tools, спецификация 2026-07-28(откроется в новой вкладке) (на английском)
- Caching, спецификация 2026-07-28(откроется в новой вкладке) (на английском)
- Pagination, спецификация 2026-07-28(откроется в новой вкладке) (на английском)
- Authorization: Security Considerations, спецификация 2026-07-28(откроется в новой вкладке) (на английском)
- Key Changes, спецификация 2026-07-28(откроется в новой вкладке) (на английском)
- Схема спецификации 2026-07-28 (schema.ts), GitHub(откроется в новой вкладке) (на английском)
- Security Best Practices(откроется в новой вкладке) (на английском)
- Tool Annotations as Risk Vocabulary: What Hints Can and Can't Do, блог MCP, 16 марта 2026(откроется в новой вкладке) (на английском)
- Writing effective tools for agents, Anthropic, 11 сентября 2025(откроется в новой вкладке) (на английском)
- JSON Schema Validation, draft 2020-12(откроется в новой вкладке) (на английском)