mcp-testops
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| TESTOPS_TOKEN | Yes | API token for Allure TestOps. Required: the server refuses to start without it (fails with `TESTOPS_TOKEN is not set`). | |
| MCP_SERVER_NAME | No | Name of the server as it appears in the client. | testops |
| TESTOPS_TIMEOUT | No | Request timeout in seconds. | 60 |
| TESTOPS_BASE_URL | No | Host (base URL) of the TestOps instance. | https://testops.net |
| TESTOPS_READ_ONLY | No | When `true`, only read-only tools are registered (17 of 30); write tools are hidden. | false |
| TESTOPS_PROJECT_ID | No | Default project ID used by tools that are called without an explicit `project_id`. | |
| TESTOPS_VERIFY_SSL | No | TLS certificate verification. Defaults to `false` (verification disabled) for internal stands with a self-signed CA; set to `true` for a valid certificate. | false |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server publishes no instructions, or was last inspected before Glama recorded them.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": false
} |
| prompts | {
"listChanged": false
} |
| resources | {
"subscribe": false,
"listChanged": false
} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| list_test_casesA | List test cases of a project as rows: id, name, status, automated, flaky, testLayer. Rows carry no description and no steps - read one case with get_test_case and its steps with get_test_case_steps. project_id defaults to TESTOPS_PROJECT_ID. sort: "property,ASC|DESC", e.g. "createdDate,DESC". |
| get_test_caseA | Get one test case by id: texts, status, workflow, testLayer, tags, links. The answer does NOT contain the scenario/steps (TestCaseDto has none) and says so with "steps_not_included": true - read them with get_test_case_steps. |
| get_test_case_stepsA | Read the scenario (steps) of a test case. as_tree=true (default) returns nested steps: {id, body, expectedResult, attachmentId, sharedStepId, children}. as_tree=false returns the raw scenarioSteps map by id. A shared step appears as a node with sharedStepId only - its own steps are not inlined. Attachment contents are not inlined either. |
| get_test_case_historyA | Which launches ran a test case, and with what result. Answers "where was this case run": each row is {testResultId, launchId, launchName, status, start, stop, duration, message, testedBy}. launch_id narrows to one launch, search narrows by result text. project_id defaults to TESTOPS_PROJECT_ID. For the full result of one row use get_test_result(testResultId). |
| search_test_casesB | Search test cases with AQL (the API calls it rql) and return full cases. Give either rql or name. name becomes the AQL query name ~ "" on the server, nothing is filtered client-side. Examples (syntax to be verified on the stand): name ~ "login", status.name = "Draft", tag.name = "smoke". Returns {content: [case fields], rql, totalElements, totalPages, number}. |
| validate_aqlA | Check an AQL query (the API calls it rql) before running search_test_cases. Returns {ok, valid, count}: count is how many test cases the query selects, so a typo is caught before a real search. project_id defaults to TESTOPS_PROJECT_ID; deleted=true counts removed cases too. rql is required. |
| create_test_caseA | Create a test case and return {ok, id, name, projectId}. name is required, project_id defaults to TESTOPS_PROJECT_ID. JSON-string arguments: tags_json [{"id": 3, "name": "smoke"}] (the API requires the tag id) links_json [{"name": "JIRA", "type": "issue", "url": "https://jira/browse/ABC-1"}] members_json [{"id": 7, "name": "ivanov", "role": {"id": 1, "name": "MEMBER"}}] (role is an object RoleDto, not a plain string) custom_fields_json [{"customField": {"id": 12, "name": "Severity"}, "name": "high"}] (the API requires both id and name of the custom field) scenario_json {"steps": [{"type": "body", "body": "Open the form"}]} Ids for status_id, workflow_id and test_layer_id come from list_statuses and list_reference. More steps are added with add_test_case_step. |
| update_test_caseA | Update fields of a test case; only the arguments you pass are sent. An empty string means "leave the field alone" unless clear=true, where an empty string clears the field. JSON-string arguments are the same as in create_test_case. |
| delete_test_caseA | Delete a test case; the API answers 202 and moves it to the trash. Without force a case that has run results may stay; force=true deletes it anyway, together with its scenario. Restoring a case is not exposed as a tool. |
| add_test_case_stepA | Add a step to a test case scenario and return {createdStepId, steps_count}. Exactly one of body (step text), attachment_id, shared_step_id is required. parent_step_id makes the new step a child of an existing step. |
| list_launchesB | List launches of a project: id, name, closed, createdDate, statistic. statistic holds status counts as the API returns them, e.g. {passed, failed, broken, skipped, unknown} or StatisticDto with inProgress, pending and total; it is absent when the launch shape carries none. The API answers with two different launch shapes; both are read into this summary. project_id defaults to TESTOPS_PROJECT_ID; sort defaults to "created_date,DESC". |
| get_launchC | Get one launch: id, name, projectId, closed, createdDate, autoclose, releaseId, tags, links. |
| get_launch_statisticA | Status counts of a launch as {passed: n, failed: n, broken: n, skipped: n, ...}. Cheaper than listing the results: one request instead of paging through them. |
| list_test_resultsC | List results of a launch: id, name, testCaseId, status, duration, message, trace, start, stop. status is one of failed, broken, passed, skipped, unknown, in_progress. |
| get_test_resultA | Get one test result: status, duration, message, trace and the text fields. The *Html duplicates are dropped. For the small table of a whole launch use list_test_results or get_launch_statistic. |
| create_launchB | Create a launch and return {ok, id, name, projectId}. tags_json: [{"id": 3, "name": "regress"}]; links_json: [{"name": "JIRA", "type": "issue", "url": "https://jira/browse/ABC-1"}]. |
| close_launchB | Close a launch; the API answers 204. Results are read-only afterwards. |
| reopen_launchB | Reopen a closed launch so results can be added again; the API answers 204. |
| delete_launchB | Delete a launch together with all its results; the API answers 204. |
| add_test_resultA | Record a test result in a launch and return {ok, id, name, launchId}. status must be one of failed, broken, passed, skipped, unknown, in_progress; any other value fails here, without a request. duration, start and stop are milliseconds (int64). test_case_id links the result to a test case. JSON-string arguments: tags_json [{"id": 3, "name": "smoke"}] links_json [{"name": "JIRA", "type": "issue", "url": "https://jira/browse/ABC-1"}] custom_fields_json [{"customField": {"id": 12, "name": "Severity"}, "name": "high"}] |
| run_test_casesA | Create a launch with one manual result per selected test case. Select the cases with one of: ids (comma separated or JSON array of tree leaf ids, becomes selection.leafsInclude), leafs_include (explicit JSON array) or filter_id (saved filter); ids and leafs_include are mutually exclusive. The API field is leafsInclude: there is no leafIds and no rql in the selection. assignees: comma separated user logins; tags_json: [{"id": 3, "name": "regress"}]. Returns {ok, launchId, name, projectId}. |
| list_test_plansB | List test plans of a project: id, name, projectId, baseRql, testCasesCount. project_id defaults to TESTOPS_PROJECT_ID; search narrows by name. |
| get_test_planC | Get one test plan: id, name, projectId, baseRql, testCasesCount, treeSelection. |
| create_test_planB | Create a test plan and return {ok, id, name, projectId}. base_rql is the AQL selection of the plan (e.g. tag.name = "smoke"). tree_selection_json: {"leafsInclude": [1001], "inverted": false} - the plan takes TreeSelectionDto, which has no filterId (that one belongs to running cases). |
| update_test_planA | Update fields of a test plan; only the arguments you pass are sent. An empty string means "leave the field alone" unless clear=true, where an empty string clears the field. tree_selection_json: {"leafsInclude": [1001], "inverted": false}. |
| delete_test_planA | Delete a test plan; the API answers 204. The test cases themselves stay. |
| list_projectsA | List TestOps projects, ordered by name. search: optional substring of the project name; favorite: only starred projects. page starts at 0, size is capped at 100. Returns {content, totalElements, totalPages, number} with id, name, abbr, isPublic, isVersioned, launchCount, automationPercent per project. |
| get_projectC | Get one project by id: id, name, abbr, isPublic, isVersioned, launchCount, automationPercent. |
| list_statusesA | List test statuses (id, name, color) - they give status_id for create_test_case. workflow_id narrows the answer to one workflow. The endpoint takes no project id: statuses are defined by workflows, not by projects. |
| list_referenceA | List a reference dictionary by kind: workflow, testlayer, custom_field or tag. Returns id and name of every entry - they are what create_test_case wants in workflow_id, test_layer_id and custom_fields_json. custom_field is project scoped (project_id defaults to TESTOPS_PROJECT_ID) and accepts query; tag is a flat list without pagination, so query is applied here. |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
TDQS
Scored across 30 tools
The surface is large but the descriptions actively separate near-pairs: list_test_cases vs search_test_cases (AQL), get_test_case vs get_test_case_steps, list_launches vs get_launch_statistic vs list_test_results, and run_test_cases vs create_launch+add_test_result. Remaining overlaps (get_test_case_history vs list_test_results for result data, list_test_plans vs get_test_plan) are documented but still require the agent to read carefully.
Uniform snake_case verb_noun pattern throughout: get_/list_/create_/update_/delete_/add_/search_/validate_/run_/close_/reopen_ plus a single noun. Even the unusual composite (run_test_cases) fits the same grammar, so intent is predictable from the name alone.
30 tools is above the comfortable range and the surface spans five sub-domains (cases, steps, plans, launches, results, reference data), plus helper tools like validate_aql. Each tool maps to a real API operation, so it is not padding, but the breadth makes the set heavy to navigate.
CRUD is complete for test cases, plans and launches, but notable gaps exist around the sub-resources: steps can only be added (no update/delete/reorder step), test results can be created but not updated or deleted, and restore-from-trash is explicitly absent. Shared steps and attachments are referenced by id but have no listing tools, which will force dead ends.