Регрессионное тестирование MCP-сервера - проверка того, что новая версия сервера не сломала то, что работало в предыдущей: контракт, на который опираются клиенты (списки инструментов, ресурсов и промптов с их схемами и смыслом ошибок), поддерживаемые версии протокола и поведение моделей, которые выбирают инструменты по описаниям. Общее понятие регрессионного тестирования описано в статье Тестовый прогон; основы тестирования MCP-серверов - в статье Тестирование MCP-серверов: основы.
Текущая версия спецификации MCP - 2026-07-28. Спецификация определяет, как обозначаются и согласуются версии протокола, а с этой версии - и формальную политику вывода функций протокола из употребления. Версионирование инструментов конкретного сервера и правила их совместимости она не определяет: это остаётся задачей автора сервера. С SEP-3398, принятого 14 октября 2026, новые версии спецификации планируются раз в шесть месяцев.
Кратко
| Параметр | Значение |
|---|---|
| Объект | Контракт сервера, поведение по протоколу, поведение моделей |
| Контракт | tools/list, resources/list, resources/templates/list, prompts/list, server/discover |
| Эталон | Снимок контракта и результаты предыдущего релиза |
| Версии протокола | Идентификаторы YYYY-MM-DD, текущая 2026-07-28 |
| Срок устаревания | Не меньше двенадцати месяцев, при ускоренном удалении не меньше девяноста дней |
| Инструменты | MCP Inspector CLI, набор тестов соответствия, набор задач оценки |
| Правила совместимости инструментов | Спецификацией не определены |
Какую задачу решает
Между релизами у MCP-сервера добавляются и переименовываются инструменты, правятся описания, обновляются SDK и поддерживаемые версии протокола. Каждый вид потребителей сервера зависит от своей его части.
- Клиенты кешируют списки. Результаты
tools/list,prompts/list,resources/listиresources/templates/listнесутttlMs, и клиент может пользоваться кешированным списком, пока срок не истёк. - Настройки хоста ссылаются на инструменты по имени. В Claude Code, например, правило разрешений называет инструмент в виде
mcp__<server>__<tool>. - Скрипты и агенты вызывают инструменты с заранее заданными аргументами и разбирают структурированные результаты.
- Модели выбирают инструменты по имени, описанию и схеме.
У инструмента в протоколе нет поля версии: описание инструмента состоит из name, title, description, icons, inputSchema, outputSchema, annotations и _meta. Ломающее изменение становится видно, только если сравнить две версии контракта.
Контракт MCP-сервера
Контракт - та часть сервера, которую клиент видит, не читая его кода. В таблице - какие списки в него входят и какие поля сравниваются.
| Список | Метод | Сравниваемые поля |
|---|---|---|
| Инструменты | tools/list |
name, title, description, inputSchema, outputSchema, annotations |
| Ресурсы | resources/list |
uri, name, title, description, mimeType |
| Шаблоны ресурсов | resources/templates/list |
uriTemplate, name, title, description, mimeType |
| Промпты | prompts/list |
name, title, description, arguments с полями name, description, required |
| Сервер | server/discover |
supportedVersions, capabilities вместе с extensions, instructions |
К контракту относится и смысл ошибок, хотя ни один список его не описывает: какие сбои возвращаются ошибкой JSON-RPC, а какие - результатом инструмента с isError: true, и с какими кодами. Его проверяют тестами ошибочных случаев, а не снимком. Устройство самих списков описано в статьях Инструменты (tools) в MCP и Промпты (prompts) в MCP.
Снимки контракта
Как снимается снимок
Снимок контракта - сохранённый и нормализованный результат методов-списков одной версии сервера. Снимок последнего релиза хранится в репозитории и служит эталоном; с ним сравнивается снимок версии-кандидата.
CLI MCP Inspector выполняет один запрос за вызов и с --format json выводит результат в JSON. Флаг --protocol-era выбирает эпоху протокола: legacy (по умолчанию, рукопожатие initialize), auto (сначала проба server/discover, затем откат к initialize) или modern (ровно 2026-07-28, без отката). Снимок современного сервера снимают с --protocol-era modern; без флага CLI подключается в эпохе legacy.
set -euo pipefail
mkdir -p snapshot
inspect() {
npx @modelcontextprotocol/inspector --cli node build/index.js \
--protocol-era modern --method "$1" --format json
}
inspect tools/list | jq -S '.result.tools | sort_by(.name)' > snapshot/tools.json
inspect resources/list | jq -S '.result.resources | sort_by(.uri)' > snapshot/resources.json
inspect resources/templates/list \
| jq -S '.result.resourceTemplates | sort_by(.uriTemplate)' > snapshot/resource-templates.json
inspect prompts/list | jq -S '.result.prompts | sort_by(.name)' > snapshot/prompts.jsonНормализация оставляет только списки и сортирует их по устойчивому ключу, поэтому одна только смена порядка не выглядит как изменение контракта. Поля вроде nextCursor, ttlMs и cacheScope описывают доставку, а не контракт, и в снимок не входят.
На содержимое снимка влияют два правила спецификации. Во-первых, список инструментов может зависеть от авторизации запроса, например от прав (scopes) токена. Для сервера с таким поведением снимок делается на каждый профиль авторизации. Во-вторых, списки бывают постраничными, и согласованность между страницами не гарантируется. Клиент, которому нужен согласованный полный список, запрашивает его заново с начала, и снимок следует тому же правилу.
Сравнение снимков
Текстовое сравнение нормализованных файлов (diff baseline/tools.json snapshot/tools.json) показывает каждое изменение, но не отличает ломающее от безвредного. Для этого скрипт классифицирует изменения по правилам из разделов ниже. Формат его отчёта спецификация не определяет; в примере - один из возможных форматов.
{
"baseline": "1.4.0",
"candidate": "1.5.0",
"changes": [
{
"list": "tools",
"name": "search_issues",
"kind": "removed",
"breaksClients": true,
"affectsModels": true
},
{
"list": "tools",
"name": "create_issue",
"path": "inputSchema.required",
"kind": "required_parameter_added",
"before": ["project", "title"],
"after": ["project", "title", "priority"],
"breaksClients": true,
"affectsModels": true
},
{
"list": "tools",
"name": "get_issue",
"path": "outputSchema.properties.assignee.type",
"kind": "type_changed",
"before": "string",
"after": "object",
"breaksClients": true,
"affectsModels": false
},
{
"list": "tools",
"name": "update_issue",
"path": "description",
"kind": "description_changed",
"breaksClients": false,
"affectsModels": true
}
]
}В такой схеме задача CI падает на изменениях с breaksClients: true, а изменения с affectsModels: true запускают оценку, описанную ниже.
Изменения, которые ломают клиентов
После ломающего изменения вызов, который работал с предыдущей версией, с новой завершается ошибкой, или клиент неверно понимает результат.
- Удалённый или переименованный инструмент.
tools/callсо старым именем получает ошибку протокола; пример в спецификации отвечает на неизвестный инструмент кодом JSON-RPC-32602. Клиент с кешированным списком предлагает старое имя, пока не истечётttlMs, а настройки хоста, которые называют инструмент по имени, перестают с ним совпадать. - Новый обязательный параметр. Вызовам, которые работали раньше, параметра не хватает, и они не проходят проверку. С SEP-1303 такие ошибки возвращаются как ошибки выполнения инструмента с
isError: true. - Сужение типа. Строка становится
enum, уменьшаетсяmaximum, появляетсяpattern, добавляетсяadditionalProperties: false. Значения, которые были допустимы, отклоняются. Естественные тестовые данные здесь - граничные значения старой и новой схемы; техника описана в статье Классы эквивалентности и граничные значения. - Изменённая
outputSchema. Свойство удалено или переименовано, у него сменился тип, оно ушло изrequired. Спецификация обязывает сервер возвращатьstructuredContent, соответствующийoutputSchema, и рекомендует клиентам его проверять. Клиент, который опирается на старую структуру, ломается. - Изменённый смысл ошибок. Сбой переходит из
isError: trueв ошибку JSON-RPC или обратно, либо меняется код ошибки. Такое изменение сделала и сама спецификация в 2026-07-28: код для ненайденного ресурса сменился с-32002на-32602(SEP-2164). По данным SEP-2164, официальные SDK до этого использовали для этого случая не меньше четырёх разных кодов, поэтому обновление SDK может изменить коды сервера без правок его собственного кода. - Изменённые URI ресурсов или шаблоны. Клиент, который сохранил URI или построил его по шаблону, больше не находит ресурс.
- Удалённая возможность или расширение. Клиент, который опирается на
listChangedили на расширение, переходит к запасному поведению или получает ошибку.
Клиент на Streamable HTTP обязан исключить из списка инструмент, у которого значение x-mcp-header нарушает ограничения спецификации. Поэтому неверная аннотация, добавленная к одному параметру, убирает инструмент у клиента без какой-либо ошибки на стороне сервера.
Изменения, которые влияют на модели
Некоторые изменения оставляют все вызовы допустимыми и всё равно меняют то, что делает модель, а с этим и результат задач.
- Правка описания. Описание инструмента и его параметров - основной сигнал для модели. По данным Anthropic, даже небольшие уточнения описаний инструментов могут заметно менять результаты; это верно и в обратную сторону.
- Правка
title. Спецификация предназначаетtitleдля показа пользователям. Передаёт ли хост его ещё и модели, спецификация не определяет. - Новый инструмент. Инструмент с назначением, близким к существующему, усложняет выбор.
- Изменённое
instructions. Полеinstructionsизserver/discoverклиент может добавить в системный промпт. - Изменённые аннотации. Подсказки вроде
readOnlyHintиdestructiveHintпротокол не меняют. Спецификация требует от клиентов считать их недоверенными, если сервер не доверенный, и не определяет, как хост их использует.
Сводка изменений
Таблица сводит вместе виды изменений, их влияние на клиентов и модели и проверку, которая обнаруживает каждое из них.
| Вид изменения | Ломает клиентов протокола | Влияет на модели | Как обнаружить |
|---|---|---|---|
| Инструмент удалён или переименован | Да | Да | Сравнение снимков, tools/call в дымовой проверке |
| Новый обязательный параметр | Да | Да | Сравнение required |
| Сужен тип параметра | Да, для значений вне нового диапазона | Да | Сравнение схемы, тесты граничных значений |
Изменена outputSchema |
Да, для клиентов, которые проверяют или разбирают результат | Возможно | Сравнение, проверка structuredContent в тестах |
| Изменён смысл ошибок | Да, для клиентов, которые ветвятся по ошибкам | Да | Тесты ошибочных случаев, набор тестов соответствия |
| Убрана версия протокола | Да, для клиентов на этой версии | Нет | Прогоны соответствия по ревизиям, Inspector по эпохам |
Неверный x-mcp-header |
HTTP-клиенты исключают инструмент | Да | Сравнение, проверка ограничений |
Правка описания или title |
Нет | Да | Сравнение, набор задач оценки |
| Новый инструмент | Нет | Да | Сравнение, набор задач оценки с задачами, где инструмент не нужен |
| Новый необязательный параметр | Нет | Возможно | Сравнение, набор задач оценки |
| Изменены аннотации | Нет | Не напрямую | Сравнение, проверка поведения хоста |
Поддержка версий протокола
Идентификаторы версий и согласование
Версия протокола - строка вида YYYY-MM-DD: дата последнего обратно несовместимого изменения. Совместимые изменения версию не меняют. Ревизия спецификации бывает Draft, Current или Final; текущая ревизия - 2026-07-28.
Каждый запрос объявляет свою версию в _meta, в поле io.modelcontextprotocol/protocolVersion; на Streamable HTTP то же значение передаётся ещё и в заголовке MCP-Protocol-Version. Сервер принимает или отклоняет каждый запрос независимо. На версию, которую он не поддерживает, сервер возвращает UnsupportedProtocolVersionError с поддерживаемыми версиями и запрошенной; на HTTP код ответа - 400 Bad Request.
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32022,
"message": "Unsupported protocol version",
"data": {
"supported": ["2026-07-28", "2025-11-25"],
"requested": "1900-01-01"
}
}
}Список поддерживаемых версий - часть контракта. Его возвращает server/discover в поле supportedVersions, и регрессионный тест проверяет его двумя способами: поле содержит ожидаемые версии, а запрос с неподдерживаемой версией, например 1900-01-01 выше, получает код -32022 с тем же списком в data.supported.
Прежние и современные версии
Спецификация называет версии до 2025-11-25 включительно, которые открывают сессию рукопожатием initialize, прежними (legacy), а версии начиная с 2026-07-28, которые передают версию и возможности в каждом запросе, современными (modern). Реализация dual-era поддерживает и те, и другие. Матрица совместимости показывает, что значит отказ от версии:
- прежний клиент и сервер dual-era работают;
- прежний клиент и сервер, который поддерживает только современные версии, не работают, и перейти на более новую версию прежний клиент не может;
- современный клиент и прежний сервер не работают.
Поэтому отказ сервера dual-era от прежних версий - ломающее изменение для каждого прежнего клиента. Изменения 2026-07-28 и переход с 2025-11-25 описаны в статье MCP без состояния.
Сервер dual-era тестируют на обоих вариантах протокола. У набора тестов соответствия есть набор требований для каждой ревизии (--requirements 2025-11-25, --requirements 2026-07-28), и каждый набор выполняется в версии протокола своей ревизии. По документации набора, сценарий, который относится к обеим ревизиям, нужно запускать дважды: успех в одной версии ничего не говорит о другой. MCP Inspector проверяет тот же сервер в обеих эпохах с --protocol-era legacy и --protocol-era modern.
Политика вывода из употребления
С 2026-07-28 у функций спецификации три состояния: Active, Deprecated и Removed (SEP-2596). Срок устаревания (deprecation window), то есть время, которое устаревшая функция остаётся в спецификации, - не меньше двенадцати месяцев, считая от выпуска ревизии, которая впервые пометила её как устаревшую. Удалить функцию можно в первой ревизии, выпущенной в момент окончания этого срока или позже. Для функции с активным риском безопасности срок можно сократить, но не меньше чем до девяноста дней.
Спецификация ведёт реестр устаревших функций. В 2026-07-28 в нём:
| Функция | Устарела в | Самое раннее удаление |
|---|---|---|
| Roots, Sampling, Logging | 2026-07-28 | Первая ревизия, выпущенная 28 июля 2027 или позже |
| Dynamic Client Registration | 2026-07-28 | Первая ревизия, выпущенная 28 июля 2027 или позже |
Значения includeContext thisServer и allServers |
2025-11-25 | Не позже Sampling |
| Транспорт HTTP+SSE | 2025-03-26 | Через три месяца после перехода SEP-2596 в статус Final |
Поэтому у сервера, который использует устаревшую функцию, есть известная дата, после которой новая ревизия может её не поддерживать. SDK уровня Tier 1 обязаны в следующем выпуске пометить устаревшие API штатным механизмом языка и должны выдавать предупреждение во время выполнения, когда устаревшая функция используется, поэтому новые предупреждения об устаревании в журналах сборки и выводе тестов - сигнал для проверки.
Частота выпусков
SEP-3398 планирует выпуски спецификации обычно на последний вторник марта и сентября и переносит выпуск, намеченный на декабрь 2026 года, на конец марта 2027 года. SDK уровня Tier 1 публикуют бета-версии примерно за полтора месяца до выпуска, чтобы экосистема могла их проверить. Прогон регрессионного набора на бета-версии SDK показывает влияние следующей ревизии до её выпуска.
Регресс поведения модели
Изменение, которое влияет на модели, проверяют повторным запуском набора задач оценки: те же задачи, те же модели и хосты, то же число попыток. Метод описан в статье Оценка MCP-сервера на моделях; для регрессионного тестирования важны четыре момента.
- Регрессионный набор. Anthropic отделяет оценку возможностей с низкой долей успеха от регрессионной оценки, доля успеха которой держится около 100 процентов. После правки описания первым запускается регрессионный набор.
- Парное сравнение. Старая и новая версии проходят одни и те же задачи, и результаты сравниваются по каждой задаче.
- Не только успех. Вместе с долей успеха сравнивают число вызовов инструментов, токены и ошибки инструментов. Описание, которое сохранило долю успеха, но удвоило число вызовов, - тоже регресс.
- Задачи без инструмента. Задачи, в которых правильное поведение - не вызывать инструмент, обнаруживают новый или изменённый инструмент, который вызывается там, где он не нужен.
Поводом для такого запуска служит сравнение снимков: изменённые description или title, новый инструмент, изменённое instructions.
Регрессионное тестирование в CI
На схеме - типичный конвейер: от дешёвых детерминированных проверок к дорогим статистическим.
build
|
+-- unit tests of tool handlers
+-- start the server
+-- Inspector smoke test: tools/list --strict, tools/call
+-- contract snapshot + diff with the baseline
| breaking change -> fail
| description change -> mark for evaluation
+-- conformance suite per revision, with expected failures
|
+-- evaluation set (marked changes, schedule)Набор тестов соответствия работает с сервером по HTTP. YAML-файл с известными сбоями передаётся флагом --expected-failures:
server:
- tools-call-with-progress
- server-stateless:sep-2575-server-implements-discoverЗапись называет либо весь сценарий, либо одну проверку в виде <scenario>:<check-id>. Вторая форма прощает одну проверку и оставляет в силе остальные проверки сценария. Код выхода подчиняется четырём правилам:
| Результат сценария | В файле | Код выхода |
|---|---|---|
| Не прошёл | Да | 0, ожидаемый сбой |
| Не прошёл | Нет | 1, регрессия |
| Прошёл | Да | 1, запись устарела и должна быть удалена |
| Прошёл | Нет | 0 |
По третьему правилу после исправления CI падает, пока запись не удалят. Для сервера dual-era набор запускается по разу на каждый набор требований:
npx @modelcontextprotocol/conformance server --url http://localhost:3000/mcp \
--requirements 2026-07-28 --expected-failures ./conformance-baseline.yml
npx @modelcontextprotocol/conformance server --url http://localhost:3000/mcp \
--requirements 2025-11-25 --expected-failures ./conformance-baseline.ymlПо документации набора, набор требований заморожен на момент выпуска своей ревизии и хранится в репозитории набора тестов, а файл ожидаемых сбоев хранится в репозитории реализации. Сбой, записанный в файл, остаётся сбоем относительно набора требований. Как устроены прогоны тестов в конвейерах CI в целом, описано в статье Автотесты в CI.
Проверка контракта, соответствие и оценка
Таблица сравнивает три проверки конвейера, которые отвечают на разные вопросы.
| Проверка контракта | Тестирование соответствия | Повторный запуск оценки | |
|---|---|---|---|
| Вопрос | Изменился ли собственный контракт сервера? | Соблюдает ли сервер протокол по-прежнему? | Выполняют ли модели задачи по-прежнему? |
| Эталон | Снимок предыдущего релиза | Требования ревизии | Результаты предыдущего релиза |
| Что находит | Удалённые инструменты, новые обязательные параметры, изменённые схемы | Дефекты протокола, в том числе после обновления SDK | Влияние описаний и новых инструментов |
| Детерминированность | Детерминирована | Детерминировано | Статистическая |
| Когда | При каждом изменении | При каждом изменении | При изменении описаний, по расписанию |
Проверка контракта сравнивает сервер с ним самим, тестирование соответствия - со спецификацией. Сервер может пройти тесты соответствия и всё равно сломать клиентов переименованием инструмента или сохранить контракт и не пройти тесты соответствия после обновления SDK. Как модели реагируют на изменённое описание, показывает только оценка.
Термины
| Термин | Значение |
|---|---|
| Контракт MCP-сервера | Списки инструментов, ресурсов, шаблонов ресурсов и промптов, поддерживаемые версии, возможности и смысл ошибок, которые видит клиент |
| Снимок контракта | Сохранённый и нормализованный результат методов-списков одной версии сервера |
| Ломающее изменение | Изменение, после которого вызов, работавший с предыдущей версией, завершается ошибкой или его результат понимается неверно |
| Прежние и современные версии (legacy, modern) | Версии протокола до 2025-11-25 с рукопожатием initialize и начиная с 2026-07-28 с версией и возможностями в каждом запросе |
| Реализация dual-era | Клиент или сервер, который поддерживает и прежние, и современные версии протокола |
| Срок устаревания (deprecation window) | Время, которое устаревшая функция остаётся в спецификации до возможного удаления, не меньше двенадцати месяцев |
| Ожидаемые сбои (expected failures) | Файл с известными сбоями тестов соответствия, передаётся флагом --expected-failures |
| Набор требований (requirement set) | Замороженный список сценариев соответствия, которых требует ревизия спецификации |
Источники
- Versioning and Compatibility, спецификация 2026-07-28(откроется в новой вкладке) (на английском)
- Versioning, документация MCP(откроется в новой вкладке) (на английском)
- Deprecated Features, спецификация 2026-07-28(откроется в новой вкладке) (на английском)
- Feature Lifecycle and Deprecation Policy(откроется в новой вкладке) (на английском)
- Key Changes, спецификация 2026-07-28(откроется в новой вкладке) (на английском)
- Tools, спецификация 2026-07-28(откроется в новой вкладке) (на английском)
- Caching, спецификация 2026-07-28(откроется в новой вкладке) (на английском)
- Схема спецификации 2026-07-28, schema.ts, GitHub(откроется в новой вкладке) (на английском)
- SEP-1303: Input Validation Errors as Tool Execution Errors(откроется в новой вкладке) (на английском)
- SEP-2164: Standardize Resource Not Found Error Code(откроется в новой вкладке) (на английском)
- SEP-2596: Specification Feature Lifecycle and Deprecation Policy(откроется в новой вкладке) (на английском)
- SEP-3398: Release Cycle Updates(откроется в новой вкладке) (на английском)
- MCP Inspector, клиент CLI(откроется в новой вкладке) (на английском)
- MCP Inspector, эпохи протокола(откроется в новой вкладке) (на английском)
- Набор тестов соответствия MCP, GitHub(откроется в новой вкладке) (на английском)
- Configure permissions, документация Claude Code(откроется в новой вкладке) (на английском)
- Writing effective tools for agents, Anthropic, 11 сентября 2025(откроется в новой вкладке) (на английском)
- Demystifying evals for AI agents, Anthropic, 9 января 2026(откроется в новой вкладке) (на английском)