{"name":"io.github.vilaabo/zephyr-scale-mcp","slug":"vilaabo-zephyr-scale-mcp","title":null,"description":"Zephyr Scale on self-hosted Jira Server/DC: test cases, cycles, executions, plans and attachments","url":"https://mcp.market/server/vilaabo-zephyr-scale-mcp","rating":null,"grade":"B","score":78,"certified":false,"status":"active","category":"devtools","tags":["devtools"],"presence":{"score":18,"stars":0,"forks":0,"downloads_week":45,"last_push_at":"2026-07-31T20:07:39.000Z","license":"MIT"},"uptime":null,"claimed":false,"transport":"npm","callable_via_gateway":false,"default_price_micros":0,"repository":"https://github.com/vilaabo/zephyr-scale-mcp","website":null,"version":"1.0.0","remotes":[],"packages":[{"registryType":"npm","registryBaseUrl":"https://registry.npmjs.org","identifier":"zephyr-scale-mcp","version":"1.0.0","transport":{"type":"stdio"},"environmentVariables":[{"description":"Jira base URL without a trailing slash, e.g. https://jira.example.com","isRequired":true,"name":"JIRA_BASE_URL"},{"description":"Jira Data Center Personal Access Token (used when JIRA_AUTH=pat, the default)","isSecret":true,"name":"JIRA_PAT"},{"description":"Authentication mode: pat (default) or basic","name":"JIRA_AUTH"},{"description":"Username for JIRA_AUTH=basic","name":"JIRA_USERNAME"},{"description":"Password for JIRA_AUTH=basic","isSecret":true,"name":"JIRA_PASSWORD"},{"description":"Project key used when a tool is called without projectKey","name":"ZEPHYR_DEFAULT_PROJECT_KEY"},{"description":"true registers 11 UNOFFICIAL tools backed by the internal /rest/tests/1.0 API the vendor does not support: editing a test cycle in place (rename/move, add/remove/reorder its cases), editing or DELETING individual executions, linking a cycle to a test plan, the folder tree, folder deletion, exact status names and custom field definitions","name":"ZEPHYR_ALLOW_INTERNAL_API"},{"description":"true disables all write tools","name":"ZEPHYR_READONLY"},{"description":"false allows self-signed certificates (disables TLS verification)","name":"JIRA_TLS_REJECT_UNAUTHORIZED"},{"description":"Timeout of a single HTTP request in milliseconds (default 30000)","name":"JIRA_TIMEOUT_MS"},{"description":"Retries for GET requests and for 429/503 responses, honoring Retry-After (default 2)","name":"JIRA_MAX_RETRIES"},{"description":"Log verbosity: debug | info | warn | error (default info; all logs go to stderr)","name":"ZEPHYR_LOG_LEVEL"}]}],"tools":[{"name":"add_test_cases_to_run","description":"… Append test cases to an EXISTING test run (cycle) IN PLACE (GET /testrun/{key} and GET /testcase/{key} to resolve numeric ids, then PUT /testrunitem/bulk/save) \\u2014 the run keeps its key and the existing items keep their execution results. The public API cannot do this at all; its only workaround, recreate_test_run_with_items, changes the key. New items are appended after the existing ones in ","write_action":true,"price_micros":0,"input_schema":null},{"name":"add_test_steps","description":"Insert steps into a STEP_BY_STEP script without losing the existing ones (GET then PUT /testcase/{testCaseKey}): reads the current steps, keeps their ids, splices the new ones in and writes the whole list back \\u2014 needed because PUT deletes every step missing from the list it receives. The stored steps are ordered by their authoritative `index` before merging, because GET /testcase serves them ","write_action":true,"price_micros":0,"input_schema":null},{"name":"clone_test_case","description":"Copy a test case inside its own project (GET /testcase/{testCaseKey}, then POST /testcase). Copies name, objective, precondition, folder, status, priority, component, owner, estimatedTime, labels, custom fields, parameters and \\u2014 unless includeScript is false \\u2014 the script; step ids are dropped so the copy owns its steps. Issue links, attachments and execution history are NOT copied. name ","write_action":false,"price_micros":0,"input_schema":null},{"name":"create_environment","description":"Create a Zephyr Scale environment in a project (POST /environments). The name must be unique within the project \\u2014 a duplicate is rejected with 400 \\u2014 and is the string other tools use to reference the environment, so create it with the exact casing you intend to pass to test results. Returns the created environment object as the API sends it.","write_action":true,"price_micros":0,"input_schema":null},{"name":"create_folder","description":"Create a folder for test cases, test plans or test runs / test cycles (POST /folder). name is the FULL path from the root, not a single segment, and every segment must be non-empty and not blank \\u2014 \"/\" alone, \"/A//B\", a trailing \"/\" and a whitespace-only segment (\"/A/ \") are rejected before any HTTP call because the API would create a permanently nameless folder from them. Spaces around a real","write_action":true,"price_micros":0,"input_schema":null},{"name":"create_test_case","description":"Create a test case (POST /testcase). … status, priority, component and custom field names must match the ones configured on the instance and are case-sensitive: an unknown or wrong-case value is rejected with 400 and nothing is created (unlike EXECUTION statuses, which the API silently ignores). owner: … estimatedTime is in milliseconds. name is limited to … characters \\u2014 a longer one is refus","write_action":true,"price_micros":0,"input_schema":null},{"name":"create_test_cases_bulk","description":"Create several test cases in one request (POST /testcase/bulk). Each item takes the same fields as create_test_case; an item without its own projectKey uses the shared projectKey, then ZEPHYR_DEFAULT_PROJECT_KEY. … Some Server builds ship a broken bulk endpoint (any 5xx, or a JSON 404 \\u2014 as opposed to the plugin-not-installed HTML 404) while single creation works: the tool then falls back to P","write_action":true,"price_micros":0,"input_schema":null},{"name":"create_test_plan","description":"Create a test plan (POST /testplan). … Returns { key } (e.g. \"PROJ-P123\") \\u2014 no UI url, because the test plan page has no stable address across Zephyr Scale versions.","write_action":true,"price_micros":0,"input_schema":null},{"name":"create_test_result","description":"Record a NEW execution of a run item (POST /testrun/{runKey}/testcase/{caseKey}/testresult). Appends to the execution history of that item \\u2014 to amend the newest execution instead, use update_last_test_result. Only the fields you pass are sent, and the execution keeps the project default for everything you omit. … … … … Returns { id } of the created execution.","write_action":true,"price_micros":0,"input_schema":null},{"name":"create_test_results_bulk","description":"Record NEW executions for several items of ONE test run in a single call (POST /testrun/{runKey}/testresults). The body is the `results` array itself; in each entry only the fields you pass are sent, and the returned ids are positionally aligned with it. … … … matchEnvironment / matchUserKey apply to the WHOLE batch and only SELECT which existing run item to append to \\u2014 they never set the cre","write_action":true,"price_micros":0,"input_schema":null},{"name":"create_test_run","description":"Create a test run / test cycle (POST /testrun). Pass the COMPLETE list of test cases in `items` now \\u2014 … Each item may also carry its execution result (status, executedBy, executionTime, actual dates, per-step scriptResults, \\u2026), which imports a run together with its results in one call; afterwards use the test result tools. A run folder is of type TEST_RUN. … … The run's own `status` is d","write_action":true,"price_micros":0,"input_schema":null},{"name":"delete_attachment","description":"Permanently delete one attachment by its numeric id (DELETE /attachments/{id}). Irreversible, and there is no bulk form \\u2014 one call per attachment. Ids come from list_attachments or from an upload_attachment response; the entity the attachment belongs to is not needed (ids are global, not per-entity). The DELETE itself answers 2xx even for an id that never existed or was already deleted, so th","write_action":true,"price_micros":0,"input_schema":null},{"name":"delete_folder","description":"… Permanently delete a Zephyr Scale folder by its numeric id (DELETE /folder/{id}) \\u2014 the public API v1 cannot delete folders at all. CANNOT BE UNDONE. CAUTION: what happens to a NON-empty folder is version-specific (its contents may be deleted or orphaned). Verified live on the reference build: CHILD FOLDERS are deleted with it (the whole subtree goes, and their ids then answer 404), while th","write_action":true,"price_micros":0,"input_schema":null},{"name":"delete_test_case","description":"Permanently delete a test case (DELETE /testcase/{testCaseKey}). Irreversible: the case, its script and its execution history cannot be restored through the API. Inbound references are NOT checked: a case that other cases invoke as a \"Call to Test\" step is deleted anyway and those steps keep pointing at a key that no longer resolves. Returns { deleted: true, key }.","write_action":true,"price_micros":0,"input_schema":null},{"name":"delete_test_plan","description":"Permanently delete a test plan (DELETE /testplan/{testPlanKey}). Irreversible \\u2014 there is no trash and no undo. NOT idempotent and it never confirms a deletion it did not perform: an unknown, mistyped or already-deleted key answers 404 and this tool fails instead of returning { deleted: true } (verified live). The key is not reused afterwards \\u2014 the next create_test_plan gets a fresh numbe","write_action":true,"price_micros":0,"input_schema":null},{"name":"delete_test_results","description":"… Permanently delete individual test executions (results) by their numeric ids (POST /testresult/bulk/delete with a bare JSON array of ids) \\u2014 the public API v1 cannot delete results at all, they normally die only together with their run. CANNOT BE UNDONE. CONSTRAINT: the LAST execution of a run item cannot be deleted \\u2014 the API rejects it with 'Test execution is the lastTestResult and can","write_action":true,"price_micros":0,"input_schema":null},{"name":"delete_test_run","description":"Permanently delete a test run / test cycle together with ALL its execution results, its attachments and its test-plan links (DELETE /testrun/{key}). Cannot be undone. … So delete + create_test_run with the full desired `items` is the public way to change a run's name, folder or composition (the new run gets a NEW key). The DELETE itself answers 2xx for a key that never existed, was already deleted","write_action":true,"price_micros":0,"input_schema":null},{"name":"download_attachment","description":"Download the content of an attachment to a local file (GET /rest/tests/1.0/attachment/{id}). Address it by attachmentId or by the exact url list_attachments returns \\u2014 pass exactly one of the two. Attachment content lives outside API v1: /rest/tests/1.0/attachment/{id} is the url the official list endpoint itself hands out, so this tool follows it without requiring ZEPHYR_ALLOW_INTERNAL_API. A","write_action":false,"price_micros":0,"input_schema":null},{"name":"download_feature_files","description":"Export BDD test cases as Gherkin .feature files packed in a ZIP archive (GET /automation/testcases). tql is REQUIRED \\u2014 the API rejects the call without it \\u2014 and this endpoint uses the testCase.-prefixed TQL dialect, which supports ONLY the fields testCase.key, testCase.projectKey and testCase.name (= and IN) joined by AND: testCase.folder, testCase.status, testCase.priority and testCase.","write_action":false,"price_micros":0,"input_schema":null},{"name":"find_jira_user","description":"Search Jira users (GET /rest/api/2/user/search). Use it to resolve the Jira USER KEY (e.g. \"JIRAUSER10000\") that the owner / executedBy / assignedTo fields of the other tools require \\u2014 those fields reject usernames and e-mail addresses. Needs the Jira \"Browse users\" permission, otherwise Jira answers 403. Returns an array of { key, name, displayName, emailAddress }, empty when nothing matches","write_action":false,"price_micros":0,"input_schema":null},{"name":"get_custom_field_definitions","description":"… List the custom field DEFINITIONS of a project \\u2014 names, types, required flags and allowed options (GET /rest/api/2/project/{projectKey}, then GET /project/{id}/customfields/{testcase|testplan|testrun}) \\u2014 so the customFields parameter of the create/update tools can be filled with valid keys and values. The public API v1 has no such endpoint. Custom fields are defined per entity type, an","write_action":false,"price_micros":0,"input_schema":null},{"name":"get_folder_tree","description":"… List the complete folder tree of a project (GET /rest/api/2/project/{projectKey} for the numeric project id, then GET /project/{id}/foldertree/{testcase|testplan|testrun}) \\u2014 the public API v1 cannot list folders at all. Each entity type has its OWN tree: test case folders are not test run folders, so a path that exists for one entity type may not exist for another. entity is OPTIONAL and de","write_action":false,"price_micros":0,"input_schema":null},{"name":"get_issue_test_coverage","description":"Traceability report for a Jira issue: every linked test case with its latest execution (GET /issuelink/{issueKey}/testcases, then GET /testcase/{key} and GET /testcase/{key}/testresult/latest per case). Costs up to 2 requests per case, so maxCases (integer 1..200, default …) caps the volume. totalLinked counts LINKS \\u2014 the API returns one entry per link, so a case linked to the issue twice is ","write_action":false,"price_micros":0,"input_schema":null},{"name":"get_latest_result_for_test_case","description":"Read ONE execution of a test case across ALL test runs (GET /testcase/{key}/testresult/latest). WHICH one is the API's choice and it is not the plain \"newest\": measured live with three executions of one case, it returned the most recently CREATED one (highest id) even though another carried a later execution date, and back-dating the winner did not dislodge it \\u2014 so neither \"latest by date\" no","write_action":false,"price_micros":0,"input_schema":null},{"name":"get_status_options","description":"… List the EXACT internal names of a project's execution statuses, test case statuses or test case priorities (GET /rest/api/2/project/{projectKey}, then GET /project/{id}/testresultstatus | testcasestatus | testcasepriority, falling back to GET /{segment}?projectId={id} on older builds). The public API v1 has no such endpoint, so this is the only way to discover the values create_test_result, upd","write_action":false,"price_micros":0,"input_schema":null},{"name":"get_test_case","description":"Read one test case (GET /testcase/{testCaseKey}). A STEP_BY_STEP script comes back with a numeric id on every step; those ids are what update_test_case and set_test_script match on, so read them before editing steps by hand (add_test_steps does it for you). Returns the test case object as the API sends it, restricted to fields when given.","write_action":false,"price_micros":0,"input_schema":null},{"name":"get_test_cases_linked_to_issue","description":"List the test cases linked to a Jira issue (GET /issuelink/{issueKey}/testcases) \\u2014 traceability from a requirement or bug to its tests. Create such links with link_issues_to_test_cases, or with the issueLinks field of create_test_case. Use get_issue_test_coverage instead to also see the latest execution of each case. Returns the API array of test case objects, one entry per LINK: a case linke","write_action":false,"price_micros":0,"input_schema":null},{"name":"get_test_plan","description":"Read a test plan by key (GET /testplan/{testPlanKey}). The payload embeds the linked test runs and Jira issues when the plan has any; narrow it with fields. Returns the test plan object as the API sends it.","write_action":false,"price_micros":0,"input_schema":null},{"name":"get_test_run","description":"Read one test run / test cycle including its items (GET /testrun/{key}). … Use get_test_run_results for the executions of the run and get_test_run_summary for status counts. items[].id is the id of that item's LATEST EXECUTION, not a stable item identifier: it changes with every new result, and it is a different id space from remove_test_cases_from_run's removedItemIds. Returns the run object as t","write_action":true,"price_micros":0,"input_schema":null},{"name":"get_test_run_results","description":"Page through the execution results of a test run / test cycle (GET /testrun/{key}/testresults/page). An item can have several executions; onlyLastExecutions=true keeps only the most recent one per item, so it never returns more values than the run has items. Creating a run already seeds one 'Not Executed' execution per item (that execution IS the item's last one), so with onlyLastExecutions=false ","write_action":true,"price_micros":0,"input_schema":null},{"name":"get_test_run_summary","description":"Aggregated execution summary of a test run / test cycle: composite read-only call of GET /testrun/{key} plus every page of its results (with the flat-endpoint fallback of get_test_run_results). Counts the LAST execution of each item \\u2014 the run object names them, so latestResults and executed never exceed itemCount \\u2014 grouping by status name verbatim in `byStatus`; nothing is normalized. … ","write_action":true,"price_micros":0,"input_schema":null},{"name":"health_check","description":"Verify connectivity and credentials (GET /rest/api/2/myself) and, when ZEPHYR_DEFAULT_PROJECT_KEY is configured, whether the Zephyr Scale plugin answers on /rest/atm/1.0 (GET /environments). Any JSON error from the plugin \\u2014 including 403 for a project without Zephyr \\u2014 still counts as reachable; only Jira's generic HTML 404 page or a network failure counts as unreachable. The tool itself ","write_action":false,"price_micros":0,"input_schema":null},{"name":"link_issues_to_test_cases","description":"Link Jira issues to test cases in bulk (POST /testcase/link-issues). One entry links one test case to one issue; repeat a testCaseKey across entries to link it to several issues. At most … UNIQUE test case keys per call \\u2014 checked locally, before any request. Additive: it only creates links, never removes existing ones. KNOWN ISSUE: on many Server/DC builds this endpoint answers HTTP 500 with ","write_action":false,"price_micros":0,"input_schema":null},{"name":"link_issues_to_test_run","description":"… Link Jira issues to an EXISTING test run (cycle) \\u2014 the traceability the public API cannot express at all: POST /testrun rejects an issueLinks field outright (HTTP 500 \"Unrecognized field 'issueLinks' (TestRunDTO)\"), so this endpoint is the only way. Resolves the run id (GET /testrun/{key}), each issue id (GET /rest/api/2/issue/{key}) and the default link type (GET /tracelinktype/default/tes","write_action":true,"price_micros":0,"input_schema":null},{"name":"link_test_run_to_plan","description":"… Associate an EXISTING test run (cycle) with a test plan after creation (GET /testrun/{key} and GET /testplan/{key} for the numeric ids, GET /tracelinktype/default/testrun for the link type, then POST /tracelink/bulk/create with a flat [{ testRunId, testPlanId, typeId }] triple). The public API only accepts testPlanKey while the run is being created. The link is one-directional bookkeeping: the r","write_action":true,"price_micros":0,"input_schema":null},{"name":"list_attachments","description":"List the attachments of a test case, test run (cycle), test result, or of one step of a case or result (GET /testcase/{key}[/step/{i}]/attachments, /testrun/{key}/attachments, /testresult/{id}[/step/{i}]/attachments). … These endpoints take no pagination and no fields projection \\u2014 the full list always comes back. Step attachments are aggregated ASYMMETRICALLY (verified live): the test-case li","write_action":false,"price_micros":0,"input_schema":null},{"name":"list_environments","description":"List the Zephyr Scale environments of a project (GET /environments?projectKey=\\u2026). Environments are per-project and are referenced BY NAME (case-sensitive) in test run items and test results, so use this to get the exact spelling. Returns the raw array of environment objects ([{ id, name, description }]); an empty array means the project defines none.","write_action":false,"price_micros":0,"input_schema":null},{"name":"move_test_cases_to_folder","description":"Move test cases into another folder \\u2014 one partial PUT /testcase/{key} per case that changes only the folder field. Select the cases with EITHER testCaseKeys OR fromFolder (resolved by GET /testcase/search on folder = \"<fromFolder>\"): exactly one of the two, checked before any request. fromFolder matches that folder EXACTLY \\u2014 cases in its subfolders are not included, and an existing but e","write_action":false,"price_micros":0,"input_schema":null},{"name":"recreate_test_run_with_items","description":"Recreate a test run (cycle) under a NEW key with a changed item list, name or folder (GET /testrun/{key} + POST /testrun, plus DELETE /testrun/{key} when deleteOriginal=true).","write_action":true,"price_micros":0,"input_schema":null},{"name":"remove_test_cases_from_run","description":"… Remove the items of the given test cases from an EXISTING test run (cycle) IN PLACE (GET /testrun/{key}, GET /testcase/{key}, GET /testrun/{id}/testrunitems, then PUT /testrunitem/bulk/save with deletedTestRunItems) \\u2014 the run keeps its key and the surviving items keep their results. The removed items are deleted TOGETHER WITH their whole execution history and this CANNOT BE UNDONE. When a t","write_action":true,"price_micros":0,"input_schema":null},{"name":"rename_folder","description":"Rename a folder and/or set its custom fields (PUT /folder/{folderId}). name replaces the name of that ONE folder segment \\u2014 it is not a path, so it cannot move the folder to another parent, and an empty or whitespace-only name, or \"/\" or \"\\\" in it, is rejected before any HTTP call. The rename changes the full path of this folder and of every folder below it, so paths held elsewhere (the folder","write_action":false,"price_micros":0,"input_schema":null},{"name":"reorder_test_run_items","description":"… Reorder the items of an existing test run (cycle) IN PLACE (GET /testrun/{key}, GET /testrun/{id}/testrunitems, then PUT /testrunitem/bulk/save with updatedTestRunItemsIndexes) \\u2014 the run keeps its key and every item keeps its results. Items whose test case is not listed in `order` keep their relative order AFTER the listed ones; a case present in the run several times has its mentions in `o","write_action":true,"price_micros":0,"input_schema":null},{"name":"search_test_cases","description":"Search test cases with a TQL query (GET /testcase/search). A query longer than … characters is sent as POST /testcase/search instead, which supports ONLY the fields projectKey, key and name and at most … values per IN list. That POST endpoint is missing or broken on some Zephyr Scale Server builds, so prefer staying under … characters (split a long IN list across calls); when a POST search fails t","write_action":false,"price_micros":0,"input_schema":null},{"name":"search_test_plans","description":"Search test plans with a TQL query (GET /testplan/search). For test plans the searchable fields include projectKey, folder, name, status, key, owner and labels (verified live) \\u2014 the exact set varies by Zephyr Scale version, and an unsupported field fails with 400 \"Unrecognized field: <name>\". folder matches EXACTLY: plans in a subfolder of the given path are NOT returned. A folder path that d","write_action":false,"price_micros":0,"input_schema":null},{"name":"search_test_runs","description":"Search test runs / test cycles with TQL (GET /testrun/search). For runs TQL accepts ONLY the fields projectKey and folder \\u2014 name, status or dates are NOT searchable, and there is no full-text search; read a candidate run with get_test_run instead. A folder clause matches that folder AND its subfolders: folder = \"/A\" also returns the runs in /A/B. … …","write_action":false,"price_micros":0,"input_schema":null},{"name":"set_test_script","description":"Replace a test case's whole script or change its format (PUT /testcase/{testCaseKey} with a full testScript). DESTRUCTIVE: switching STEP_BY_STEP to PLAIN_TEXT or BDD irreversibly deletes all steps, and a STEP_BY_STEP replacement deletes every stored step whose id is absent from steps. text is required for PLAIN_TEXT and BDD, steps for STEP_BY_STEP \\u2014 the pairing is validated locally, before a","write_action":false,"price_micros":0,"input_schema":null},{"name":"update_last_test_result","description":"Amend the LAST (most recent) execution of a run item (PUT /testrun/{runKey}/testcase/{caseKey}/testresult). The endpoint REPLACES the execution instead of patching it: fields missing from the body are reset (verified live \\u2014 an omitted status falls back to the project default 'Not Executed', executedBy becomes null, actualEndDate/executionDate jump to the server time; only comment, environment","write_action":true,"price_micros":0,"input_schema":null},{"name":"update_test_case","description":"Update a test case (PUT /testcase/{testCaseKey}). PARTIAL: only the fields passed are changed and omitted fields keep their values, so never send empty placeholders. projectKey cannot be changed. issueLinks REPLACES the whole link set instead of adding to it \\u2014 sending issueLinks: [\"PROJ-1\"] to a case already linked to PROJ-2 silently unlinks PROJ-2, so read the current links with get_test_cas","write_action":true,"price_micros":0,"input_schema":null},{"name":"update_test_plan","description":"Update a test plan (PUT /testplan/{testPlanKey}). PARTIAL update: only the fields you pass are written and omitted fields keep their current value, so never send empty placeholders \\u2014 they overwrite real data. projectKey cannot be changed. … Returns { key }.","write_action":true,"price_micros":0,"input_schema":null},{"name":"update_test_result_by_id","description":"… Edit ANY test execution (result) by its numeric id, including OLDER history entries (PUT /testresult with a single-element array) \\u2014 update_last_test_result reaches only the newest execution of an item. Partial update: only the fields passed are changed, and at least one is required. `status` is the case-sensitive status NAME and is resolved to the internal testResultStatusId via GET /rest/a","write_action":true,"price_micros":0,"input_schema":null},{"name":"update_test_run","description":"… Rename a test run (cycle), move it to another folder or change its planned dates IN PLACE (GET /testrun/{key} for the numeric id, then PUT /testrun/{id}, then GET /testrun/{key} again to read the result back) \\u2014 the run keeps its key, items and results. The public API v1 has no PUT /testrun at all. At least one field to change is required. folderId is the NUMERIC id of a TEST_RUN folder (fro","write_action":true,"price_micros":0,"input_schema":null},{"name":"upload_attachment","description":"Attach a local file to a test case, test run (cycle), test result, or to one step of a case or result (POST multipart/form-data /testcase/{key}[/step/{i}]/attachments, /testrun/{key}/attachments, /testresult/{id}[/step/{i}]/attachments). … filePath is read from the disk of the machine running this MCP server, not from the caller. Uploads are not idempotent: calling twice creates two attachments. A","write_action":true,"price_micros":0,"input_schema":null},{"name":"upload_automation_results","description":"Publish automated execution results from a local ZIP archive (POST multipart/form-data /automation/execution/{projectKey}). The archive must hold JSON files in Zephyr's custom results format: {\"version\": 1, \"executions\": [{\"source\", \"result\", \"testCase\": {\"key\"}}]}. Validation is strict only at the TOP level of an execution: an extra sibling of source/result such as executionTime is rejected with ","write_action":true,"price_micros":0,"input_schema":null},{"name":"upload_cucumber_results","description":"Publish Cucumber execution results from a local ZIP archive (POST multipart/form-data /automation/execution/cucumber/{projectKey}). The archive must hold the output of Cucumber's built-in json formatter (one or more .json report files). Every scenario must carry a @TestCaseKey=PROJ-T1 tag naming the BDD test case it reports on \\u2014 that tag is how the server matches a scenario to an existing tes","write_action":true,"price_micros":0,"input_schema":null}],"scan":{"score":78,"grade":"B","scanned_at":"2026-09-24T12:03:04.447Z","report":{"scannerVersion":"0.1.9","scannedAt":"2026-09-24T12:03:04.469Z","components":{"code":{"score":25,"max":25,"notes":["2 source files scanned"]},"reliability":{"score":-1,"max":20,"notes":["no gateway calls yet and no remote to probe"]},"poisoning":{"score":-1,"max":15,"notes":["tools not inspected (local package is not executed); not counted"]},"auth":{"score":6,"max":15,"notes":["static API keys via environment variables"]},"maintenance":{"score":12,"max":15,"notes":["last push 55 days ago"]},"identity":{"score":8,"max":10,"notes":["registry namespace matches repository owner","GitHub account older than a year"]}},"findings":[],"inputs":{"packages":[{"registryType":"npm","identifier":"zephyr-scale-mcp","version":"1.0.0","found":true,"license":"MIT","hasInstallScripts":false,"dependencyCount":2,"publishedAt":"2026-07-31T19:58:40.003Z","repositoryUrl":"git+https://github.com/vilaabo/zephyr-scale-mcp.git","weeklyDownloads":45}],"repo":{"found":true,"owner":"vilaabo","repo":"zephyr-scale-mcp","archived":false,"pushedAt":"2026-07-31T20:07:39Z","stars":0,"forks":0,"openIssues":0,"ownerType":"User","ownerAvatarUrl":"https://avatars.githubusercontent.com/u/135213271?v=4","ownerCreatedAt":"2023-06-01T08:17:30Z","license":"MIT"},"icon":{"url":"https://avatars.githubusercontent.com/u/135213271?v=4&s=128","source":"github"},"presence":{"stars":0,"forks":0,"downloadsWeek":45,"license":"MIT","lastPushAt":"2026-07-31T20:07:39.000Z","score":18}}}},"grade_history":[],"reviews":[]}