Server data from the Official MCP Registry
Zephyr Scale on self-hosted Jira Server/DC: test cases, cycles, executions, plans and attachments
About
Zephyr Scale on self-hosted Jira Server/DC: test cases, cycles, executions, plans and attachments
Security Report
Valid MCP server (1 strong, 1 medium validity signals). No known CVEs in dependencies. Package registry verified. Imported from the Official MCP Registry.
4 files analyzed · 1 issue found
Security scores are indicators to help you make informed decisions, not guarantees. Always review permissions before connecting any MCP server.
Permissions Required
This plugin requests these system permissions. Most are normal for its category.
What You'll Need
Set these up before or after installing:
Environment variable: JIRA_BASE_URL
Environment variable: JIRA_PAT
Environment variable: JIRA_AUTH
Environment variable: JIRA_USERNAME
Environment variable: JIRA_PASSWORD
Environment variable: ZEPHYR_DEFAULT_PROJECT_KEY
Environment variable: ZEPHYR_ALLOW_INTERNAL_API
Environment variable: ZEPHYR_READONLY
Environment variable: JIRA_TLS_REJECT_UNAUTHORIZED
Environment variable: JIRA_TIMEOUT_MS
Environment variable: JIRA_MAX_RETRIES
Environment variable: ZEPHYR_LOG_LEVEL
How to Install
Add this to your MCP configuration file:
{
"mcpServers": {
"io-github-vilaabo-zephyr-scale-mcp": {
"env": {
"JIRA_PAT": "your-jira-pat-here",
"JIRA_AUTH": "your-jira-auth-here",
"JIRA_BASE_URL": "your-jira-base-url-here",
"JIRA_PASSWORD": "your-jira-password-here",
"JIRA_USERNAME": "your-jira-username-here",
"JIRA_TIMEOUT_MS": "your-jira-timeout-ms-here",
"ZEPHYR_READONLY": "your-zephyr-readonly-here",
"JIRA_MAX_RETRIES": "your-jira-max-retries-here",
"ZEPHYR_LOG_LEVEL": "your-zephyr-log-level-here",
"ZEPHYR_ALLOW_INTERNAL_API": "your-zephyr-allow-internal-api-here",
"ZEPHYR_DEFAULT_PROJECT_KEY": "your-zephyr-default-project-key-here",
"JIRA_TLS_REJECT_UNAUTHORIZED": "your-jira-tls-reject-unauthorized-here"
},
"args": [
"-y",
"zephyr-scale-mcp"
],
"command": "npx"
}
}
}Documentation
View on GitHubFrom the project's GitHub README.
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.
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 + 12 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 spots —
add_test_stepsmerges steps by id (read → merge → write) so nothing is silently deleted;recreate_test_run_with_itemsworks 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. - 1181 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"
}
}
}
}
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=trueis 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.0endpoints 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/Paymentsand 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-T1tag. - "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
| Variable | Required | Default | Purpose |
|---|---|---|---|
JIRA_BASE_URL | yes | — | Jira base URL without a trailing /, e.g. https://jira.example.com |
JIRA_AUTH | no | pat | pat | basic |
JIRA_PAT | with pat | — | Jira Server/DC Personal Access Token |
JIRA_USERNAME, JIRA_PASSWORD | with basic | — | Basic-auth credentials |
JIRA_TIMEOUT_MS | no | 30000 | Per-request timeout |
JIRA_MAX_RETRIES | no | 2 | Retries for GET and for any 429/503, honoring Retry-After, otherwise exponential backoff with jitter |
JIRA_TLS_REJECT_UNAUTHORIZED | no | true | false accepts self-signed certificates — this disables TLS verification process-wide and prints a warning to stderr |
ZEPHYR_DEFAULT_PROJECT_KEY | no | — | Used whenever a tool is called without projectKey |
ZEPHYR_READONLY | no | false | true makes every write tool refuse with an error; read tools keep working |
ZEPHYR_ALLOW_INTERNAL_API | no | false | true registers the 12 UNOFFICIAL tools backed by the internal /rest/tests/1.0 API |
ZEPHYR_LOG_LEVEL | no | info | debug | 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 12 in the last group only with ZEPHYR_ALLOW_INTERNAL_API=true.
| Tool | What it does |
|---|---|
create_test_case | Create a case with a STEP_BY_STEP / PLAIN_TEXT / BDD script, parameters, custom fields, Call-to-Test steps |
get_test_case | Read a case, optionally narrowed by fields; step ids come back here |
search_test_cases | TQL search with pagination; a query longer than 1500 characters is sent as POST /testcase/search (which supports only projectKey, key, name) |
update_test_case | Partial update; testScript.steps is synchronized by id (see limitation 7) |
add_test_steps | Insert steps at a position without losing the existing ones — read, merge by id, write back |
set_test_script | Replace the whole script or change its format; destructive by design |
clone_test_case | Copy a case inside its project with fresh step ids; links, attachments and history are not copied |
move_test_cases_to_folder | Bulk-move by explicit keys or by source folder; a failing case does not abort the rest |
delete_test_case | Permanent delete of the case, its script and its history |
create_test_cases_bulk | Create many cases in one call, with a per-case fallback on broken bulk endpoints |
link_issues_to_test_cases | Bulk-link cases to Jira issues (additive) |
get_test_cases_linked_to_issue | Reverse lookup: issue → cases |
get_issue_test_coverage | Traceability report: issue → linked cases → latest execution of each |
| Tool | What it does |
|---|---|
create_test_run | Create a cycle with its complete item list — optionally with each item's execution result in the same call |
get_test_run | Read a cycle including its items |
search_test_runs | TQL search — for runs only projectKey and folder are searchable |
delete_test_run | Permanent delete of the cycle and all its results |
get_test_run_results | Page through the executions of a cycle, with the legacy flat-endpoint fallback |
get_test_run_summary | Last execution per item: byStatus counts verbatim, executionProgressPct, passRatePct when a literal Pass status exists |
recreate_test_run_with_items | The 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 |
| Tool | What it does |
|---|---|
create_test_result | Append a new execution to a run item, including per-step scriptResults |
update_last_test_result | Partial update of the most recent execution of an item |
create_test_results_bulk | Many executions for one cycle in a single call |
get_latest_result_for_test_case | The 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.
| Tool | What it does |
|---|---|
create_test_plan | Create a plan; returns { key }, e.g. PROJ-P123 |
get_test_plan | Read a plan with its linked runs and issues |
update_test_plan | Partial update |
delete_test_plan | Permanent delete |
search_test_plans | TQL search; the searchable field set varies by Zephyr Scale version |
| Tool | What it does |
|---|---|
create_folder | Create 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_folder | Rename one folder segment by its numeric id (and optionally set its custom fields) |
| Tool | What it does |
|---|---|
upload_attachment | Attach a local file to a case, a case step, a cycle, a result or a result step (multipart) |
list_attachments | List the attachments of any of those targets; each record carries the id and url the other tools need |
download_attachment | Save 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_attachment | Permanently delete one attachment by numeric id |
| Tool | What it does |
|---|---|
upload_automation_results | Publish a ZIP of results in Zephyr's custom JSON format; always creates a new cycle |
upload_cucumber_results | Publish a ZIP of Cucumber JSON reports; scenarios are matched by their @TestCaseKey=PROJ-T1 tag |
download_feature_files | Export 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 |
| Tool | What it does |
|---|---|
health_check | Jira reachability, credentials, and whether the Zephyr plugin answers |
list_environments | The project's environments — the exact case-sensitive names results reference |
create_environment | Create an environment in the project |
find_jira_user | Resolve the Jira user key (JIRAUSER10000) that owner / executedBy / assignedTo require |
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.
| Tool | What it does |
|---|---|
update_test_run | Rename 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_run | Append cases to an existing cycle in place; the key and the existing results survive |
remove_test_cases_from_run | Remove items from an existing cycle in place — their whole execution history dies with them |
reorder_test_run_items | Reorder the items of an existing cycle; a cycle already in the requested order makes no write at all |
link_issues_to_test_run | Link 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_plan | Associate an existing cycle with a test plan after creation (the public API accepts testPlanKey only at creation) |
delete_test_results | Delete individual executions by numeric id. The last execution of an item cannot be deleted |
update_test_result_by_id | Edit any execution in an item's history, including older ones; status is resolved from its case-sensitive name to the internal id |
get_folder_tree | The 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_options | The 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_definitions | Custom field definitions per entity type: names, types, required flags, options |
delete_folder | Delete 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 internalupdate_test_run/add_test_cases_to_run/remove_test_cases_from_run(same key). - Folders are never created implicitly.
create_test_case,create_test_runandcreate_test_planfail with 400 on an unknown folder. Folders also cannot be listed through the public API, and renaming needs the numeric id returned bycreate_folder— orget_folder_treewith the internal API enabled. owner/executedBy/assignedTotake a Jira user key (JIRAUSER10000), never a username or an e-mail.find_jira_userresolves it.- TQL is strict: spaces around operators are mandatory, string values go in double quotes,
ANDis the only connector (noOR), and folder paths start with/. Test runs are searchable byprojectKeyandfolderonly. For single/multi-choice custom fields=does not work — useIN. - Statuses, priorities and environments are case-sensitive internal names. Built-in statuses are shown localized in the UI (the label you see for
Draftmay be a translation) while custom ones use their literal names —get_status_optionsshows 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/Butlines. Text wrapped inFeature:/Scenario:is rejected with400 Invalid BDD Script; the wrapper is generated on export. STEP_BY_STEPsteps are synchronized by id onPUT: 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 fromget_test_case— or letadd_test_stepsdo the read-merge-write.- Deprecated fields are intentionally not accepted: use
issueLinksinstead ofissueKey,actualEndDateinstead ofexecutionDate,executedByinstead ofuserKey.
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
-Cprefix (PROJ-C34) instead of-R. Every run-key parameter takes the key as-is, so pass whatever your instance shows. GET /testrun/{key}/testresults/pagemay not exist.get_test_run_resultsandget_test_run_summaryfall back to the deprecated flat endpoint and paginate client-side, adding anoteto the response. A run that genuinely does not exist still surfaces as a 404.- An overall
statussent together withscriptResultsis ignored. Send the step results first, then set the overall status withupdate_last_test_result. POST /testcase/link-issuesmay answer 500. Link throughupdate_test_casewithissueLinksinstead.POST /testcase/bulkmay answer HTTP 500 with an empty body for any payload while single creation works.create_test_cases_bulkfalls back to creating the cases one by one — on any5xx, and on a JSON404(which means "no such endpoint on this build", unlike the HTML404Jira serves when the plugin is absent) — and reports which ones succeeded (#1). Every other4xxis 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 asexecutionTimeare rejected withInvalid Custom Format JSON file. Cucumber JSON reports work as-is when the scenario carries@TestCaseKey=PROJ-T1. download_feature_filesrequires thetqlquery parameter — the API rejects the call without it.- The last execution of a run item cannot be deleted.
delete_test_resultsrejects 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 # 1181 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
Reviews
No reviews yet
Be the first to review this server!
More Developer Tools MCP Servers
Git
Freeby Modelcontextprotocol · Developer Tools
Read, search, and manipulate Git repositories programmatically
Fetch
Freeby Modelcontextprotocol · Developer Tools
Web content fetching and conversion for efficient LLM usage
Toleno
Freeby Toleno · Developer Tools
Toleno Network MCP Server — Manage your Toleno mining account with Claude AI using natural language.
mcp-creator-python
Freeby mcp-marketplace · Developer Tools
Create, build, and publish Python MCP servers to PyPI — conversationally.
MCP Marketplace
Freeby mcp-marketplace · Developer Tools
Search and install MCP servers from inside your AI client.
MarkItDown
Freeby Microsoft · Content & Media
Convert files (PDF, Word, Excel, images, audio) to Markdown for LLM consumption
