Skip to main content
Glama
vilaabo

zephyr-scale-mcp

by vilaabo

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 + 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 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.

  • 1181 unit and contract tests (vitest + msw, no network) plus a gated end-to-end scenario against a real instance.

Related MCP server: mcp-zephyr-scale

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=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

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.

  1. 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).

  2. 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.

  3. owner / executedBy / assignedTo take a Jira user key (JIRAUSER10000), never a username or an e-mail. find_jira_user resolves it.

  4. 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.

  5. 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.

  6. 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.

  7. 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.

  8. 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             # 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

MIT

Install Server
A
license - permissive license
A
quality
A
maintenance

Maintenance

Maintainers
Response time
2dRelease cycle
6Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

View all related MCP servers

Related MCP Connectors

  • MCP server for AI access to SmartBear tools, including BugSnag, Reflect, Swagger, PactFlow, QTM4J.

  • Official MCP server for Qase — manage test cases, runs, suites, defects via AI tools.

  • MCP server for AI agents to plan, verify, and deploy Cloudflare-native apps.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/vilaabo/zephyr-scale-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server