·

TestRail MCP With Cursor

Set up TestRail MCP in Cursor so your AI agent can manage test cases, runs, and results right from your editor.

Cursor's angle on TestRail MCP is proximity to the code that implements the feature being tested — Agent mode has the actual source diff open in the same context as the TestRail conversation, which changes the quality of a specific task: linking automated tests to case IDs and spotting coverage gaps directly against the code you're currently editing, rather than a codebase you're grepping from a separate terminal session. This topic covers connecting TestRail MCP to Cursor Agent mode, linking test code to case IDs, detecting uncovered cases without leaving the IDE, and the specific limitations Cursor's MCP implementation has today.


Connecting TestRail MCP to Cursor Agent Mode

Cursor reads MCP server config from .cursor/mcp.json at the project root, or ~/.cursor/mcp.json for global config across projects. The shape mirrors Claude Code's .mcp.json closely, since both build on the same underlying MCP spec conventions:

{
  "mcpServers": {
    "testrail": {
      "command": "npx",
      "args": ["-y", "@testrail/mcp-server"],
      "env": {
        "TESTRAIL_URL": "https://yourcompany.testrail.io",
        "TESTRAIL_USERNAME": "qa-bot@yourcompany.com",
        "TESTRAIL_API_KEY": "${env:TESTRAIL_API_KEY}",
        "TESTRAIL_PROJECT_ID": "14"
      }
    }
  }
}

Cursor's env var substitution accepts ${env:VAR_NAME} — yet another variant syntax across this MCP ecosystem, so if you're standardizing config across Claude Code, OpenCode, Gemini CLI, and Cursor on the same team, this is genuinely worth a one-page internal cheat sheet rather than trusting anyone to remember four different substitution conventions correctly.

After saving the config, open Cursor's settings panel (Cmd/Ctrl+Shift+J on most builds) → MCP, and confirm testrail shows a green status dot with a tool count. Cursor surfaces individual tool names here too, which is a useful sanity check that the server exposed the full expected set (get_cases, add_case, add_run, add_result_for_case, and so on) rather than a partial list from a version mismatch.

To actually use it, switch to Agent mode (not Ask mode — Ask mode in Cursor is read/chat-only and won't execute MCP tool calls in most current builds). In Agent mode, reference TestRail naturally in the chat:

@testrail: pull case C1050 and show me its current expected results,
then check if tests/e2e/checkout.spec.ts has a test asserting the
same behavior.

Cursor resolves the @testrail reference to the MCP server's tool set and combines it with its native codebase search — this cross-referencing between a TestRail case and the actual local file is the workflow Cursor is best positioned for among the four tools in this module, precisely because the code is already open in the same window.

Tips
- Confirm Agent mode (not Ask mode) before expecting any MCP tool call to actually execute — this is the most common "why isn't it doing anything" support question with Cursor MCP setups.
- Check the tool count shown in Cursor's MCP settings panel against the expected TestRail tool list after any server version bump — a partial tool list usually means a version mismatch between the MCP server and what Cursor cached.
- Keep a one-page internal reference for env var substitution syntax across your team's agent tools (${env:VAR} for Cursor, ${VAR} for Claude Code, {env:VAR} for OpenCode, $VAR for Gemini CLI) — this single mismatch causes more support tickets than anything else in multi-tool TestRail MCP setups.


Linking Automated Test Code to TestRail Case IDs

Because Cursor Agent mode has the file tree and diff context natively, it's well suited to retrofitting TestRail: C{id} annotations onto an existing test suite that doesn't have them yet — a common state for codebases that adopted TestRail after the automated suite already existed.

Look at tests/e2e/checkout.spec.ts. For each test, search TestRail suite 12
for a case with a matching or clearly related title. Where you find a
confident match, add a "// TestRail: C{id}" comment above the test.
Where there's no clear match, add a "// TestRail: NEEDS CASE" comment
and tell me what new case you'd propose.

A representative diff Cursor Agent produced on a real checkout spec file:

// TestRail: C1050
test('declined card shows retry banner, not generic error', async ({ page }) => {
  await submitPayment(page, DECLINED_CARD);
  await expect(page.locator('.retry-banner')).toBeVisible();
});

// TestRail: NEEDS CASE
// Proposed: "Checkout preserves cart contents after payment failure and retry"
test('cart items persist through payment retry flow', async ({ page }) => {
  await submitPayment(page, DECLINED_CARD);
  await retryPayment(page, VALID_CARD);
  await expect(page.locator('.cart-item')).toHaveCount(3);
});

That second annotation is the valuable output — a genuinely reasonable proposed case title for a test that existed in code but had no TestRail counterpart at all. Confidence matching on titles alone is not perfect: on one pass through a larger spec file, Cursor matched a test called 'blocks checkout with empty cart' to a case titled 'Checkout validates cart is not empty before payment' — correct — but also matched an unrelated test called 'clears cart after successful order' to a case about cart abandonment reminders, purely on shared keyword overlap ("cart"). Always spot-check matches on titles that share only generic domain vocabulary; that's where false positives cluster.

After review, bulk-add the newly proposed cases:

POST /index.php?/api/v2/add_case/{section_id}
{
  "title": "Checkout preserves cart contents after payment failure and retry",
  "template_id": 1,
  "type_id": 6,
  "priority_id": 3,
  "custom_steps_separated": [
    {
      "content": "Add 3 items to cart, proceed to checkout, submit a declined card",
      "expected": "Payment fails but cart still shows all 3 original items"
    },
    {
      "content": "Retry payment with a valid card",
      "expected": "Order completes with the original 3 items, no items lost or duplicated"
    }
  ],
  "refs": "auto-detected-gap"
}

Using "auto-detected-gap" as a placeholder refs value (rather than leaving it blank or inventing a fake ticket number) is a small but useful convention — it makes it trivially greppable later which cases originated from this kind of retrofit pass versus a requirement-driven authoring session.

Tips
- Always review title-based case matches manually before trusting them, especially on tests whose names share only generic domain words ("cart," "user," "form") with an unrelated case — that's where false positive matches cluster.
- Use a distinct refs placeholder value (like "auto-detected-gap") for cases created from a retrofit sweep, so you can distinguish them from requirement-driven cases later.
- Run the annotation-retrofit pass file by file for large spec suites rather than the whole tests/ directory at once — matching quality holds up better with a smaller, more legible diff per review.


Detecting Test Cases with No Automation Coverage from Cursor

The inverse direction — finding TestRail cases with no automated test at all — benefits from the same local-codebase proximity, and is a query worth running before any release sign-off, not just as a one-time audit.

Get all cases in suite 12, section "Payment Failures". Search the entire
tests/ directory for "TestRail: C{id}" comments matching those case IDs.
List cases with zero matches. For each, tell me if it looks automatable
(deterministic, no real third-party dependency) or better left manual
(depends on an actual card issuer's real-world timeout behavior, etc.).

A realistic response on a 14-case section:

No automation reference found (6 of 14):

C1044 - "Payment retry after 3 consecutive declines locks card for 15 min"
  -> Automatable. Deterministic, no external dependency if the lockout
     logic is server-side and testable with mocked decline responses.

C1046 - "Checkout under real network packet loss shows retry option"
  -> Better left manual, or requires dedicated network-condition tooling
     (e.g., Toxiproxy) — not a good fit for standard Playwright/Cypress.

C1052 - "International card (non-USD billing address) checkout succeeds"
  -> Automatable, but needs realistic test card fixtures per region —
     flag as a backlog item requiring test data setup first, not a
     quick add.

That triage judgment — automatable vs. genuinely better as manual — is the actually useful output here, more so than the bare list of uncovered case IDs. It turns a raw coverage report into a prioritized backlog: C1044 is a same-sprint automation task, C1052 needs a test-data investment before it's worth automating, and C1046 might reasonably stay manual indefinitely.

Tips
- Ask explicitly for an automatable-vs-manual judgment on every uncovered case, not just a bare list — it's the difference between a report and a backlog.
- Flag cases needing test-data or tooling investment (realistic card fixtures, network-condition simulators) as a distinct bucket from cases that are simply not yet automated but trivially could be.
- Re-run this coverage sweep on a schedule (before each release, or monthly on stable suites) — coverage erodes quietly as new manual cases get added faster than automation catches up.


Known Limitations and Workarounds for TestRail MCP in Cursor

Ask mode silently ignores MCP tool calls in some Cursor versions. If you paste a TestRail-referencing prompt while in Ask mode expecting a live lookup, you'll sometimes get a plausible-sounding answer that Cursor invented from general TestRail knowledge rather than an actual API call — because Ask mode doesn't execute tools at all in some builds. Always confirm you're in Agent mode before trusting any TestRail-specific answer, and treat an oddly generic-sounding response about "your" case as a signal to check the mode.

Long-running MCP sessions across many files can lose the @testrail context thread. On very long Agent sessions spanning many file edits and multiple TestRail lookups, Cursor occasionally needs the @testrail reference re-stated rather than inferring you still mean the same server from three messages back — a minor friction compared to Claude Code's session continuity, worth knowing so you don't assume it dropped the connection when it just dropped the reference.

No built-in dry-run mode for write operations. Unlike explicitly asking Claude Code to "show me the payload before creating," Cursor doesn't have a distinct visual staging step for MCP write calls — the approval dialog shows the tool name and can be expanded to see parameters, but it's less prominent in the UI than a dedicated preview, so it's easy to approve a batch quickly without fully reading a payload. Compensate by explicitly prompting for a preview, same pattern as with the other tools:

Draft the add_case payloads for these 3 cases but don't call the tool yet.
Show me the JSON first.

None of these are severe, but they're the kind of thing that bites a team on day one of adoption and then never gets written down anywhere, so new team members rediscover them independently.

Tips
- Confirm Agent mode before trusting any TestRail-specific response — Ask mode can return plausible-sounding but non-authoritative answers if it doesn't actually invoke the tool.
- Re-state the @testrail reference in long sessions rather than assuming context carries forward indefinitely across many turns and file edits.
- Explicitly request a JSON preview before any write operation — Cursor's approval dialog is functional but easy to click through without fully reading, so build the preview-first habit into your prompts rather than relying on the UI to slow you down.


Tips

Tips
- Use Cursor specifically for the code-proximity tasks — linking tests to case IDs, spotting coverage gaps against the file you're actively editing — where having the source open in the same context beats a separate terminal-based grep.
- Watch the ${env:VAR} substitution syntax specifically when copying MCP config from another tool; it's a distinct variant from Claude Code, OpenCode, and Gemini CLI's conventions.
- Build a preview-before-write habit into prompts, since Cursor doesn't have a dedicated staging UI for MCP write calls the way some other tools do.