tuskr-mcp-server
Summary: This MCP server lets you read and write Tuskr test management data — projects, test cases, suites, runs and results — over the Tuskr REST API, so an AI client can explore a tenant and record or clean up test execution.
Projects: list projects filtered by name/status, and create new projects (with team members mapped to roles).
Test cases: list cases by suite, section, key, name, type or custom fields (with optional full custom fields or full result history); create single cases; create-or-update via
upsert_test_case(by ID or external ID); bulk import many cases in one call.Test suites: list suites with their embedded sections and names to discover suite/section IDs.
Test runs: list runs by name, key, status, assignee or incompleteness; create runs (all cases or a named subset); copy a run without carrying results; update a run's case selection; lock/unlock, archive, and delete runs in bulk (to recycle bin or purged); bulk-export runs with their cases and latest results.
Results: record one status across one or many cases in a run in a single bulk call, with comments, time spent, assignee, custom fields and up to two file attachments (restricted to
TUSKR_ATTACHMENT_DIR); fetch a run's cases with latest results, filtered by status (e.g. aFAILEDconfirmation-test worklist), with pagination and trimming.Reporting/timeline: derive when testing actually started/finished from per-case result history (first/last execution, span, distinct active days, optional daily breakdown, never-executed cases), and fetch run metadata with
firstResultAt/lastResultAt/durationInMinutes.Access & deployment: access-token authentication, per-request
Authorization/Tenant-IDheaders (multi-tenant over HTTP) or env vars for stdio, HTTP or stdio transport, plus aresource://service_descriptionresource; respects Tuskr's 10 req/s limit by batching where possible.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@tuskr-mcp-server List all test cases in the 'Login' project"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
tuskr-mcp-server
Implements a Model Context Protocol (MCP) server for the Tuskr REST API
Built on the FastMCP Python SDK.
Supports access token authentication.
Tools
Each tool wraps one Tuskr REST endpoint, or two for get_test_run_timeline.
Endpoints are relative to https://api.tuskr.live/api/tenant/<tenant-id>/.
Tool | Method | Endpoint | Purpose |
|
|
| Lists projects, filterable by name and by status ( |
|
|
| Creates a project. |
|
|
| Lists a project's test cases, filterable by test suite, section, key, name, test case type and custom fields. Use it to discover the case keys a project actually contains before recording results. Each row is trimmed to its ID, key, name, suite, section and type IDs and last result status by default; |
|
|
| Lists a project's test suites, each with its embedded sections, filterable by name. Use it to find the suite and section IDs that |
|
|
| Lists a project's test runs, filterable by name, key, status and assignee. |
|
|
| Creates a test case in an existing suite and section. Pass |
|
|
| Creates a test case or updates an existing one. Pass exactly one of |
|
|
| Creates many test cases in one call. Each record uses Tuskr's own keys ( |
|
|
| Creates a test run containing all cases in the project or a named subset. |
|
|
| Copies an existing run and its case selection within the same project. Results are not carried over; every case starts untested. |
|
|
| Records one status against one or many cases in a run, optionally with up to two file attachments (see Attachments). Pass |
|
|
| Fetches the cases in a run with their latest result, filterable by status and by |
|
|
| Exports a project's test runs with their test cases and the latest result per case, in one call. Filterable by run IDs, status, completion status and assignee. Always narrow it with |
|
|
| Reports when testing in a run actually started and finished, derived from each case's |
|
|
| Fetches a run's metadata plus |
|
|
| Locks a run read-only, or unlocks it. One run per call. |
|
|
| Archives one or more runs by ID. The response is a bare boolean, so confirm the new state with |
|
|
| Deletes every run in the given projects that matches the filters: a case-sensitive name prefix and a minimum age in days, counted from the run's creation. Archived runs are included. It cannot target a single run by ID or key. At least one of |
|
|
| Adds test cases to an existing run and removes test cases from it, without losing the results already recorded, and switches a run between all cases and a chosen selection. |
Result status keys such as PASSED and FAILED are configured per tenant, so use
the keys defined in your own account.
add_test_run_results matches a test run given by key or name across the whole
tenant by default, not within one project. When several projects have a run with
the same key, for example R-1, Tuskr rejects the call with Multiple test runs found matching: R-1 and records nothing. Pass project (the project ID) to look
the run up inside that project first: the key is matched exactly, ignoring case,
then the name is matched exactly, and the call fails without recording anything
when no run or several runs match. Passing the run's UUID also avoids the
ambiguity and skips the lookup; list_test_runs returns it for a given project.
Tuskr rate-limits every plan at 10 requests/second. The bulk tools exist for that reason: batch where the endpoint allows it.
Attachments
add_test_run_results accepts an attachments list of local file paths, for
screenshots and logs. Tuskr allows two files of 5 MB each per call and attaches
every file to the result record of every test case in that call, so record cases
that need different evidence in separate calls. Both limits are checked before
the request is sent.
Attachments are off by default. The tool arguments are chosen by the model, so a
plain path would let a prompt injection upload any file the server can read.
Set TUSKR_ATTACHMENT_DIR to the one directory the server may read from. Relative
paths resolve against it, and anything that resolves outside it, including through
a symlink, is rejected. Because the server reads the files itself, this only works
when the server runs on the machine that holds them (stdio, or an HTTP instance
with the directory mounted).
The server also exposes one resource, resource://service_description, which
describes its purpose to the client.
Related MCP server: MCP DevOps Test Server
Installation
Environment variables / .env file
Set up environment variables or configure the .env file using the .env.example template.
The following environment variables are supported:
TUSKR_TENANT_ID=<your tenant id>
TUSKR_ACCESS_TOKEN=<your access token>(this doc desc https://tuskr.app/kb/latest/api)
and optionally
MCP_TRANSPORT=<transport type: http or stdio>
MCP_HOST=<host for HTTP transport>
MCP_PORT=<port for HTTP transport>
TUSKR_ATTACHMENT_DIR=<directory attachments may be read from>
TUSKR_BASE_URL=<Tuskr API base URL, default https://api.tuskr.live/api/tenant/>TUSKR_BASE_URL only needs changing to point the server at a different Tuskr
API host, such as a mock in tests. Keep the trailing slash.
In HTTP mode the Authorization and Tenant-ID headers of each request take
precedence over TUSKR_ACCESS_TOKEN and TUSKR_TENANT_ID, so one server can
serve several users with their own tokens.
Command Line Parameters
The MCP server supports the following command line parameters:
--transport: Transport type for the MCP server. Options:http(default) orstdio--host: Host address for HTTP transport (default:0.0.0.0)--port: Port number for HTTP transport (default:8000)
Note: The --host and --port parameters are only applicable when using the http transport.
Note: The default host 0.0.0.0 listens on every network interface, which
is what a container needs. When you run the server directly on a workstation,
pass --host 127.0.0.1 (or set MCP_HOST) so it is reachable only locally.
Default Values
Transport:
http(can be overridden withMCP_TRANSPORTenvironment variable)Host:
0.0.0.0(can be overridden withMCP_HOSTenvironment variable)Port:
8000(can be overridden withMCP_PORTenvironment variable)
Connect from client
HTTP Transport (Default)
Use the following template to connect the server via HTTP:
{
"mcpServers": {
"tuskr": {
"transport": "http",
"url": "http://<your-mcp-dns-or-ip>/mcp/",
"headers": {
"Authorization": "Bearer <your access token>",
"Tenant-ID": "<your-tuskr-tenant-id>"
}
}
}
}The Authorization is mandatory.
The Tenant-ID is not required and can be set on the server side using the TUSKR_TENANT_ID env variable. It's convenient in case you have a single MCP Server for your organization.
Migrating from TUSKR_ACCOUNT_ID
Earlier versions of this server used the env var TUSKR_ACCOUNT_ID and the HTTP header
Account-ID. These continue to work, but emit a deprecation warning. Tuskr's own
documentation and UI consistently use the term "Tenant ID" (it is part of the REST URL
path: /api/tenant/<tenant-id>/), so the preferred names are now TUSKR_TENANT_ID and
the Tenant-ID HTTP header. Both names will be supported until a future major version
removes the legacy names.
To migrate an existing config, replace TUSKR_ACCOUNT_ID with TUSKR_TENANT_ID; no
other changes are required.
stdio Transport (for local development)
For local development and integration with tools like uvx, use the stdio transport:
{
"mcpServers": {
"tuskr": {
"transport": "stdio",
"command": "uvx",
"args": ["tuskr-mcp-server", "--transport", "stdio"]
}
}
}or use uv with source code:
{
"mcpServers": {
"tuskr": {
"transport": "stdio",
"command": "uv",
"args": [
"--directory",
"/path/to/your/tuskr-mcp-server",
"run",
"src/main.py",
"--transport",
"stdio"
]
}
}
}Integrate with Claude
Worked examples for Claude Code and the Claude desktop app. Both name the server
tuskr, so its tools appear as list_projects, list_test_runs and so on.
Claude Code
Run the server locally over stdio, with credentials passed as environment variables:
claude mcp add --transport stdio tuskr \
--env TUSKR_TENANT_ID=<your tenant id> \
--env TUSKR_ACCESS_TOKEN=<your access token> \
-- uvx tuskr-mcp-server --transport stdioEverything after -- is the command Claude Code runs to start the server, kept
separate from Claude Code's own flags.
Or connect to a shared instance over HTTP:
claude mcp add --transport http tuskr https://<your-mcp-dns-or-ip>/mcp/ \
--header "Authorization: Bearer <your access token>" \
--header "Tenant-ID: <your-tuskr-tenant-id>"Drop the Tenant-ID header if the instance already sets TUSKR_TENANT_ID
server-side.
claude mcp add stores the configuration without validating credentials, so a
wrong token is only reported when the server is first contacted. Run /mcp
inside a session and check that tuskr shows as connected.
Add --scope project to write the entry to the repository's .mcp.json and
share it with the team. Keep credentials out of a shared file: reference them as
--env TUSKR_ACCESS_TOKEN=${TUSKR_ACCESS_TOKEN} and let each developer supply
the value from their own environment.
Claude desktop app
Edit claude_desktop_config.json (Settings, Developer, Edit Config):
macOS:
~/Library/Application Support/Claude/claude_desktop_config.jsonWindows:
%APPDATA%\Claude\claude_desktop_config.json
For a local stdio server:
{
"mcpServers": {
"tuskr": {
"command": "uvx",
"args": ["tuskr-mcp-server", "--transport", "stdio"],
"env": {
"TUSKR_TENANT_ID": "<your tenant id>",
"TUSKR_ACCESS_TOKEN": "<your access token>"
}
}
}
}command has to resolve on the app's own PATH, which is not the same as your
shell's. If the server fails to start, use the absolute path to uvx.
For a shared HTTP instance:
{
"mcpServers": {
"tuskr": {
"type": "http",
"url": "https://<your-mcp-dns-or-ip>/mcp/",
"headers": {
"Authorization": "Bearer <your access token>",
"Tenant-ID": "<your-tuskr-tenant-id>"
}
}
}
}The type key is required on any entry that uses url; without it the entry is
read as a stdio server and skipped. Restart the app after editing the file.
Check it works
Ask Claude to list your Tuskr projects. It should call list_projects and come
back with project names and IDs. A useful follow-up, since it exercises a filter
and a second tool: ask which test runs in a given project are not yet finished,
which resolves to list_test_runs with filter_incomplete.
A request such as "mark C-14 and C-15 as passed in run R-3" resolves to a
single add_test_run_results call. Bear in mind that several of these tools
write to Tuskr, so point the server at a sandbox project while you are still
exploring.
Development
Setup
Clone repo
Install development dependencies:
uv sync --devCreate
.envfrom.env.example
Running MCP service
HTTP Transport (Default)
uv run --env-file .env src/main.pystdio Transport (for local development)
uv run --env-file .env src/main.py --transport stdioCustom Host/Port
uv run --env-file .env src/main.py --host 127.0.0.1 --port 9000Running tests
The project uses pytest for testing. The following command will run all tests
uv run pytest -vsxRunning linters
The project uses the ruff tool as a linter.
CI runs both of these and fails on any finding, so run them before pushing:
uv run ruff format --check .
uv run ruff check .uv run ruff format . rewrites the files in place to fix formatting.
Dockerization
The following command allows to build a docker image
docker build -t tuskr-mcp .and then run it with your .env file, publishing the server port:
docker run -it --rm --env-file .env -p 8000:8000 tuskr-mcpThe server is then reachable at http://localhost:8000/mcp/. If your .env
sets MCP_PORT, publish that port instead.
Available Tools
16 toolsadd_test_caseAdd Test CaseA
Creates a new test case in a project and returns it.
The test suite and section must already exist: this tool does not create them. There is no delete endpoint for test cases in the Tuskr API, so use a sandbox project while experimenting and name throwaway cases clearly.
Pass test_case_type as well: Tuskr rejects a create without one with
"Type is required", at least on a tenant with no default type. Find a
type's ID on the testCaseTypeId field of any list_test_cases row.
Inputs: name (required), project (required), test_suite, description, +4 more.
add_test_run_resultsAdd Test Run ResultsA
Records a result against one or more test cases in a test run.
Tuskr exposes a single bulk endpoint for results, so one call applies the same status to every test case listed. Pass a single test case to record one result, or a list to record many in one request. Always prefer one call with a list over repeated single calls: Tuskr rate-limits every plan at 10 requests/second.
Inputs: status (required), test_run (required), test_cases (required), comments, +4 more.
archive_test_runsArchive Test RunsA
Archives one or more test runs.
Archiving hides a run from the default test-run list without deleting it. Archived runs remain readable: pass filter_status='archived' to list_test_runs to see them, or filter_status='active' to exclude them.
The endpoint is bulk. Reversing an archive is not exposed as a dedicated API action: Tuskr's own UI does it by sending the run's full object back with status changed to "active", which this server does not wrap yet. Restoring an archived run today means using the Tuskr UI.
The response is a bare boolean, not a run snapshot. Confirming the new status on a specific run needs a follow-up list_test_runs call with filter_status set.
Inputs: test_runs (required).
bulk_export_test_runsBulk Export Test RunsA
Exports test runs of a project together with their test cases and the latest result of each case, in one call.
One call can return several runs, where get_test_run_results needs a call per run and per page. Always narrow the export with filter_ids or the other filters when test cases are included: a project's runs with all their cases run to tens of megabytes. A response over the MCP size limit is replaced by an error saying how large it was and how to narrow it.
Run fields and the response envelope ({count, rows, meta}) are passed through as the API sends them, keys in the API's own capitalisation.
Inputs: filter_project (required), page, filter_ids, filter_status, +4 more.
copy_test_runCopy Test RunA
Creates a copy of an existing test run along with its test cases.
The copy is created in the same project as the source run: this endpoint takes no project parameter, so a run cannot be copied across projects. The case selection is carried over in full and cannot be narrowed here; use create_test_run when a different selection is needed. Results are not carried over: every case in the copy starts UNTESTED even if the source run had recorded results (verified live against a sandbox project).
Every optional argument below overrides the corresponding attribute on the copy. Leaving one unset keeps the source run's own value rather than clearing it.
Inputs: test_run (required), name, deadline, assigned_to, +1 more.
create_test_runCreate Test RunC
Creates a new test run in a project.
Inputs: name (required), project (required), test_case_inclusion_type (required), deadline, +3 more.
get_test_run_detailsGet Test Run DetailsA
Fetches a test run's details, including when execution started and ended.
Besides the run's metadata (id, key, name, project, description, references, assignedTo, externalId, deadline, status), the response carries three timing fields:
firstResultAt: timestamp of the first result marked in the run
lastResultAt: timestamp of the last result marked in the run
durationInMinutes: time between the two, computed by Tuskr
This is the direct way to answer "when did this run finish" or "how long did execution take". Deriving the same from each case's resultHistory via get_test_run_results is slower and needs one large call per run.
Caveat: Tuskr says it timestamps only results marked after this endpoint shipped (8 Sept 2026), so older runs may return null firstResultAt and lastResultAt with a meaningless durationInMinutes. This is not guaranteed: a run whose only result predates the release still returned real timestamps. A run with no results also returns nulls. A null therefore means unknown, not "no results yet"; check the run's results before concluding that.
The endpoint takes one test run per call. Tuskr rate-limits every plan at 10 requests/second.
Inputs: test_run (required).
get_test_run_resultsGet Test Run ResultsA
Fetches the test cases in a test run together with their latest result.
The main use is confirmation testing: pass status='FAILED' to get back only the test cases that need re-checking after a fix.
Inputs: test_run (required), page, status, test_cases, +2 more.
get_test_run_timelineGet Test Run TimelineA
Reports when testing in a test run actually started and finished.
Use this instead of comparing a run's createdAt to its lockedAt. Those two bracket the run's administrative life, not its testing: a run is typically opened before anyone executes against it and locked well after the last result, so the gap between them can be twice the real duration.
This reads the per-execution timestamps Tuskr keeps on each test case and keeps only those recorded against this run, giving the true first and last execution, the span between them, and how many distinct days saw activity.
For a recent run, get_test_run_details is the cheaper way to get the first and last result times and the duration: one small call, where this tool fetches every results page untrimmed (100 cases per page). Use this tool when those timestamps come back null (Tuskr may not have recorded them for older results), or when you need the number of distinct days with activity, the daily breakdown, or the cases that were never executed.
Inputs: test_run (required), include_daily_breakdown.
list_projectsList ProjectsC
Retrives list of projects based on various filter criteria.
Inputs: page, filter_name, filter_status.
list_test_casesList Test CasesA
Retrieves list of test cases of a project with support for various filters.
Use this to discover the test case keys a project actually contains before recording results against them with add_test_run_results.
Inputs: filter_project (required), page, filter_key, filter_name, +4 more.
list_test_runsList Test RunsC
Retrieves list of test runs of a project with support for various filters.
Inputs: filter_project (required), page, filter_key, filter_name, +3 more.
list_test_suitesList Test SuitesA
Retrieves list of test suites and their associated sections in a project.
Use this to discover suite and section IDs. Pass them to list_test_cases as filter_test_suite and filter_test_suite_section instead of reading the IDs off existing test case rows.
Each suite row embeds its sections. Returns Tuskr's raw rows, count and meta for a single page; this tool does not fetch further pages.
Inputs: filter_project (required), page, filter_name.
set_test_run_lockSet Test Run LockA
Locks or unlocks a test run.
A locked test run is read-only: results can no longer be recorded against it and its test cases can no longer be edited. Locking is otherwise a manual action in the Tuskr UI, so this tool is what lets an automated workflow seal a run once execution is complete.
The endpoint accepts one test run per call, so locking several runs takes one call each. Tuskr rate-limits every plan at 10 requests/second.
Inputs: lock (required), test_run (required).
update_test_runUpdate Test RunA
Adds test cases to an existing test run and removes test cases from it.
This is how to change which cases a run contains after it was created. It also switches a run between ALL and SPECIFIC case selection. Do not use add_test_run_results to add a case: recording a result for a case that is not in the run returns an empty list and stores nothing. Do not use copy_test_run either, which resets every result.
To add a case: read the run's current case IDs with get_test_run_results (the testCase.id of each row), append the new case's ID (the id of a list_test_cases row), and send the full list as test_cases.
To remove a case: send the run's current case IDs without that case. A case that already has a result in this run cannot be removed. Tuskr then answers HTTP 400 "Cannot remove test case because it already has results: ", and this tool returns that text as is.
test_cases is the complete selection, not a delta: it replaces the current one, so any case left out is removed. Cases that stay in the selection keep the results already recorded for them, also across a switch to ALL and back.
Switching from SPECIFIC to ALL takes no test_cases. Switching from ALL to SPECIFIC needs test_cases to include every case that already has a result in the run.
Every identifier here is a UUID, unlike create_test_run: keys and names are not accepted for the run, the project or the cases. The tool never sends the run's description, deadline or assignee, and a description stays unchanged. On success Tuskr returns the updated run.
Inputs: test_run (required), test_case_inclusion_type (required), name, project, +1 more.
upsert_test_caseUpsert Test CaseA
Creates a test case or updates an existing one, and returns it.
This tool can both create new test cases and modify existing ones. Exactly
one of test_case_id or external_id must be given:
test_case_idupdates the test case with that ID; the case must already exist.external_idupdates the case with that external ID in the project, or creates a new case carrying that external ID when none exists.
Only the fields you pass are sent. A field left unset keeps its current
value on an update, so this tool cannot clear a field to empty. Pass name
when the call may create a case.
Unlike add_test_case, this endpoint looks the suite and section up by NAME only: a suite or section ID is answered with "Test suite does not exist". With create_missing_suite or create_missing_section set, an ID passed by mistake would instead create a suite or section named after it. The test case type accepts an ID or a name.
Inputs: project (required), name, test_suite, description, +8 more.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 16 tools
Most tools target clearly distinct operations across test runs, test cases, results, suites, and projects. The main overlap is add_test_case vs upsert_test_case and the several run-reading tools, but the detailed descriptions explicitly differentiate them, reducing misselection risk.
All tool names use snake_case with a predictable verb_noun structure such as create_test_run, get_test_run_results, and list_test_cases. Minor compound verbs like bulk_export are still readable and consistent with the overall convention.
Sixteen tools is slightly above the ideal compact range but justified by the breadth of the test-management domain: runs, cases, results, suites, projects, export, archiving, and locking. Each tool maps to a reasonably distinct operation rather than redundant surface area.
The core lifecycle for test runs, test cases, and results is covered, including creation, listing, result recording, locking, archiving, and export. However, there are notable gaps: no dedicated suite/section management beyond listing, no project creation/update, no test run metadata editing, and no restore-from-archive operation.
Maintenance
Related MCP Connectors
Manage Timequip projects, tasks, comments, members, and dashboards through MCP.
Free test management over MCP: projects, test cases, plans, runs, environments and tasks.
Read and write Mission Control state via MCP — projects, tasks, subtasks, templates, status updates.
Unofficial community ClickUp MCP via personal API token. Not affiliated with ClickUp.
Related MCP Servers
- AlicenseBqualityDmaintenanceMCP server for Tuskr test management that allows browsing, searching, reading, creating, and updating test cases, suites, sections, and test runs from any MCP host like Cursor.17MIT
- AlicenseAqualityDmaintenanceEnables test execution and management through MCP clients, allowing retrieval of projects, listing tests, executing tests with browser selection, and monitoring results.612 npmMIT
- AlicenseBqualityDmaintenanceEnables TestRail test management through MCP tools, allowing users to manage projects, test cases, runs, and results via natural language.2548 npmMIT
- AlicenseNot gradedqualityDmaintenanceEnables interaction with Kiwi TCMS test management system through MCP, supporting read and write operations on test plans, cases, runs, and executions, with API key authentication.1AGPL 3.0