Регрессионное тестирование MCP-сервера

Регрессионное тестирование 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.

Bash
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) показывает каждое изменение, но не отличает ломающее от безвредного. Для этого скрипт классифицирует изменения по правилам из разделов ниже. Формат его отчёта спецификация не определяет; в примере - один из возможных форматов.

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.

JSON
{
  "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:

yaml
server:
  - tools-call-with-progress
  - server-stateless:sep-2575-server-implements-discover

Запись называет либо весь сценарий, либо одну проверку в виде <scenario>:<check-id>. Вторая форма прощает одну проверку и оставляет в силе остальные проверки сценария. Код выхода подчиняется четырём правилам:

Результат сценария В файле Код выхода
Не прошёл Да 0, ожидаемый сбой
Не прошёл Нет 1, регрессия
Прошёл Да 1, запись устарела и должна быть удалена
Прошёл Нет 0

По третьему правилу после исправления CI падает, пока запись не удалят. Для сервера dual-era набор запускается по разу на каждый набор требований:

Bash
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) Замороженный список сценариев соответствия, которых требует ревизия спецификации

Источники

Серия

Model Context Protocol (MCP)
  1. Что такое MCP (Model Context Protocol)
  2. Инструменты (tools) в MCP
  3. Ресурсы и шаблоны ресурсов в MCP
  4. Промпты (prompts) в MCP
  5. Транспорты MCP: stdio и Streamable HTTP
  6. MCP без состояния: изменения 2026-07-28
  7. Авторизация в MCP
  8. Безопасность MCP
  9. Расширения MCP
  10. MCP-сервер поверх существующего API
  11. Тестирование MCP-серверов: основы
  12. Оценка MCP-сервера на моделях
  13. Регрессионное тестирование MCP-сервераТекущая
  14. Публикация MCP-сервера в MCP Registry

Статьи Wiki пишет и поддерживает команда ComplexQA.