🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

zephyr-scale-mcp

Package Overview
Dependencies
Maintainers
1
Versions
6
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

zephyr-scale-mcp

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

latest
Source
npmnpm
Version
1.0.0
Version published
Maintainers
1
Created
Source

zephyr-scale-mcp

MCP server for Zephyr Scale on self-hosted Jira Server / Data Center (formerly TM4J). It gives an AI agent — Claude Code, Claude Desktop, Cursor, any MCP client — 54 tools over your test management: test cases, folders, test cycles, executions, test plans, attachments and automation imports, through the Zephyr Scale REST API v1 at {JIRA_BASE_URL}/rest/atm/1.0.

Node TypeScript Tests License API

npm: zephyr-scale-mcp · MCP Registry: io.github.vilaabo/zephyr-scale-mcp · Русская версия →

⚠️ Scope: Server / Data Center only. Zephyr Scale Cloud (API v2) and Zephyr Squad are different APIs and are out of scope — this server will not work against them.

Why this exists

Most Zephyr MCP servers target the Cloud API. If your Jira is on-premise you get the v1 API instead, and v1 has teeth: test runs are immutable after creation, folders cannot be listed, statuses are case-sensitive internal names, BDD scripts reject a Feature: header, and older plugin builds are missing whole endpoints. This server encodes that knowledge instead of leaving the agent to discover it one 400 at a time.

  • 54 tools (42 public + 11 opt-in internal-API tools), each with a description that states the endpoint, the constraints a caller cannot guess, and the exact response shape.
  • Composite tools for the API's blind spotsadd_test_steps merges steps by id (read → merge → write) so nothing is silently deleted; recreate_test_run_with_items works around run immutability and can carry the last results over; get_issue_test_coverage, get_test_run_summary, clone_test_case, move_test_cases_to_folder.
  • Degradation on older builds — automatic fallback when the paginated results endpoint is absent, and a per-item fallback when the bulk create endpoint is broken. Both were found on a real legacy instance, not in a changelog.
  • Plumbing that fails loudly — strict zod input validation, Retry-After-aware retries with exponential backoff, error messages carrying actionable hints, read-only mode, secrets kept out of logs and tool output.
  • 1177 unit and contract tests (vitest + msw, no network) plus a gated end-to-end scenario against a real instance.

Quick start

Requirements: Node.js >= 20, Jira Server/DC with the Zephyr Scale plugin, and a Personal Access Token (Jira 8.14+) or a username/password pair.

Nothing to clone — the package is on npm. Claude Code:

claude mcp add zephyr-scale \
  --env JIRA_BASE_URL=https://jira.example.com \
  --env JIRA_PAT=<personal access token> \
  --env ZEPHYR_DEFAULT_PROJECT_KEY=PROJ \
  --env ZEPHYR_ALLOW_INTERNAL_API=true \
  -- npx -y zephyr-scale-mcp

Claude Desktop / any MCP client (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_PAT": "<personal access token>",
        "ZEPHYR_DEFAULT_PROJECT_KEY": "PROJ",
        "ZEPHYR_ALLOW_INTERNAL_API": "true"
      }
    }
  }
}
Running from source instead
git clone https://github.com/vilaabo/zephyr-scale-mcp.git
cd zephyr-scale-mcp
npm install
npm run build        # -> dist/index.js

Then point the client at the build: "command": "node", "args": ["/path/to/zephyr-scale-mcp/dist/index.js"].

ZEPHYR_ALLOW_INTERNAL_API=true is optional and recommended. It registers 12 extra tools that reach the things the public API cannot do at all: editing a test run in place — renaming or moving it (update_test_run) and adding or removing cases without changing its key (add_test_cases_to_run, remove_test_cases_from_run) — plus listing the folder tree (get_folder_tree), deleting folders (delete_folder), editing older executions (update_test_result_by_id) and reading the exact status names the API silently expects (get_status_options). These call the same undocumented /rest/tests/1.0 endpoints the Jira UI itself uses; the vendor does not support them and they may differ or be absent on another Zephyr Scale version. Leave the flag off if that trade-off is not acceptable — the other 42 tools are unaffected.

Then ask the agent to run health_check. It verifies connectivity and credentials via GET /rest/api/2/myself and, when ZEPHYR_DEFAULT_PROJECT_KEY is set, that the Zephyr plugin answers on /rest/atm/1.0.

What you can ask your agent to do

  • "Create the folder /Regression/Payments and add step-by-step test cases for the checkout flow described in this document."
  • "Find every Draft case in /Regression, review them, and set the ready ones to Approved."
  • "Create a cycle for sprint 42 with all smoke cases, then record the results from this report — step by step where the script has steps."
  • "Which test cases cover PROJ-123, and when did each of them last pass?" — traceability from the issue to its cases and their latest executions.
  • "Take this ZIP of Cucumber JSON reports and publish it as a new cycle in PROJ." — automation import; scenarios are matched to BDD cases by their @TestCaseKey=PROJ-T1 tag.
  • "Add two steps to PROJ-T55 after step 3." — existing steps and their ids survive.
  • "Recreate cycle PROJ-R7 with three more cases, keep the results, and delete the original."

Configuration

VariableRequiredDefaultPurpose
JIRA_BASE_URLyesJira base URL without a trailing /, e.g. https://jira.example.com
JIRA_AUTHnopatpat | basic
JIRA_PATwith patJira Server/DC Personal Access Token
JIRA_USERNAME, JIRA_PASSWORDwith basicBasic-auth credentials
JIRA_TIMEOUT_MSno30000Per-request timeout
JIRA_MAX_RETRIESno2Retries for GET and for any 429/503, honoring Retry-After, otherwise exponential backoff with jitter
JIRA_TLS_REJECT_UNAUTHORIZEDnotruefalse accepts self-signed certificates — this disables TLS verification process-wide and prints a warning to stderr
ZEPHYR_DEFAULT_PROJECT_KEYnoUsed whenever a tool is called without projectKey
ZEPHYR_READONLYnofalsetrue makes every write tool refuse with an error; read tools keep working
ZEPHYR_ALLOW_INTERNAL_APInofalsetrue registers the 11 UNOFFICIAL tools backed by the internal /rest/tests/1.0 API
ZEPHYR_LOG_LEVELnoinfodebug | info | warn | error

Two guarantees the tests cover: JIRA_PAT and JIRA_PASSWORD never appear in logs, tool output or error messages — in any encoding they can take on the way out: raw, JSON-escaped, or the base64 basic-auth token (error text carries the method and path only — never the query string, which may contain data) — and stdout is reserved for the MCP protocol, every log line goes to stderr. One deliberate exception: a secret shorter than six characters is left alone, because redacting it would corrupt unrelated output while protecting nothing; the server warns about it at startup.

Configuration is validated at startup: all problems are reported at once and the process exits with a non-zero code rather than starting half-configured.

Tools

54 tools. The 42 public ones are always registered; the 11 in the last group only with ZEPHYR_ALLOW_INTERNAL_API=true.

Test cases — 13 tools
ToolWhat it does
create_test_caseCreate a case with a STEP_BY_STEP / PLAIN_TEXT / BDD script, parameters, custom fields, Call-to-Test steps
get_test_caseRead a case, optionally narrowed by fields; step ids come back here
search_test_casesTQL search with pagination; a query longer than 1500 characters is sent as POST /testcase/search (which supports only projectKey, key, name)
update_test_casePartial update; testScript.steps is synchronized by id (see limitation 7)
add_test_stepsInsert steps at a position without losing the existing ones — read, merge by id, write back
set_test_scriptReplace the whole script or change its format; destructive by design
clone_test_caseCopy a case inside its project with fresh step ids; links, attachments and history are not copied
move_test_cases_to_folderBulk-move by explicit keys or by source folder; a failing case does not abort the rest
delete_test_casePermanent delete of the case, its script and its history
create_test_cases_bulkCreate many cases in one call, with a per-case fallback on broken bulk endpoints
link_issues_to_test_casesBulk-link cases to Jira issues (additive)
get_test_cases_linked_to_issueReverse lookup: issue → cases
get_issue_test_coverageTraceability report: issue → linked cases → latest execution of each
Test cycles (test runs) — 7 tools
ToolWhat it does
create_test_runCreate a cycle with its complete item list — optionally with each item's execution result in the same call
get_test_runRead a cycle including its items
search_test_runsTQL search — for runs only projectKey and folder are searchable
delete_test_runPermanent delete of the cycle and all its results
get_test_run_resultsPage through the executions of a cycle, with the legacy flat-endpoint fallback
get_test_run_summaryLast execution per item: byStatus counts verbatim, executionProgressPct, passRatePct when a literal Pass status exists
recreate_test_run_with_itemsThe public workaround for run immutability: rebuild under a new key with cases added or removed, optionally carrying the last results over and deleting the original
Executions (test results) — 4 tools
ToolWhat it does
create_test_resultAppend a new execution to a run item, including per-step scriptResults
update_last_test_resultPartial update of the most recent execution of an item
create_test_results_bulkMany executions for one cycle in a single call
get_latest_result_for_test_caseThe execution of a case with the greatest stored execution date, across all cycles — not necessarily the one recorded last

Items that exist several times in a run (per environment or per assignee) are disambiguated with matchEnvironment / matchUserKey, sent as query parameters.

Test plans — 5 tools
ToolWhat it does
create_test_planCreate a plan; returns { key }, e.g. PROJ-P123
get_test_planRead a plan with its linked runs and issues
update_test_planPartial update
delete_test_planPermanent delete
search_test_plansTQL search; the searchable field set varies by Zephyr Scale version
Folders — 2 tools
ToolWhat it does
create_folderCreate a case / plan / cycle folder from a full path; with recursive (default true) a 400 on the full path triggers creating each parent prefix and one retry — 403, 409 and 5xx propagate untouched
rename_folderRename one folder segment by its numeric id (and optionally set its custom fields)
Attachments — 4 tools
ToolWhat it does
upload_attachmentAttach a local file to a case, a case step, a cycle, a result or a result step (multipart)
list_attachmentsList the attachments of any of those targets; each record carries the id and url the other tools need
download_attachmentSave an attachment by id or by the url list_attachments returned — that url must be on the configured Jira host. The only public tool that reads from the internal API: attachment content is served by /rest/tests/1.0/attachment/{id}, which exists regardless of ZEPHYR_ALLOW_INTERNAL_API
delete_attachmentPermanently delete one attachment by numeric id
Automation — 3 tools
ToolWhat it does
upload_automation_resultsPublish a ZIP of results in Zephyr's custom JSON format; always creates a new cycle
upload_cucumber_resultsPublish a ZIP of Cucumber JSON reports; scenarios are matched by their @TestCaseKey=PROJ-T1 tag
download_feature_filesExport BDD cases as a ZIP of .feature files; tql is required and uses the testCase.-prefixed dialect. The archive is written only after its PK signature is verified, so an HTML login page served with HTTP 200 fails instead of leaving a corrupt file
Service and diagnostics — 4 tools
ToolWhat it does
health_checkJira reachability, credentials, and whether the Zephyr plugin answers
list_environmentsThe project's environments — the exact case-sensitive names results reference
create_environmentCreate an environment in the project
find_jira_userResolve the Jira user key (JIRAUSER10000) that owner / executedBy / assignedTo require
UNOFFICIAL — internal API, opt-in — 11 tools

Registered only with ZEPHYR_ALLOW_INTERNAL_API=true. These call /rest/tests/1.0, the undocumented API behind the Jira UI. The vendor does not support it: endpoints may differ or be absent on another Zephyr Scale version, and a 404/405 from one of these tools means exactly that. Every request shape below was either captured from the Jira UI's own traffic or verified live against a real instance — never guessed. Errors from this layer carry a hint saying so.

ToolWhat it does
update_test_runRename a cycle, move it to another folder (by numeric folder id) or change its planned dates in place, keeping its key, items and results. No PUT /testrun exists in the public API
add_test_cases_to_runAppend cases to an existing cycle in place; the key and the existing results survive
remove_test_cases_from_runRemove items from an existing cycle in place — their whole execution history dies with them
reorder_test_run_itemsReorder the items of an existing cycle; a cycle already in the requested order makes no write at all
link_issues_to_test_runLink Jira issues to an existing cycle — the public API rejects an issueLinks field on runs outright, so this is the only way
link_test_run_to_planAssociate an existing cycle with a test plan after creation (the public API accepts testPlanKey only at creation)
delete_test_resultsDelete individual executions by numeric id. The last execution of an item cannot be deleted
update_test_result_by_idEdit any execution in an item's history, including older ones; status is resolved from its case-sensitive name to the internal id
get_folder_treeThe full folder tree of a project with the numeric ids rename_folder, delete_folder and update_test_run need. Each entity type has its own tree
get_status_optionsThe exact internal names of the project's execution statuses, case statuses or priorities — the values the public API silently ignores when they are wrong
get_custom_field_definitionsCustom field definitions per entity type: names, types, required flags, options
delete_folderDelete a folder by numeric id. What happens to a non-empty folder is version-specific — empty it first

Working around API v1

These are the constraints the server is built around. Every tool description repeats the ones relevant to it, so the agent sees them at call time.

  • Test runs are immutable. There is no PUT /testrun: a run cannot be renamed, moved, or have cases added or removed. Its items are fixed at creation and the run status is derived from item statuses. Escape hatches: recreate_test_run_with_items (public, produces a new key) or the internal update_test_run / add_test_cases_to_run / remove_test_cases_from_run (same key).
  • Folders are never created implicitly. create_test_case, create_test_run and create_test_plan fail with 400 on an unknown folder. Folders also cannot be listed through the public API, and renaming needs the numeric id returned by create_folder — or get_folder_tree with the internal API enabled.
  • owner / executedBy / assignedTo take a Jira user key (JIRAUSER10000), never a username or an e-mail. find_jira_user resolves it.
  • TQL is strict: spaces around operators are mandatory, string values go in double quotes, AND is the only connector (no OR), and folder paths start with /. Test runs are searchable by projectKey and folder only. For single/multi-choice custom fields = does not work — use IN.
  • Statuses, priorities and environments are case-sensitive internal names. Built-in statuses are shown localized in the UI (the label you see for Draft may be a translation) while custom ones use their literal names — get_status_options shows what the API actually expects. A wrong execution status is silently ignored by the public API.
  • A BDD script is the scenario body only — bare Given / When / Then / And / But lines. Text wrapped in Feature: / Scenario: is rejected with 400 Invalid BDD Script; the wrapper is generated on export.
  • STEP_BY_STEP steps are synchronized by id on PUT: a step without an id is created, a step with an id is updated, and every stored step missing from the list is deleted. Always send the complete final list with ids carried over from get_test_case — or let add_test_steps do the read-merge-write.
  • Deprecated fields are intentionally not accepted: use issueLinks instead of issueKey, actualEndDate instead of executionDate, executedBy instead of userKey.

Quirks of older plugin builds

All of the following was observed live on a real legacy Zephyr Scale Server instance and is covered by tests.

  • Cycle keys may use the -C prefix (PROJ-C34) instead of -R. Every run-key parameter takes the key as-is, so pass whatever your instance shows.
  • GET /testrun/{key}/testresults/page may not exist. get_test_run_results and get_test_run_summary fall back to the deprecated flat endpoint and paginate client-side, adding a note to the response. A run that genuinely does not exist still surfaces as a 404.
  • An overall status sent together with scriptResults is ignored. Send the step results first, then set the overall status with update_last_test_result.
  • POST /testcase/link-issues may answer 500. Link through update_test_case with issueLinks instead.
  • POST /testcase/bulk may answer HTTP 500 with an empty body for any payload while single creation works. create_test_cases_bulk falls back to creating the cases one by one — on any 5xx, and on a JSON 404 (which means "no such endpoint on this build", unlike the HTML 404 Jira serves when the plugin is absent) — and reports which ones succeeded (#1). Every other 4xx is a payload error and is not retried.
  • Posting a result for a case that is not among the run's items silently adds it to the run on this build; other builds reject the call with 400/404. The result tools document both.
  • The custom automation-results format is validated strictly. {"version": 1, "executions": [{"source", "result", "testCase": {"key"}}]} works; extra per-execution fields such as executionTime are rejected with Invalid Custom Format JSON file. Cucumber JSON reports work as-is when the scenario carries @TestCaseKey=PROJ-T1.
  • download_feature_files requires the tql query parameter — the API rejects the call without it.
  • The last execution of a run item cannot be deleted. delete_test_results rejects it; remove the item instead.
  • Custom fields may be absent entirely — the definitions endpoint then returns [].

Development

npm run typecheck    # tsc --noEmit, strict
npm test             # 1177 unit + contract tests (vitest + msw), no network
npm run build        # tsup -> dist/index.js
npm run smoke        # 13 end-to-end tests, ZEPHYR_E2E=1, real instance required

The smoke scenario is skipped unless ZEPHYR_E2E=1. It needs a real JIRA_BASE_URL, credentials and a dedicated ZEPHYR_DEFAULT_PROJECT_KEY — it creates and deletes real entities and leaves /mcp-smoke-* folders behind, since the public API cannot delete folders.

src/
├── index.ts             # bootstrap: config, tool registration, stdio transport
├── config.ts            # environment validation
├── http.ts              # fetch wrapper: auth, timeouts, retries, error normalization,
│                        #   multipart and binary bodies
├── schemas.ts           # shared parameter schemas, field shapes, recurring description constants
├── toolkit.ts           # defineTool(): strict zod input, read-only guard,
│                        #   JSON / isError response shaping, shared helpers
├── internal.ts          # the UNOFFICIAL /rest/tests/1.0 layer: key -> id resolution,
│                        #   run-item plumbing, status resolution, error hints
├── runResults.ts        # paged run results with the legacy fallback
├── log.ts               # leveled logger, stderr only
└── tools/               # one module per tool family: testCases, testRuns, testResults,
                         #   testPlans, folders, attachments, automation, misc,
                         #   runMaintenance, resultsMaintenance, internalRefs
test/                    # one test module per source module + negative contract tests
                         #   and the gated smoke scenario

Versioning and changelog

Version 1.0.0. Tool names, parameter names and response shapes are a public contract from this release on and change only in a major version. See CHANGELOG.md.

License

MIT

Keywords

mcp

FAQs

Package last updated on 31 Jul 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts