zephyr-scale-mcp
v0.4.0
Published
MCP server for Zephyr Scale on self-hosted Jira Server / Data Center: test cases, cycles, executions, plans, folders, attachments and automation via REST API v1
Maintainers
Readme
zephyr-scale-mcp
MCP-сервер (Model Context Protocol) для Zephyr Scale на self-hosted Jira Server / Data Center (бывш. TM4J / Kanoah). Даёт ИИ-агентам (Claude Code, Claude Desktop и другим MCP-клиентам) инструменты для управления тестовой моделью через REST API v1 ({JIRA_BASE_URL}/rest/atm/1.0).
Zephyr Scale Cloud (API v2) и Zephyr Squad — другие API, этим сервером не поддерживаются.
Возможности
| Группа | Инструменты |
|---|---|
| Тест-кейсы | create_test_case, get_test_case, search_test_cases (TQL, GET/POST), update_test_case, add_test_steps, set_test_script, delete_test_case, create_test_cases_bulk, link_issues_to_test_cases, get_test_cases_linked_to_issue |
| Папки | create_folder (с рекурсивным созданием цепочки), rename_folder |
| Тест-циклы | create_test_run (с items и результатами), get_test_run, search_test_runs, delete_test_run, get_test_run_results (постранично), get_test_run_summary (сводка по статусам), recreate_test_run_with_items (обход неизменяемости циклов) |
| Результаты | create_test_result, update_last_test_result, create_test_results_bulk, get_latest_result_for_test_case |
| Тест-планы | create_test_plan, get_test_plan, update_test_plan, delete_test_plan, search_test_plans |
| Вложения | upload_attachment, list_attachments, download_attachment, delete_attachment (кейс / шаг кейса / цикл / результат / шаг результата, multipart) |
| Автоматизация | upload_automation_results, upload_cucumber_results (zip), download_feature_files (zip с .feature) |
| Композитные | clone_test_case (клонирование кейса), move_test_cases_to_folder (массовое перемещение кейсов), get_issue_test_coverage (кейсы задачи + их последние результаты) |
| Сервисные | list_environments, create_environment, find_jira_user, health_check |
| UNOFFICIAL | get_folder_tree (дерево папок), get_status_options (точные имена статусов/приоритетов проекта), delete_folder (удаление папки по id), add_test_cases_to_run (добавление кейсов в существующий цикл с сохранением ключа), update_test_run (переименование/перенос цикла на месте), remove_test_cases_from_run (удаление кейсов из цикла), delete_test_results (удаление отдельных выполнений), update_test_result_by_id (правка любого выполнения из истории), reorder_test_run_items (переупорядочивание item'ов), link_test_run_to_plan (привязка цикла к плану постфактум), get_custom_field_definitions (определения кастомных полей проекта) — internal API; регистрируются только при ZEPHYR_ALLOW_INTERNAL_API=true |
Поддерживаются все три формата скриптов тест-кейсов: STEP_BY_STEP, PLAIN_TEXT, BDD (Gherkin), включая шаги Call to Test (steps[].testCaseKey) и параметры (§parameters).
Требования
- Node.js ≥ 20
- Jira Server / Data Center с установленным плагином Zephyr Scale
- Персональный токен Jira DC (PAT, Jira 8.14+) или логин/пароль
Установка
Пакет опубликован в npm — клонировать репозиторий не нужно, в конфиге MCP-клиента достаточно npx -y zephyr-scale-mcp (см. раздел «Подключение»).
Из исходников (для разработки):
git clone https://github.com/vilaabo/zephyr-scale-mcp.git
cd zephyr-scale-mcp
npm install
npm run build # → dist/index.jsКонфигурация (переменные окружения)
| Переменная | Обяз. | Default | Назначение |
|---|---|---|---|
| JIRA_BASE_URL | да | — | Базовый URL Jira без завершающего /, напр. https://jira.example.com |
| JIRA_AUTH | нет | pat | pat | basic |
| JIRA_PAT | при pat | — | Персональный токен доступа Jira DC |
| JIRA_USERNAME, JIRA_PASSWORD | при basic | — | Логин/пароль |
| JIRA_TIMEOUT_MS | нет | 30000 | Таймаут одного HTTP-запроса |
| JIRA_MAX_RETRIES | нет | 2 | Повторы для GET и для ответов 429/503 |
| JIRA_TLS_REJECT_UNAUTHORIZED | нет | true | false разрешает самоподписанные сертификаты (отключает проверку TLS для всех запросов процесса; в stderr выводится предупреждение) |
| ZEPHYR_DEFAULT_PROJECT_KEY | нет | — | Подставляется, если инструмент вызван без projectKey |
| ZEPHYR_READONLY | нет | false | При true инструменты записи возвращают ошибку |
| ZEPHYR_ALLOW_INTERNAL_API | нет | false | При true регистрируются UNOFFICIAL-инструменты на базе internal API /rest/tests/1.0 (вендором не поддерживается — риски на пользователе) |
| ZEPHYR_LOG_LEVEL | нет | info | debug | info | warn | error (весь лог — в stderr) |
Секреты (JIRA_PAT, JIRA_PASSWORD) не пишутся в логи и не попадают в ответы инструментов и тексты ошибок.
Подключение к MCP-клиенту
claude_desktop_config.json / .mcp.json:
{
"mcpServers": {
"zephyr-scale": {
"command": "npx",
"args": ["-y", "zephyr-scale-mcp"],
"env": {
"JIRA_BASE_URL": "https://jira.example.com",
"JIRA_AUTH": "pat",
"JIRA_PAT": "<personal access token>",
"ZEPHYR_DEFAULT_PROJECT_KEY": "PROJ",
"ZEPHYR_ALLOW_INTERNAL_API": "true"
}
}
}
}Claude Code:
claude mcp add zephyr-scale \
--env JIRA_BASE_URL=https://jira.example.com \
--env JIRA_PAT=<token> \
--env ZEPHYR_DEFAULT_PROJECT_KEY=PROJ \
--env ZEPHYR_ALLOW_INTERNAL_API=true \
-- npx -y zephyr-scale-mcp(из исходников: замените npx -y zephyr-scale-mcp на node /path/to/zephyr-scale-mcp/dist/index.js)
Проверка: попросите агента вызвать health_check — он должен вернуть { ok: true, jiraUser, baseUrl, zephyrPluginReachable }.
💡
ZEPHYR_ALLOW_INTERNAL_API=true— опционально, но настоятельно рекомендуется. Флаг открывает то, чего публичный API не умеет в принципе: редактирование циклов на месте — переименование/перенос (update_test_run) и добавление кейсов в существующий цикл без смены ключа (add_test_cases_to_run), — а также дерево папок (get_folder_tree), удаление папок (delete_folder) и точные имена статусов (get_status_options). Эти инструменты ходят на те же internal-эндпоинты, что и сам UI Jira, но вендором они не поддерживаются — уберите флаг, если такой компромисс не подходит.
Примеры
Создание кейса с шагами:
{
"tool": "create_test_case",
"arguments": {
"projectKey": "PROJ",
"name": "Успешный вход",
"folder": "/Регресс/Авторизация",
"testScript": {
"type": "STEP_BY_STEP",
"steps": [
{ "description": "Открыть страницу логина", "testData": "URL: /login", "expectedResult": "Форма логина отображается" },
{ "testCaseKey": "PROJ-T45" },
{ "description": "Ввести валидные креды", "expectedResult": "Открыт дашборд" }
]
}
}
}Поиск по TQL: projectKey = "PROJ" AND folder = "/Регресс" AND status = "Approved" (синтаксис строгий: пробелы вокруг операторов, строки в двойных кавычках, только AND).
Цикл с результатами создаётся одним вызовом create_test_run — см. описание инструмента (поле items, в т.ч. scriptResults с пошаговыми статусами).
Ограничения API v1 (важно)
- Тест-циклы неизменяемы после создания.
PUT /testrun/{key}не существует: нельзя переименовать цикл, сменить папку, добавить/убрать кейсы. Состав задаётся только вcreate_test_run(полеitems). Инструменты результатов лишь находят существующий item поtestCaseKey. - Папки не создаются автоматически при создании кейсов/циклов; публичного листинга папок нет; переименование — только по числовому
id(его возвращаетcreate_folder). owner/executedBy/assignedTo— это Jira user key (JIRAUSER10000), не логин и не e-mail; для резолва используйтеfind_jira_user.- TQL строгий — только
AND, пробелы вокруг операторов, строки в двойных кавычках; для циклов доступны только поляprojectKeyиfolder. - Статусы / приоритеты / окружения регистрозависимы и передаются внутренними (нелокализованными) именами; на инстансе могут быть кастомные наборы.
- В
labelsпробелы заменяются API на_. - Устаревшие поля API не поддерживаются намеренно:
issueKey→issueLinks,executionDate→actualEndDate,userKey→executedBy. - Ключи сущностей:
PROJ-T1— кейс,PROJ-P1— план,PROJ-R1— цикл. - Серверный default
maxResults= 200; инструменты по умолчанию запрашивают 50. - Шаги STEP_BY_STEP при
PUTсинхронизируются поid: безid— создать, сid— обновить, отсутствует в списке — удалить. Поэтомуupdate_test_caseсtestScript.stepsтребует полный итоговый список; безопасное частичное добавление шагов делаетadd_test_steps(читает кейс, сливает, записывает).
Если API вашего инстанса ведёт себя иначе (старая версия плагина и т.п.) — зафиксируйте расхождение здесь и сообщите разработчику.
Известные расхождения конкретных инстансов
Обнаружены при живой проверке на Jira DC со старой версией плагина (2026-07-16, стенд jira.digital-spirit.ru, проект NBUL):
- BDD-текст — только строки шагов, без заголовков:
POST /testcaseсtestScript.type = "BDD"принимает исключительно строкиGiven/When/Then/And/But(кириллица в тексте шагов — ок, проверено вживую с round-trip байт-в-байт). Текст с обёрткойFeature:/Scenario:отклоняется с400 {"errorMessages":["Invalid BDD Script"]}— несмотря на то, что пример в §8.1 ТЗ включает эти заголовки. BDD-кейс в Zephyr Scale Server — это один сценарий;Featureгенерируется при экспорте. Описания инструментов предупреждают об этом. - Ключи циклов с префиксом
-C, а не-R(NBUL-C34) — старая нотация TM4J. На работу сервера не влияет: ключи передаются сквозняком. - Нет эндпоинта
GET /testrun/{key}/testresults/page(404 для любого ключа). Сервер автоматически переключается на устаревший плоскийGET /testrun/{key}/testresultsи пагинирует на своей стороне; в ответе появляется полеnote.onlyLastExecutionsв этом режиме эмулируется по максимальномуidна каждыйtestCaseKey. statusрезультата теряется, если в том же запросе переданscriptResults:create_test_resultсоstatus: "Pass"+scriptResultsвернул201 {id}, но общий статус осталсяNot Executed(пошаговые статусы при этом применились). БезscriptResultsстатус применяется корректно. Рабочий паттерн: сначалаcreate_test_resultсоscriptResults, затем общийstatusотдельнымupdate_last_test_result(проверено). Точные имена статусов проекта смотрите черезget_status_options(UNOFFICIAL) или в UI.POST /testcase/link-issuesотвечает 500 (эндпоинта, вероятно, ещё нет на этой версии) — используйтеupdate_test_case/create_test_caseс полемissueLinks, это работает.- Запись результата для кейса, которого нет в цикле, молча ДОБАВЛЯЕТ его в цикл (проверено живьём) — на других версиях такой вызов, наоборот, падает; описания инструментов покрывают оба поведения.
POST /testcase/bulkотвечает 500 с пустым телом на любой payload, хотя одиночное создание работает —create_test_cases_bulkраспознаёт это и автоматически создаёт кейсы по одному, возвращая{ note, created, failed? }(#1).- Custom-формат результатов автоматизации валидируется строго: работает
{"version": 1, "executions": [{"source", "result", "testCase": {"key"}}]}, а дополнительные поля выполнения (например,executionTime) отклоняются сInvalid Custom Format JSON file. Cucumber JSON-отчёты (сценарий с тегом@TestCaseKey=PROJ-T1) работают как есть; статусы шагов переносятся, упавший шаг даёт общийFail.
Обработка ошибок
Ошибки API возвращаются в формате Zephyr API error <status> (<METHOD> <path>): <тело до 2 КБ> с подсказкой для типовых причин (несуществующая папка, регистрозависимые статусы, синтаксис TQL, недоступный плагин, права). 429/503 повторяются с учётом Retry-After; сетевые ошибки и 5xx повторяются только для GET (backoff 500ms * 2^n + джиттер).
Разработка
npm run typecheck # tsc --noEmit
npm test # unit + contract (vitest + msw), без сети
npm run smoke # интеграционный сценарий против реального стенда (ZEPHYR_E2E=1)Smoke-сценарий требует реальных JIRA_BASE_URL/JIRA_PAT и ZEPHYR_DEFAULT_PROJECT_KEY (выделенный тестовый проект!) и оставляет на стенде папки /mcp-smoke-* (API не умеет удалять папки).
Структура
src/
├── index.ts # bootstrap: конфиг, регистрация инструментов, stdio-транспорт
├── config.ts # чтение и валидация env
├── http.ts # zephyrFetch(): auth, таймаут, ретраи, нормализация ошибок
├── schemas.ts # zod-схемы общих структур (Step, TestScript, поля результатов…)
├── toolkit.ts # defineTool(): strict-валидация, read-only guard, формат ответов
├── runResults.ts # чтение результатов цикла с fallback на плоский эндпоинт
└── tools/ # testCases, folders, testRuns, testResults, testPlans,
# attachments, automation, runMaintenance, misc
test/ # unit + contract (msw) + smoke.e2eФаза 3 (§7.6) — реализовано
- Вложения:
upload_attachment/list_attachments/delete_attachment. Единая адресация:target=test_case|test_run|test_result(+ опциональныйstepIndexдля кейса и результата). Загрузка — multipart, файл читается с диска машины, где запущен MCP-сервер. - Тест-планы: полный CRUD +
search_test_plans(TQL). - Автоматизация:
upload_automation_results(zip с результатами в формате Zephyr),upload_cucumber_results(zip с Cucumber JSON), опциональноautoCreateTestCases;download_feature_filesвыгружает zip с.feature-файлами BDD-кейсов в локальный файл. recreate_test_run_with_items— обход неизменяемости циклов: читает исходный цикл, создаёт новый с изменённым составом (addItems/removeTestCaseKeys), заголовочные поля наследуются от исходного;copyResults: trueпереносит последние результаты item'ов (включая пошаговыеscriptResults); исходный цикл удаляется только при явномdeleteOriginal: trueи никогда — если создание нового не удалось. Новый цикл получает новый ключ.- Internal API (за флагом
ZEPHYR_ALLOW_INTERNAL_API=true):get_folder_tree— дерево папок проекта (/rest/tests/1.0/project/{id}/foldertree/...). ПомеченUNOFFICIAL: вендор internal API не поддерживает, на другой версии плагина эндпоинт может отсутствовать или отличаться.
