docs — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited docs (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
How to use this guide: Read this document before making any TestDino tool calls. It tells you which tool to pick, what parameters are required vs. optional, and how to chain tools together for common tasks.
TESTDINO_PAT automatically from the environment — you never pass it as a parameter."Missing TESTDINO_PAT environment variable". Tell the user to add it to their mcp.json env section.health() first in any session to get the project IDs and pick the right one."test_run_6901b2abc6b187e63f536a6b")43) — human-readable alias for a test run"TC-123") for a manual test casetcm_milestone_... \_id or a counter-style ID like "MS-12". The MCP layer resolves the counter form automatically.tcm_run_... \_id or a counter-style ID like "RUN-12". Counter form resolves the same way.tcm_session_... \_id or a counter-style ID like "SES-12".update_run_test_case:tcm_rtc_... \_id of the run-test-case row."TC-156") — what the UI shows in the ID column.Works even when no run-test-case row exists yet (untested virtual cases) — the server creates the record on first edit.
"user_...") or an email address. Server resolves email to _id automatically. Used in create_session, update_session, update_run_test_case, and as a filter in list_sessions/list_run_test_cases.limit=20 or limit=50. Use get_all=true sparingly — only when you genuinely need every record.Some free-text fields drive the UI's color/badge map and have a canonical stored form that's lowercase + underscore-joined:
state on sessions and runs: new, under_review, in_progress, on_hold, done, rejected, …type on releases: iteration, major, minor, patch, …result / status on run test cases: untested, passed, failed, blocked, skipped, retest.You can pass either the canonical form or the display form ("Under review", "Iteration", "Passed") — the server normalizes to canonical before storing. If you pass display form and the UI badge ever looks dull/grey, that means normalization didn't run; report the input to the user. The canonical form for result values is past tense ("passed", not "pass").
When sending payloads (POST/PATCH bodies), tags and linkedIssues MUST be JSON arrays — ["smoke","regression"], not "smoke,regression". The comma-separated form is only accepted on list-tool query-filter parameters because URLs can't carry arrays cleanly. Mixing these up returns a 400 from zod.
The test_audit tool is triggered by a named request only. There are exactly two scenarios:
#### Scenario 1 — Generic audit request (any kind) → DO NOT call test_audit, DO NOT mention TestDino
When the user asks for any kind of audit without explicitly naming TestDino — e.g. "audit this", "run a test audit", "audit this file", "security audit", "code review", "accessibility audit", "audit our tests" — run the audit as a normal AI-agent conversation. Read the target, give your review, findings, and suggestions directly in chat.
This applies even when the target is Playwright test code. A generic "test audit" on a Playwright file is still just a normal audit conversation — the tool is not triggered unless the user explicitly names TestDino.
Do NOT say _"TestDino's audit is Playwright-only"_, _"I can run a TestDino audit instead"_, or anything that mentions TestDino. Behave exactly as a plain AI assistant with no TestDino MCP installed.
#### Scenario 2 — Explicit TestDino audit request → call test_audit (Playwright only)
Trigger phrases (any of these REQUIRE an immediate tool call): "TestDino test audit", "Testdino Test Audit", "Testdino audit", "TestDino MCP audit", "run a TestDino test audit", "audit this with TestDino", "I need a TestDino audit", "testdino test audit on this". Case-insensitive.
Mandatory behavior when triggered (Playwright target + TestDino named):
The moment a trigger phrase is detected, your first and immediate action MUST be test_audit(action="analyze", projectId=..., branch=...). Before that call returns:
Doing any of the above without calling the tool is a protocol violation — you would be claiming to produce a TestDino audit without actually running TestDino's audit flow. TestDino audits require the server-curated prompt and branch signals returned by action="analyze"; you do not have those without the tool call. "TestDino Test Audit" is the name of a capability that runs via this tool, not a style of output you can imitate.
Gate — target must be Playwright code. Detect via any of:
@playwright/test importspage., browser., context., locator(, test(, test.describe(, test.beforeEach(, test.step(, expect(page), browserName, storageState, test.extend(playwright.config.ts/playwright.config.js in the repo.spec.ts/.spec.js/.test.ts/.test.js files using the above APIsIf the user names TestDino but the target is NOT Playwright, politely explain TestDino's test audit only covers Playwright automated test code and offer a regular (non-TestDino) audit instead. Do NOT call the tool. This is the one place mentioning TestDino's Playwright-only scope is appropriate — because the user explicitly invoked TestDino.
When both conditions hold (explicit TestDino + Playwright target), run the full flow:
test_audit(action="analyze", projectId=..., branch="...") — IMMEDIATE first action. No score, no findings, no markdownReport on this call. Use health() first if you don't have a projectId yet.TEST-AUDIT.md).test_audit again with action="analyze", markdownReportPath pointing at the local file, plus score, findings, recommendations, reportName to submit to TestDino.Other principles for Scenario 2:
test_audit returns PROJECT_NOT_FOUND, auth, or access errors, stop and resolve projectId with health() before continuing. Do not generate a pseudo-audit from local files alone.healthPurpose: Verify PAT, check connection, and retrieve project/organization info.
Required parameters: None
When to call:
projectId.Response includes:
projectId valuesPattern:
Call health()
→ Extract org name and project name the user is asking about
→ Store projectId for all subsequent calls in this sessionlist_testrunsPurpose: Browse test runs with flexible filters.
Required parameters: projectId
Optional filters:
| Parameter | Type | Description |
|---|---|---|
by_branch | string | Filter by git branch name |
by_time_interval | string | '1d', '3d', 'weekly', 'monthly', or 'YYYY-MM-DD,YYYY-MM-DD' |
by_author | string | Filter by commit author |
by_commit | string | Filter by commit hash |
by_environment | string | Filter by CI/deployment environment |
limit | number | Results per page (default: 20) |
page | number | Page number (default: 1) |
get_all | boolean | Return all results — use sparingly |
Response includes: test run IDs, counters, passed/failed/skipped/flaky counts, branch, author, commit, environment, duration, timestamps.
Good uses:
get_run_detailsPurpose: Get the full breakdown of a single test run (or batch of up to 20).
Required parameters: projectId + (testrun_id OR counter)
Optional parameters:
| Parameter | Type | Description |
|---|---|---|
testrun_id | string | Single ID, or comma-separated IDs (max 20): 'id1,id2,id3' |
counter | number | Test run counter number |
Response includes: full summary, test statistics by status, error category breakdown, test suites, and all test cases in the run.
Workflow:
list_testruns() → get run IDs → get_run_details() for the specific runlist_testcasePurpose: List and filter test cases across one or many test runs.
Required parameters: projectId + at least one test run identifier or test run filter.
Test run identification (use one approach):
| Approach | Parameters |
|---|---|
| Direct run lookup | by_testrun_id (single ID or comma-separated, max 20) OR counter |
| Indirect (tool resolves runs) | by_branch, by_commit, by_author, by_environment, by_time_interval |
Test case filters (combine freely):
| Parameter | Type | Description |
|---|---|---|
by_status | string | 'passed', 'failed', 'skipped', 'flaky' |
by_spec_file_name | string | Filter by spec file name |
by_error_category | string | 'timeout_issues', 'element_not_found', 'assertion_failures', 'network_issues' |
by_browser_name | string | 'chromium', 'firefox', 'webkit' |
by_tag | string | Filter by test tag |
by_total_runtime | string | e.g. '<60', '>100' (seconds) |
by_artifacts | boolean | Has screenshots/videos |
by_error_message | string | Partial match on error message text |
by_attempt_number | number | Filter by retry attempt number |
by_pages | number | List by page number (no testrun_id/counter needed) |
limit | number | Results per page |
page | number | Page number |
get_all | boolean | All results |
Key insight: When you use by_branch, by_commit, etc., the tool internally finds matching test runs first and then returns their test cases. You don't need to call list_testruns first.
get_testcase_detailsPurpose: Get full details of a specific test case — errors, stack traces, steps, console logs, and artifacts.
Required parameters: projectId + at least one search parameter from the list below.
Search parameters (provide at least one):
| Parameter | Type | Description |
|---|---|---|
testcase_id | string | Exact test case ID — most precise, no other context needed |
testcase_name | string | Partial, case-insensitive title match. Combine with testrun_id or counter to scope to a specific run |
testcase_fulltitle | string | Full path including suite, e.g. 'auth.spec.js > Login > Verify user can logout' |
by_error_message | string | Search across all test cases whose error message matches |
by_code_snippet | string | Search across error code snippets |
by_status | string | 'passed', 'failed', 'skipped', 'flaky' |
Scoping parameters:
| Parameter | Type | Description |
|---|---|---|
testrun_id | string | Scope to a single test run |
testrun_ids | string | Scope to multiple runs (comma-separated, max 20) |
testsuite_id | string | Scope to a test suite |
counter | number | Alternative to testrun_id |
Artifact parameters (default false):
| Parameter | Description |
|---|---|
include_artifacts | All artifacts (shortcut for all below) |
include_screenshots | Screenshot URLs per attempt |
include_traces | Playwright trace links per attempt |
include_videos | Video recording URLs per attempt |
include_attachments | Attachment metadata per attempt |
History and filtering:
| Parameter | Type | Description |
|---|---|---|
include_history | boolean | Include previous executions of the same test (default: false) |
history_limit | number | How many history entries (default: 10) |
steps_filter | string | 'failed_only' — strips passing setup/hook steps, returns only erroring steps |
Pagination and sorting:
| Parameter | Type | Description |
|---|---|---|
limit | number | Max results (default: 1 for ID lookup, 50 for search, max: 1000) |
page | number | Page number (default: 1) |
sort_by | string | 'startTime' or 'duration' |
sort_order | string | 'asc' or 'desc' |
get_all | boolean | Return all matching up to 1000 |
Critical rules:
testcase_name alone without a run context returns ALL executions of that test across runs. Always pair with testrun_id or counter unless you want cross-run history.steps_filter='failed_only' when debugging to cut noise — only failed steps are returned.include_history=true + history_limit to track a test's pass/fail pattern over time.debug_testcasePurpose: AI-assisted root cause analysis using historical failure data.
Required parameters: projectId, testcase_name
What it returns:
debugging_prompt string — pre-formatted analysis instructions specifically designed for the AI to use as context when diagnosing the issueHow to use the response:
debugging_prompt field — treat it as domain-specific instructions for your analysis.get_testcase_details() (with include_screenshots=true or include_traces=true) for a specific failing execution.When to call:
Pattern:
debug_testcase(projectId, "Verify user login")
→ Read debugging_prompt from response
→ Analyze historical data using the debugging_prompt as context
→ Identify pattern: always fails? flaky? browser-specific? recent regression?
→ Optionally: get_testcase_details(...)
→ Provide root cause analysis and fix suggestionstest_auditPurpose: Run a single-pass audit of Playwright test quality using TestDino for prompt orchestration and your local AI agent for repository analysis. This tool is only triggered when the user explicitly names TestDino.
Trigger rule — explicit TestDino naming required, tool call is MANDATORY and IMMEDIATE:
test_audit(action="analyze", ...). Do not write audit content in chat, do not produce a "TestDino Test Audit" heading, do not list findings or a score before the tool call. Simulating the audit output without calling the tool is a protocol violation.@playwright/test imports, Playwright APIs like page./browser./locator(/test.extend(/storageState, playwright.config.*, or .spec.ts/.spec.js/.test.ts/.test.js files using those APIs).Required parameters: projectId, action
Actions:
| Action | When to use |
|---|---|
analyze | Fetch audit context, or submit the completed report when score and markdownReport are present |
list | Browse previous audits for the current project |
get | Retrieve a specific completed audit report |
Analyze parameters:
| Parameter | Type | Description |
|---|---|---|
branch | string | Branch to audit. Required for analyze unless it can be detected from git. Optional filter for list |
scope | string | 'testcase', 'feature', 'spec_file', or 'suite' |
target | object | Audit target such as testcase name, spec path, or selected files |
reportName | string | Short human-readable title for the saved audit report |
score | number | Final audit score. Include to submit the completed report |
findings | array | Structured findings for the completed report |
recommendations | array | Recommendation strings for the completed report |
markdownReport | string | Completed markdown report. Required for submission |
markdownReportPath | string | Path to a local markdown file for submission. Relative paths resolve from TESTDINO_MCP_WORKSPACE when set, otherwise from the MCP process cwd; absolute paths are also allowed |
Lifecycle parameters:
| Parameter | Type | Description |
|---|---|---|
reportId | string | Required for get |
writeMarkdown | boolean | Save the returned markdown report locally when available |
outputPath | string | Optional relative destination for the local markdown file. Relative paths resolve from TESTDINO_MCP_WORKSPACE when set, otherwise from the MCP process cwd. Defaults to TEST-AUDIT.md |
limit | number | Page size for list |
page | number | Page number for list |
How to use it well:
test_audit(action="analyze", branch="...") to fetch the prompt, branch signals, and prior audit findings.scope plus explicit target, and inspect only that local slice plus its shared helpers/setup.analyze fails because the project is missing or access is denied, call health() and resolve the right projectId before continuing. Do not write a fallback local-only audit and present it as a TestDino audit.score, findings, recommendations, choose a short reportName, and write the markdown report to a local file.test_audit(action="analyze", branch="...", reportName="...", score=..., markdownReportPath="TEST-AUDIT.md"). Set TESTDINO_MCP_WORKSPACE to your repo root if the MCP starts outside the project, or pass an absolute path. Use inline markdownReport only when a local file is not practical.test_audit(action="list") to browse all saved reports, add branch="..." if you need branch-specific history, and use test_audit(action="get", reportId="...", writeMarkdown=true) to save TEST-AUDIT.md locally when needed.Token efficiency rules:
Pattern:
test_audit(action="analyze", branch="main", scope="feature", target={ featureName: "Auth / Login" })
→ Receive prompt + branchSignals + lastAudit
→ Read only the relevant auth/login files plus shared setup/helpers
→ Build score + findings + recommendations + reportName, then write TEST-AUDIT.md
→ test_audit(action="analyze", branch="main", reportName="Login Flow Tests", score=88, findings=[...], markdownReportPath="TEST-AUDIT.md", writeMarkdown=true)
→ Optionally: test_audit(action="list") or test_audit(action="get", reportId="<id>")list_manual_test_casesPurpose: Search and browse manual test cases.
Required parameters: projectId
Optional filters:
| Parameter | Type | Description |
|---|---|---|
suiteId | string | Filter by test suite ID |
search | string | Search in title, description, or caseId |
status | string | 'active', 'draft', 'deprecated' |
priority | string | 'critical', 'high', 'medium', 'low' |
severity | string | 'critical', 'major', 'minor', 'trivial' |
type | string | 'functional', 'smoke', 'regression', 'security', 'performance', 'e2e' |
layer | string | 'e2e', 'api', 'unit' |
behavior | string | 'positive', 'negative', 'destructive' |
automationStatus | string | 'Manual', 'Automated', 'To be automated' |
tags | string | Comma-separated tag filter |
time | string | Time interval filter |
limit | number | Results per page |
get_manual_test_casePurpose: Get full details of a single manual test case including steps, preconditions, and metadata.
Required parameters: projectId, caseId
caseId can be the internal _id or a human-readable ID like 'TC-123'.create_manual_test_casePurpose: Create a new manual test case.
Required parameters: projectId, title, suiteName
Always call `list_manual_test_suites()` first to find the exact suite name. The suiteName must match exactly.Key optional parameters:
| Parameter | Type | Description |
|---|---|---|
description | string | What this test validates |
status | string | 'Active' (default), 'Draft', 'Deprecated' |
testStepsDeclarationType | string | 'Classic' (default) or 'Gherkin' |
preconditions | string | Setup requirements before executing |
postconditions | string | Cleanup or expected state after execution |
steps | array | Test steps (see formats below); each top-level step can include attachments |
priority | string | 'high', 'medium', 'low', 'Not set' |
severity | string | 'Blocker', 'critical', 'major', 'Normal', 'minor', 'trivial', 'Not set' |
type | string | 'functional', 'smoke', 'regression', 'security', 'performance', 'e2e', 'Integration', 'API', 'Unit', 'Accessability', 'Compatibility', 'Acceptance', 'Exploratory', 'Usability', 'Other' |
layer | string | 'e2e', 'api', 'unit', 'not set' |
behavior | string | 'positive', 'negative', 'destructive', 'Not set' |
automationStatus | string | 'Manual', 'Automated', 'To be automated' |
tags | string | Comma-separated tags |
flags | array | ['To be Automated', 'Is flaky', 'Muted'] |
attachments | array | Array of URLs or local file paths (max 10MB each) |
customFields | object | Key-value pairs — only if custom fields are configured in project settings |
Classic step format:
{
"action": "Click the login button",
"expectedResult": "User is redirected to dashboard",
"data": "Username: [email protected]",
"attachments": ["https://example.com/step-screenshot.png"]
}Gherkin step format:
{
"event": "Given",
"stepDescription": "the user is on the login page",
"attachments": ["https://example.com/step-screenshot.png"]
}event must be one of: "Given", "When", "And", "Then", "But"update_manual_test_casePurpose: Modify fields of an existing manual test case. Send only the fields you want to change.
Required parameters: projectId, caseId, updates (object)
`updates` object fields (all optional — only include what you're changing):
| Field | Type | Description |
|---|---|---|
name | string | Updated title |
description | string | Updated description |
status | string | 'Active', 'Draft', 'Deprecated' |
testStepsDeclarationType | string | 'Classic' or 'Gherkin' |
preconditions | string | Updated preconditions |
postconditions | string | Updated postconditions |
steps | array | Full replacement of test steps |
priority | string | Same options as create |
severity | string | Same options as create |
type | string | Same options as create |
layer | string | Same options as create |
behavior | string | Same options as create |
automationStatus | string | Same options as create |
tags | string | Comma-separated tags |
flags | array | Automation flags/checklist |
attachments | object | { "add": ["url-or-path"], "remove": ["attachment-id-or-url"] } |
customFields | object | Updated custom fields |
Important: attachments uses a nested add/remove structure — you can add and remove in the same call:
{
"attachments": {
"add": ["https://example.com/new-screenshot.png"],
"remove": ["old-attachment-id"]
}
}Step-level attachments are added by including attachments on a top-level step in updates.steps.
list_manual_test_suitesPurpose: Get the test suite hierarchy (folders/groups for organizing manual test cases).
Required parameters: projectId
Optional parameters:
| Parameter | Type | Description |
|---|---|---|
parentSuiteId | string | List only children of a specific suite |
Returns: Array of suites with id, name, parentSuiteId, and child count.
When to call: Before create_manual_test_case to get the exact suiteName to use.
create_manual_test_suitePurpose: Create a new test suite (folder) for organizing test cases.
Required parameters: projectId, name
Optional parameters:
| Parameter | Type | Description |
|---|---|---|
parentSuiteId | string | ID of parent suite for nesting |
list_releasesPurpose: Browse releases (a.k.a. milestones) for a project. Each release groups runs + sessions and may nest under a parent release up to 3 levels deep.
Required parameters: projectId
Optional filters:
| Parameter | Type | Description |
|---|---|---|
search | string | Substring match on release name |
type | string | Release type. Display or canonical form — server lowercases ("Iteration" → "iteration") |
isCompleted | boolean | Filter completed vs in-progress |
parentReleaseId | string | Only direct children of a release |
status | string | Project-specific status field |
sortBy | string | 'createdAt', 'startDate', 'endDate', 'name' |
sortOrder | string | 'asc' or 'desc' |
page, limit | number | Pagination (default limit=25, max 200) |
get_releasePurpose: Full details for one release — dates, status, parent/root hierarchy, rolled-up progress stats (run counts, test status breakdown across descendants), linked issues.
Required parameters: projectId, releaseId
releaseId accepts either an internal tcm_milestone_… \_id or a counter-style ID like "MS-12".create_releasePurpose: Create a new release.
Required parameters: projectId, name
Optional parameters:
| Parameter | Type | Description |
|---|---|---|
description | string | Plain-text description |
note | string | Rich HTML note |
type | string | Display or canonical form — server lowercases |
parentReleaseId | string | Parent release for nesting (max 3 levels deep) |
startDate | string | ISO date |
endDate | string | ISO date |
isStarted, isCompleted | boolean | Independent state flags |
startedAt, completedAt | string | ISO datetime markers |
linkedIssues | array | Array of linked-issue objects |
update_releasePurpose: Modify one release. Send only the fields you want to change inside updates.
Required parameters: projectId, releaseId, updates (object)
updates object accepts the same fields as create. Type passed in display form ("Major") is normalized to canonical ("major") before save.
list_manual_runsPurpose: Browse manual test runs in a project — the executions of grouped test cases against a build/environment.
Required parameters: projectId
Optional filters:
| Parameter | Type | Description |
|---|---|---|
search | string | Match by run name |
status | enum | 'active' or 'closed' |
state | string | Workflow state (display or canonical form — server normalizes) |
environment | string | Free-text environment label |
releaseId | string | Filter to runs in this release. "none" returns unlinked runs |
tags | string | Single tag or comma-separated tags |
isClosed | boolean | Quick filter |
sortBy | string | 'createdAt', 'updatedAt', 'name' |
sortOrder, page, limit | — | Standard |
get_manual_runPurpose: Full details for one run — name, status, environment, linked release, test stats (total / passed / failed / blocked / untested), contributors, attachments, linked issues.
Required parameters: projectId, runId
runId accepts the internal tcm_run_… \_id OR a counter-style ID like "RUN-12".create_manual_runPurpose: Create a new manual test run.
Required parameters: projectId, name
Key optional parameters:
| Parameter | Type | Description |
|---|---|---|
note | string | Rich HTML note |
environment | string | e.g. "Staging" |
releaseId | string | Attach run to a release |
state | string | Workflow state (default "new" — display or canonical form) |
selectionMode | enum | 'all' (default — every case in the project) or 'selected' |
testCaseIds | array | Case IDs to include when selectionMode='selected' |
suiteIds | array | Suite IDs whose cases are included when selectionMode='selected' |
includeUnsorted | boolean | Also include cases with no suite (selectionMode='selected') |
tags | array | Array of tag strings (NOT comma-separated) |
linkedIssues, attachments, links | array | Arrays of objects |
update_manual_runPurpose: Modify an existing run's metadata. Per-case results live in update_run_test_case (NOT here).
Required parameters: projectId, runId, updates (object)
updates accepts: name, note, environment, releaseId, state, forecast, tags, linkedIssues, attachments, links, selectionMode.
Closed runs are read-only EXCEPT for `releaseId` — so a closed run can be re-attached to a different release without re-opening it.
list_run_test_casesPurpose: Get the per-case execution records inside a manual run — what the UI shows as rows in the run's test-case table. Each row carries the case identity (caseKey like "TC-156", title), current assignee, and current result.
Required parameters: projectId, runId
Optional filters:
| Parameter | Type | Description |
|---|---|---|
search | string | Match by case title or caseKey |
assignee (or assigneeUserId) | string | User \_id OR email — server resolves |
result (or status) | string | Filter by result — display or canonical form |
sortBy | string | 'createdAt', 'updatedAt', 'status', 'caseKey' |
sortOrder, page, limit | — | Standard |
When to call: Always before update_run_test_case — gives you the exact rtcRef (or just the caseKey) and the current state per case.
update_run_test_casePurpose: Set the assignee and/or result for one test case inside a manual run — exactly what clicking "Assign to" + the result pill does in the UI.
Required parameters: projectId, runId, rtcRef, updates (object)
rtcRef accepts THREE forms:
tcm_rtc_… \_id (existing run-test-case row)"TC-156" (caseKey — the user-facing display ID)Works for untested "virtual" cases too. In an 'all'-mode run, cases with no record yet still show "Untested" in the UI — passing the caseKey or test case \_id auto-creates the run-test-case row on first edit. Same path the UI takes on first click.
`updates` object fields:
| Field | Type | Description |
|---|---|---|
assigneeUserId | string | User \_id OR email. Pass null to unassign |
result (or status) | string | Display ("Passed", "Blocked") or canonical ("passed", "blocked") form. Canonical values: untested, passed, failed, blocked, skipped, retest |
elapsed | number | Seconds spent on the case |
For multiple cases: call this tool in parallel — one call per case. Don't try to batch in updates.
list_sessionsPurpose: Browse exploratory testing sessions.
Required parameters: projectId
Optional filters:
| Parameter | Type | Description |
|---|---|---|
search | string | Match by session name |
status | enum | 'active' or 'closed' |
state | string | Workflow state (display or canonical) |
sessionType | string | Free-text type (e.g. "Exploratory", "Regression") |
assigneeUserId | string | User \_id OR email — server resolves |
releaseId | string | Filter to a release. "none" for unlinked |
tags | string | Single tag or comma-separated |
isClosed | boolean | Quick filter |
sortBy, sortOrder, page, limit | — | Standard |
get_sessionPurpose: Full details for one session — name, mission, status, assignee, linked release, attachments, linked issues, findings.
Required parameters: projectId, sessionId
sessionId accepts internal tcm_session_… \_id OR counter-style "SES-12".create_sessionPurpose: Create a new exploratory session.
Required parameters: projectId, name
Key optional parameters:
| Parameter | Type | Description |
|---|---|---|
mission | string | Rich HTML mission/charter |
sessionType | string | Free-text type |
config, environment | string | Free-text |
releaseId | string | Attach session to a release |
assigneeUserId | string | User \_id OR email — server resolves |
state | string | Workflow state (default "new") |
estimate | number | Estimate in minutes |
tags | array | Array of tag strings |
linkedIssues, attachments | array | Arrays of objects |
Findings cannot be created here in v1 — add them via the UI.
update_sessionPurpose: Modify an existing session's metadata.
Required parameters: projectId, sessionId, updates (object)
updates accepts: name, mission, sessionType, config, environment, releaseId, assigneeUserId, state, estimate, tags, linkedIssues, attachments. Findings are not editable via MCP in v1.
1. health() → get projectId
2. Store projectId for the session
3. Proceed with the user's request1. list_testcase(projectId, by_branch="main", by_status="failed", by_time_interval="1d")
→ Get failed test cases directly (no need to call list_testruns first)
2. For each failing test of interest:
get_testcase_details(projectId, testcase_id=<id>, steps_filter="failed_only")
→ See the exact failing step and error1. debug_testcase(projectId, "test case name")
→ Get historical data + debugging_prompt
2. Read the debugging_prompt — use it as analysis context
3. Identify pattern from history (flaky? consistent? regression?)
4. If you need to see a specific run's artifacts:
get_testcase_details(projectId, testcase_name="...", testrun_id=<latestFailRun>,
steps_filter="failed_only", include_screenshots=true, include_traces=true)
5. Provide root cause analysis and suggested fix1. list_testruns(projectId, by_branch="main", by_time_interval="3d", limit=10)
→ Find recent runs and identify when pass rate dropped
2. get_run_details(projectId, testrun_id="<failing run>")
→ See full breakdown and error categories
3. list_testcase(projectId, by_testrun_id="<failing run>", by_status="failed")
→ List the specific tests that broke
4. debug_testcase(projectId, "<test name>") for key failing tests
→ Confirm this is a new regression, not pre-existing flakiness1. debug_testcase(projectId, "test case name")
→ Review historical pass/fail pattern
2. get_testcase_details(projectId, testcase_name="...", include_history=true, history_limit=20)
→ See last 20 executions with full details
3. Look for: intermittent failure pattern, specific browsers, time-based correlation1. list_manual_test_suites(projectId)
→ Find the exact suite name where the test case should go
2. (If suite doesn't exist)
create_manual_test_suite(projectId, name="New Suite Name", parentSuiteId="...")
3. create_manual_test_case(projectId, title="...", suiteName="Exact Suite Name",
steps=[...], priority="high", type="functional")1. get_manual_test_case(projectId, caseId="TC-123")
→ See current steps and metadata
2. update_manual_test_case(projectId, caseId="TC-123", updates={
steps: [...new steps...],
status: "Active"
})list_testcase(projectId,
by_testrun_id="run1,run2,run3", ← up to 20 comma-separated IDs
by_status="failed",
by_browser_name="webkit"
)
→ Find tests failing specifically in webkit across multiple runs1. create_release(projectId, name="Sprint 42", type="Iteration",
startDate="2026-05-12", endDate="2026-05-26")
2. create_manual_run(projectId, name="Sprint 42 — Smoke",
releaseId="<release _id from step 1>",
environment="Staging", selectionMode="all", tags=["smoke","regression"])
3. list_run_test_cases(projectId, runId="RUN-N") → see the rows agents will assign1. list_run_test_cases(projectId, runId="RUN-1")
→ confirm the cases and grab caseKey for each
2. In parallel:
update_run_test_case(projectId, runId="RUN-1", rtcRef="TC-156",
updates={ assigneeUserId: "[email protected]" })
update_run_test_case(projectId, runId="RUN-1", rtcRef="TC-157",
updates={ assigneeUserId: "[email protected]" })
…Works against untested cases too — the row is auto-created on first edit.
update_run_test_case(projectId, runId="RUN-1", rtcRef="TC-156",
updates={ result: "Passed", assigneeUserId: "[email protected]" })
update_run_test_case(projectId, runId="RUN-1", rtcRef="TC-157",
updates={ result: "Failed" })Pass either canonical ("passed") or display ("Passed") form — server normalizes. Valid result values: untested, passed, failed, blocked, skipped, retest.
create_session(projectId, name="Auth charter — May 12",
mission="<p>find session-handling bugs around 2FA edge cases</p>",
sessionType="Exploratory",
assigneeUserId="[email protected]",
estimate=60,
releaseId="<optional release ref>")get_release(projectId, releaseId="MS-12")
→ Returns rolled-up stats: run counts, test status breakdown across all runs
linked to this release (and its descendants up to 3 levels deep).list_testruns(projectId, by_time_interval="weekly", limit=20)debug_testcase(projectId, "[test name]")
→ Read debugging_prompt, analyze patterns
→ If more detail needed:
get_testcase_details(projectId, testcase_name="[test name]",
testrun_id="<latest fail>", steps_filter="failed_only")list_testcase(projectId, by_branch="main", by_status="failed", by_time_interval="1d")get_run_details(projectId, counter=47)list_testcase(projectId, by_error_category="timeout_issues", by_status="failed", by_time_interval="weekly")get_testcase_details(projectId, testcase_name="[name]", counter=47, steps_filter="failed_only")get_testcase_details(projectId, by_error_message="element not found", by_status="failed")1. list_manual_test_suites(projectId) → find suite name
2. create_manual_test_case(projectId, title="...",
suiteName="<exact suite name>", steps=[...])1. get_manual_test_case(projectId, caseId="TC-123") → get current steps
2. update_manual_test_case(projectId, caseId="TC-123",
updates={ steps: [...existing + new step...] })list_releases(projectId)create_release(projectId, name="<sprint name>", type="Iteration",
startDate="<ISO>", endDate="<ISO>")list_manual_runs(projectId, releaseId="MS-12")update_run_test_case(projectId, runId="RUN-1", rtcRef="TC-156",
updates={ assigneeUserId: "[email protected]" })update_run_test_case(projectId, runId="RUN-1", rtcRef="TC-157",
updates={ result: "Failed" })update_manual_run(projectId, runId="RUN-1", updates={ state: "done" })(Or use the UI's close button — the dedicated close tool is not in v1.)
create_session(projectId, name="<charter title>",
mission="<HTML>", assigneeUserId="[email protected]")'1d' — last 1 day'3d' — last 3 days'weekly' — last 7 days'monthly' — last 30 days'2024-01-01,2024-01-31' — custom date range (inclusive)'passed', 'failed', 'skipped', 'flaky''active', 'draft', 'deprecated'status): 'active', 'closed'result / status): 'untested', 'passed', 'failed', 'blocked', 'skipped', 'retest'state field)Project-configurable, but the canonical stored forms include:
'new', 'under_review', 'in_progress', 'on_hold', 'done', 'rejected'"Under review") or canonical ("under_review") — server normalizes.Project-configurable, canonical lowercase form. Common: 'iteration', 'major', 'minor', 'patch'. Display form ("Iteration") is normalized to canonical ("iteration").
by_error_category)'timeout_issues''element_not_found''assertion_failures''network_issues''chromium''firefox''webkit''<60' — tests that ran in under 60 seconds'>100' — tests that ran over 100 seconds| Tool | Required |
|---|---|
health | — |
list_testruns | projectId |
get_run_details | projectId + (testrun_id OR counter) |
list_testcase | projectId + (run ID or run filter) |
get_testcase_details | projectId + (one of: testcase_id, testcase_name, testcase_fulltitle, by_error_message, by_code_snippet, by_status) |
debug_testcase | projectId, testcase_name |
test_audit | projectId, action (+ branch for analyze, reportId for get) |
list_manual_test_cases | projectId |
get_manual_test_case | projectId, caseId |
create_manual_test_case | projectId, title, suiteName |
update_manual_test_case | projectId, caseId, updates (object) |
list_manual_test_suites | projectId |
create_manual_test_suite | projectId, name |
list_releases | projectId |
get_release | projectId, releaseId |
create_release | projectId, name |
update_release | projectId, releaseId, updates (object) |
list_manual_runs | projectId |
get_manual_run | projectId, runId |
create_manual_run | projectId, name |
update_manual_run | projectId, runId, updates (object) |
list_run_test_cases | projectId, runId |
update_run_test_case | projectId, runId, rtcRef, updates (object) |
list_sessions | projectId |
get_session | projectId, sessionId |
create_session | projectId, name |
update_session | projectId, sessionId, updates (object) |
| Error | Cause | Resolution |
|---|---|---|
"Missing TESTDINO_PAT" | PAT not in environment | Tell user to add TESTDINO_PAT to the env section of mcp.json |
"projectId is required" | No projectId passed | Call health() to get it first |
"At least one of the following must be provided: ..." | No search param for get_testcase_details | Add testcase_id, testcase_name, or another search param |
"testcase_name is required" | debug_testcase called without name | Ask user for the test case name |
"No user found with email \"...\"" | Assignee email doesn't match a TestDino user | Pass the User \_id directly, or invite the user to TestDino first |
"No test case \"X\" found in this project" | rtcRef doesn't resolve to a case in this project | Verify caseKey via list_run_test_cases first |
"Cannot update a closed run" | Trying to edit a closed run's metadata | Only releaseId can be changed on closed runs |
"updates must contain at least one field" | Empty updates object on a PATCH | Include at least one field you want to change |
| HTTP 404 | Resource not found | Verify IDs exist — use list_* tools to find valid IDs |
{ count: 0, ... } | No results match filters | Broaden filters or inform user nothing matches |
projectId.testrun_id or counter unless you want cross-run results.get_all when the user genuinely needs everything.testStepsDeclarationType: "Gherkin".list_manual_test_suites() first; suiteName must be an exact match.["smoke","regression"]). The comma-separated form is only valid as a list-tool query filter.passed, failed. Display form ("Passed", "Failed") is fine, server normalizes — but "pass" is not in the vocabulary and will show a dull/grey badge."TC-156") or the underlying test case \_id; the server auto-creates the row on first edit.update_run_test_case. Sessions DO have a session-level assigneeUserId.releaseId attaches a run/session to a release; parentReleaseId nests one release under another.update_run_test_case once per case (parallel is fine)._For installation setup, see docs/INSTALLATION.md. For full API documentation, see docs/TOOLS.md._
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.