@testplanit/mcp-server
Model Context Protocol server for TestPlanIt — exposes test-case data to AI agents over stdio.
Versions
0.3.1latest1.0.0-beta.00.3.00.2.00.1.4+ show 4 moreshow less
0.1.30.1.20.1.10.1.0Tools 50
testplanit_test_runs_get Fetch a single test run with denormalized header (state/createdBy/configuration/milestone/tags/issues/testRunType), status-count rollup (counts SUM to total per R3), and the first 50 test cases inline (each with latestResult, a union of manual TestRunResults and automated JUnit results discriminated by `source`). Rollup source depends on testRunType: REGULAR runs count TestRunCases by execution status (`untested` = cases with no result, `total` = case count); automated runs (testRunType != REGULAR: JUNIT/TESTNG/XUNIT/NUNIT/MSTEST/MOCHA/CUCUMBER) count imported JUnit result ROWS by status — attempts, so retries count once per row and `total` is the attempt count, matching the web UI. Automated results live in JUnit suite tables — list them via testplanit_test_run_results_list({runId}); rows come back with source \"JUnit\". Inline testCases[].status is the junction row's status when set, falling back to latestResult.status (the automated-run junction rows never carry their own status). When the run has more than 50 cases, `testCasesNextCursor` is set; call testplanit_test_runs_cases_list with that cursor to fetch the rest. (per EXEC-02 / D7-04 / D7-05)
testplanit_reviews_decide Record a review decision on behalf of the authenticated user, for a review request assigned to them (find ids with testplanit_reviews_list). IRREVERSIBLE AND VISIBLE TO OTHERS: decisions are append-only — they cannot be changed or retracted, they notify the requester, and APPROVING APPLIES THE WORKFLOW TRANSITION, moving the test case / run / session into the requested state. Confirm the decision with your user before calling; do not approve on your own initiative. `decision` is APPROVED, CHANGES_REQUESTED, or REJECTED. `comment` is required for CHANGES_REQUESTED and REJECTED (the reviewer must say what is wrong) and optional for APPROVED; it is posted as a comment on the entity's thread, addressed to the requester. Fails with INELIGIBLE_REVIEWER if the caller is not the assignee (directly or through an assigned role) with approve permission for the entity's area, and with ALREADY_DECIDED if someone else got there first. Read-only (`mode:read`) tokens are refused.
testplanit_reviews_list List the review requests assigned to the authenticated user — the same queue as the Review inbox in the app. This is work assigned TO the caller, so there is no user filter: rows are scoped to the token owner, covering both direct assignment and assignment to a role the caller holds. `view` selects the tab: 'pending' (default) = PENDING requests awaiting the caller's decision, oldest first; 'decided' = requests the caller has already approved / requested changes on / rejected, most recently decided first. Filters: projectId, entityType (CASE | RUN | SESSION). Each row carries the polymorphic subject resolved to entityName (plus entityDeleted — a request can outlive its subject), the workflow transition being requested as transition:{from,to}, the requester, assignedTo:{via:'USER'|'ROLE',...}, requestNote (the requester's submit-time prose, plain text), and — on the decided view — decision:{status,comment,decidedBy,decidedAt}. Use entityType+entityId with testplanit_cases_get / testplanit_test_runs_get / testplanit_sessions_get to inspect what is being reviewed. Requests from projects with the review workflow turned off are excluded, and `reviewFeatureEnabled:false` distinguishes 'the feature is off system-wide' from 'nothing is assigned to you'. Cursor pagination via the `cursor` returned in `nextCursor`. Pass a row's `id` to testplanit_reviews_decide to record a decision.
testplanit_folders_update Update a folder (partial). Provide name to rename; provide parentId to reparent (use null to move to root). Returns the updated folder with breadcrumb + case count.
testplanit_folders_list List the folder tree for a project. Every serialized node carries its accurate direct caseCount (soft-deleted excluded) and hasChildren. `depth` controls how many levels below the roots are inlined (default 2; pass 'all' for the entire tree) — nodes at the cut with children beyond it get children:null + truncated:true, so an empty leaf (children:[], hasChildren:false) is never confusable with an unfetched subtree.
testplanit_folders_delete Soft-delete a folder by id. Folders with cases or sub-folders cannot be deleted; the tool returns a structured error in that case. Returns { id, isDeleted: true } on success.
testplanit_folders_create Create a folder under a parent (omit parentId for root). Resolves the active repository automatically. Returns the created folder with breadcrumb + case count.
testplanit_folders_get Fetch a single folder by id with parent breadcrumb, children, cases summary (capped at 100; use testplanit_cases_list for full pagination), and case counts: caseCount is the folder's DIRECT live-case count, caseCountRecursive covers the entire subtree, and automatedCaseCount / automatedCaseCountRecursive split out automated-flagged cases. Children carry the same recursive fields plus hasChildren. For counts under other filters use testplanit_cases_count with folderId + includeDescendants.
testplanit_session_results_list List session results with cursor pagination. Filters: sessionId, createdById (executor user id, string), statusId. NOTE: there is NO testCase filter — `SessionResults` has no `testCaseId` column (sessions are exploratory and not case-linked; R4 / Pitfall 4 / SESS-03 schema gap). Each row carries denormalized status / createdBy (the executor) / session summary, plus resultData extracted to plain text via ProseMirror. Ordered by createdAt DESC then id DESC (BL-04 deterministic). isDeleted:false defense-in-depth. (per SESS-03)
testplanit_session_results_get Fetch a single session result by id. Returns the result with denormalized status, createdBy (the executor — sessions don't have a separate executor field; per D7-13), session summary, attachments, linked issues, and customFields (resultFieldValues denormalized to a flat name-keyed dict per A3 — same shape as caseFieldValues for read). NOTE: no step-level results — sessions are exploratory and don't have ordered steps (per D7-13). resultData is surfaced as plain text (resultDataText) via ProseMirror extraction. (per SESS-04 / D7-13)
testplanit_test_run_results_list List test run results across runs — BOTH manual results (TestRunResults, source \"TestRun\") AND automated results ingested from JUnit/TestNG/xUnit/NUnit/MSTest/Mocha/Cucumber reports (JUnitTestResult, source \"JUnit\"; automated runs — testRunType != REGULAR — store results ONLY there). Filters: runId, caseIds (RepositoryCases ids — back-half of EXEC-06 chain when paired with testplanit_cases_list({issueId})), executedById (user id, string; for JUnit rows this is the importer), statusId, from/to (executedAt date range, ISO 8601), source (\"TestRun\" | \"JUnit\" to restrict to one kind). Merged ordering: executedAt DESC then id DESC per source (BL-04 deterministic + D7-02 latest-first). Cursor is OPAQUE — pass back `nextCursor` verbatim (compound `tr:<id>|ju:<id>` string; bare numbers are accepted as legacy TestRunResults cursors). Each row carries `source`, denormalized status/executedBy, top-level repositoryCase + testRun, and (TestRun rows only) testRunCase. For detail call testplanit_test_run_results_get with the row id AND its `source`. (per EXEC-04 / D7-01)
testplanit_test_run_results_create Submit a test result for a case in a run. Atomically creates the result record and updates the run case's current status. The attempt number is auto-incremented — no need to track it manually. Optional `fieldValues` records custom Result Field entries alongside the result; pass either the field's display name or its system name. The server rejects the submission if the case's template marks any Result Field required and `fieldValues` does not supply each one. Returns the full denormalized result (same shape as testplanit_test_run_results_get).
testplanit_test_run_results_get Fetch a single test run result. Pass the `source` from the results_list row: \"TestRun\" (default — manual TestRunResults) or \"JUnit\" (automated results ingested from JUnit/TestNG/xUnit/… reports; automated runs store results ONLY there — the two id spaces are independent). TestRun detail inlines step-level results (drill-down model — D7-09): stepText / expectedResultText (ProseMirror plain-text), status (stepStatus relation per R2 / schema.zmodel:2437), notes, evidence (Json as-is per D7-08), attachments, linked issues, plus customFields (resultFieldValues denormalized) and the parent testRunCase summary. JUnit detail carries junitType (PASSED/FAILURE/ERROR/SKIPPED), message, content (stack trace), systemOut/systemErr, time/assertions/file/line, suite + repositoryCase + testRun, and attachments; JUnit steps are stored per-CASE (no per-result FK) so they are not inlined. (per EXEC-05 / D7-07)
whoami Debug: returns the authenticated user (token owner) and scopes.
testplanit_code_repositories_list List the project's code-repository configuration. Returns ProjectCodeRepositoryConfig rows with the underlying CodeRepository denormalized inline (id, name, provider, status, lastTestedAt, settings stripped to a public-key allow-list, and a derived web url). The secrets column is never returned. One row per project today (schema enforces @@unique([projectId])).
testplanit_sessions_update Update an existing exploratory test session. All fields except sessionId are optional — only supplied fields are changed. Tags use full-replacement semantics (the new list replaces all current tags). Returns the full denormalized session detail.
testplanit_sessions_list List sessions scoped to a project. Filters: stateId, isCompleted, createdById (user id, string), from/to (createdAt date range, ISO 8601). Cursor pagination ordered by createdAt DESC then id DESC (BL-04 deterministic). Each row carries denormalized state/createdBy/assignedTo/template/configuration/milestone/tags + mission and note extracted to plain text. (per SESS-01)
testplanit_sessions_create Create a new exploratory test session in a project. Resolves the default template and SESSIONS workflow state automatically. Returns the full denormalized session detail (same shape as testplanit_sessions_get).
testplanit_sessions_findings_list List findings — Issue rows linked to a session OR retrieve the cross-cutting view of one issue across sessions/results. Provide EXACTLY ONE of `sessionId` or `issueId` (XOR; per D7-11).
testplanit_sessions_get Fetch a single session by id with denormalized header (state/createdBy/assignedTo/template/configuration/milestone/tags), session-level linked issues, mission and note (ProseMirror -> plain text), customFields (flat name-keyed dict from sessionFieldValues), and sessionResults inline up to 100 (D7-12 — when more exist, `truncated: true` is set; call testplanit_session_results_list({sessionId}) with cursor pagination for the full set). (per SESS-02 / D7-12)
testplanit_repository_case_links_list List RepositoryCaseLink rows for traversing the manual-↔-imported case linkage graph. Provide exactly one of caseId (bidirectional — matches both caseA.id and caseB.id), caseAId (one-way originating side), or caseBId (one-way destination side). Optional linkType narrows to SAME_TEST_DIFFERENT_SOURCE or DEPENDS_ON. In caseId mode each row carries an inline `otherCase` (the side opposite the queried id); in caseAId/caseBId mode each row carries both caseA and caseB. Project scope is enforced via the underlying access policy on caseA.project — the link row itself is not project-scoped.
testplanit_milestones_update Update an existing milestone. All fields except milestoneId are optional — only supplied fields are changed. Setting isCompleted: true also records completedAt; setting isStarted: true records startedAt. Returns the updated milestone header.
testplanit_milestones_list List milestones scoped to a project, with POOLED statusCounts rollup inline on every row (merged across linked test runs AND linked sessions). Filters: isCompleted, isStarted, milestoneTypeId, createdById (single string), from/to (ISO 8601 createdAt range), parentId (null = root-only, number = direct children of, omitted = all). Cursor pagination via the `cursor` returned in `nextCursor`. Each row carries denormalized milestoneType {id,name}, creator, parentId, directChildrenCount, totalDescendants (recursive CTE), commentCount inline, plus statusCounts:[{id,name,count}] + untested + total (counts SUM to total). Two batched groupBy calls per page (testRunCases + sessionResults), never per-row. No icon field — schema only carries an icon class identifier, deliberately dropped for v1.
testplanit_milestones_create Create a new milestone in a project. Use testplanit_milestone_types_list to get available milestoneTypeId values. Optionally nest under a parent milestone. Returns the created milestone header.
testplanit_milestone_types_list List all milestone types assigned to a project (via the MilestoneTypesAssignment junction). Returns {items:[{id, name, isDefault}]} sorted by name asc. No cursor pagination — types-per-project is small (~10 typical). Every milestones_list row + milestones_get response also denormalizes milestoneType:{id,name} inline; this tool exists for full-catalog or filter-picker use cases. No icon field — schema only carries an icon class identifier, deliberately dropped for v1.
testplanit_milestones_get Fetch a single Milestone by id with denormalized header (milestoneType {id,name}, creator, parentId, directChildrenCount, commentCount, totalDescendants), note + docs (ProseMirror plain text), pooled statusCounts (testRuns + sessions merged) + untested + total, and three inlined linked arrays — linkedTestRuns (cap 250), linkedSessions (cap 100), children (cap 100, 1-level deep with totalDescendants). When an array overflows the cap the response carries truncated.<key>: true; the rest is reachable via testplanit_milestones_list with parentId, or testplanit_test_runs_list. No icon field — schema only carries an icon class identifier, deliberately dropped for v1.
testplanit_issues_list List issues scoped to a project. Filters: externalSystem (IntegrationProvider enum), integrationId, status (TestPlanIt-side, exact match), externalStatus (externally-synced label, case-insensitive substring). Cursor pagination ordered by createdAt DESC then id DESC (deterministic). Each row carries linkedCaseCount via _count for the dominant fan-out signal. The assignee filter is intentionally omitted — Issue has no native assigneeId column; assignee data lives in Issue.data: Json (provider-shaped) and is not addressable here.
testplanit_issues_list_links Walk the issue ↔ X graph in either direction. Outbound mode: provide issueId + target (one of: cases, sessions, sessionResults, testRuns, testRunResults, testRunStepResults) to get the linked counterpart rows. Inbound mode: provide exactly one of caseId, sessionId, sessionResultId, runId, runResultId, runStepResultId to get the issues linked to that row. Cursor pagination, deterministic orderBy.
testplanit_issues_link Link one or more entities (test cases, sessions, test runs, etc.) to an issue. Supports batch linking: pass up to 100 entity IDs in a single call. Use entityType='testCase' to link test cases created with testplanit_cases_create. Returns a count of linked entities.
testplanit_issues_unlink Remove links between one or more entities and an issue. Supports batch: pass up to 100 entity IDs in a single call. Returns a count of unlinked entities.
testplanit_issues_find_by_key Resolve an Issue by (externalKey, externalSystem, projectId). Schema enforces @@unique([externalId, integrationId]) — externalKey is NOT globally unique, so when two integrations of the same provider share a key in the same project, the response shape is { issues: [...], multipleMatches: true, hint: 'Pass integrationId to disambiguate.' } instead of { issue, multipleMatches: false }. Pass optional integrationId to skip the multi-match path. Internal-only issues (where Issue.integrationId IS NULL) are NOT addressable by this tool — agents reach those via testplanit_issues_list.
testplanit_issues_get Fetch a single Issue by id with its full denormalized header (including ProseMirror note rendered to plain text, integration, createdBy) and three inlined linked arrays — linkedCases, linkedSessions, linkedTestRuns — each capped at 100. When an array is over-capacity the response carries truncated.<key>: true and the rest are reachable via testplanit_cases_list({ issueId }) or testplanit_issues_list_links with the appropriate target.
testplanit_projects_list List projects accessible to the authenticated token. Returns { id, name } pairs only — minimal context-disambiguation surface for agents.
testplanit_tags_list List tags with usage counts per repository case / test run / session. Optionally pass projectId to scope counts to that project; without it, counts are global. Tags are global; this tool is read-only.
testplanit_cases_update Update a test case (partial). Provide only the fields to change: name, automated, steps (replaces all current steps), tags (replaces the tag set), customFields (upserts each), stateName, folderId. Returns the full denormalized case detail (CASE-02 shape). (per D-05 / CASE-04)
testplanit_cases_list List test cases scoped to a project. Filters: folderId (exact folder; add includeDescendants:true to cover the folder's entire subtree), tagIds, name (case-insensitive substring), stateId, customField, issueId (linked Issue numeric id — see issues_list for resolution from external keys). customField takes {name} to match cases that have the field set, or {name, value} to match by value (Dropdown/Multi-Select accept the option name or id; an unknown field name or option returns a validation error rather than unfiltered results). Cursor pagination via the `cursor` returned in `nextCursor`. (per CASE-01 + EXEC-06 chain via D7-03)
testplanit_cases_delete Soft-delete a test case by id. Sets isDeleted=true so the case is hidden from subsequent list/get queries. Returns { id, isDeleted: true }. (per D-05 / CASE-05)
testplanit_cases_create Create a new test case in a project + folder. Resolves the active repository, default template, and CASES workflow state automatically. Returns the full denormalized case detail (CASE-02 shape). (per D-05 / CASE-03)
testplanit_cases_create_many Create many test cases in one operation, far faster than calling testplanit_cases_create per case. Each case supports the same fields as a single create (name, steps, tags, customFields) plus optional per-case folderId / stateName overriding the batch defaults. Cases are grouped by their effective (folder, state) and each group is persisted in one transaction. Returns a per-case results array so partial failures are visible (each entry has status 'success' with caseId, or 'error' with a message). customFields must belong to the chosen template — out-of-template fields are reported as a per-case error, not silently dropped.
testplanit_cases_get Fetch a single test case by id with full denormalized details — steps (plain text), custom fields (flat dict), folder breadcrumb, tags, linked issues, linked automated tests. (per D-05 / CASE-02)
testplanit_cases_count Count test cases server-side — the aggregation companion to testplanit_cases_list. Accepts the same filters (folderId + includeDescendants for whole-subtree scope, tagIds, name, stateId, customField, issueId, automated, source, repositoryId, hasNeverExecuted, hasAutomatedResults, automatedResultSince, noAutomatedResultSince, updatedAfter/updatedBefore, creatorIds, from/to) and returns {total} — never paginate cases_list just to count.
testplanit_cases_generate_script Generate a QuickScript — an AI-authored automation test script — from one or more stored test cases. The server resolves the project's export template and, when a code repository is connected to the project, pulls repository context so the script follows the repo's existing framework, fixtures, and page objects (this takes precedence over the template's framework — intended behavior). When no LLM integration is configured or generation fails, each file falls back to the deterministic template render (generatedBy=\"template\"). Requires QuickScript to be enabled for the project. Returns the generated file(s) plus the resolved framework/language/fileExtension for naming.
testplanit_templates_list List a project's enabled templates, each with the case fields it defines (displayName, systemName, type, required). Use this to pick a templateId for testplanit_cases_create / testplanit_cases_create_many and to learn which customFields a template accepts. Read-only.
testplanit_runs_cases_remove Remove test cases from a run (the counterpart of testplanit_runs_cases_add — takes the same repository case IDs, not TestRunCase row IDs). Soft-deletes the run's junction rows AND their recorded results, step results, and iterations, matching the web UI's remove-cases flow; the repository cases themselves are untouched. Re-adding a removed case via testplanit_runs_cases_add restores it. Fails when the run is completed or composition-locked (execution started) — the case set is frozen. Returns the number of cases removed and the run's remaining case count.
testplanit_runs_update Update an existing test run. Supports renaming, changing workflow state, swapping milestone or configuration, and replacing tags. All fields except runId are optional — only supplied fields are changed. Returns the full denormalized run detail (same shape as testplanit_test_runs_get).
testplanit_test_runs_list List test runs scoped to a project, with statusCounts rollup inline on every row (per D7-06). Filters: stateId, isCompleted, createdById (user id, string), from/to (createdAt date range, ISO 8601). Cursor pagination via the `cursor` returned in `nextCursor`. Each row carries denormalized project/state/createdBy/configuration/milestone/tags/issues + testRunType + `statusCounts: [{id,name,count}]` + `untested` + `total` (counts SUM to total). Rollup source depends on testRunType: REGULAR runs count TestRunCases by execution status (`untested` = cases with no result, `total` = case count); automated runs (JUNIT/TESTNG/XUNIT/NUNIT/MSTEST/MOCHA/CUCUMBER) count imported JUnit result ROWS by status — attempts, so retries count once per row and `total` is the attempt count, matching the web UI. Rollups are fetched via batched groupBy calls per page, NOT per-row — agents can list 100 runs in one tool call without N+1 cost. (per EXEC-01 / D7-06)
testplanit_runs_cases_add Add repository test cases to an existing test run. Cases are appended after any existing run cases (order preserved). Case IDs already in the run are silently skipped; a case previously removed from the run is restored (at its former position, with its prior results still soft-deleted). Returns a confirmation with the number of cases requested, restored, and the updated total.
testplanit_test_runs_cases_list List the test cases assigned to a specific run. Filters: isCompleted, statusId, assignedToId (user id, string). Cursor pagination ordered by `order` then `id`. Cases removed from the run (soft-deleted rows) are excluded; restore one via testplanit_runs_cases_add. Each row carries `latestResult` (most recent executedAt) inline — a union of manual TestRunResults and run-scoped automated JUnit results, discriminated by `source` (\"TestRun\" | \"JUnit\"); pass both the id and source to testplanit_test_run_results_get. Row `status` is the junction row's status when set, falling back to latestResult.status; the statusId FILTER matches only the junction column, which automated runs (testRunType != REGULAR) never set — filter automated results by status via testplanit_test_run_results_list({runId, statusId}) instead. (per EXEC-03 / D7-05)
testplanit_runs_create Create a new test run in a project. Optionally add test cases, link to a milestone or configuration, set tags, and pick a workflow state by name. Returns the full denormalized run detail (same shape as testplanit_test_runs_get). State defaults to the first RUNS-scope state by order when omitted. testRunType is always REGULAR.
testplanit_runs_cases_update Edit a test case's row WITHIN a run: assign/unassign a tester (assignedToId) or move it to a new position (order). Identify the row by testRunCaseId — the `id` on rows from testplanit_test_runs_cases_list / testplanit_test_runs_get testCases (NOT repositoryCase.id). All fields except testRunCaseId are optional — only supplied fields change. To change the case's execution status, submit a result via testplanit_test_run_results_create instead; to edit the case's CONTENT (name, steps, tags, custom fields), use testplanit_cases_update with the row's repositoryCase.id. Fails when the parent run is completed (row is frozen); `order` is additionally frozen while the run is composition-locked (execution started). Returns the updated row in the same shape as testplanit_test_runs_cases_list.
Permissions 5
network medium filesystem low shell high database medium env_vars low