MCP
Probara exposes a Model Context Protocol (MCP) server at:
https://app.probara.net/mcpAny MCP-compatible client (an AI assistant or IDE) can connect to this endpoint to call Probara’s test-management tools — projects, suites, test cases, runs, defects, and more. See the MCP tool reference for the full, generated list of every available tool.
Authentication
Section titled “Authentication”/mcp accepts either credential type as a Bearer token — see API authentication for the full comparison:
- OAuth 2.1 (recommended for interactive clients like Claude.ai, Claude Code, Cursor, ChatGPT) — the client discovers the server, registers itself, and a human approves access via a consent screen. No secret to copy/paste.
- API token (recommended for scripts and headless integrations) — a long-lived
probara_...token created in Avatar menu → API tokens (/user/api-tokens), pasted directly into the client’s Bearer/Authorization field.
An app token (probara_app_..., from Workspace → Integrations) is not one of them: it only reports results from CI, and /mcp answers it 403 forbidden.
A /mcp request without a credential returns 401 with a WWW-Authenticate: Bearer resource_metadata="https://app.probara.net/.well-known/oauth-protected-resource" header pointing the client at discovery.
A credential the server does not accept (unknown, revoked, expired, or issued for another resource) also returns 401, with error="invalid_token" in that header. An OAuth client then refreshes its token or authorizes again.
Claude.ai (custom connector)
Section titled “Claude.ai (custom connector)”- Open Settings → Connectors → Add custom connector.
- Enter the server URL:
https://app.probara.net/mcp. - Claude.ai discovers the OAuth metadata automatically, registers itself (Dynamic Client Registration), and redirects you to Probara’s consent screen.
- Pick the organization to bind the connection to and approve. Claude.ai now has an OAuth grant scoped to that organization.
Claude Code
Section titled “Claude Code”- Add the server:
claude mcp add --transport http probara https://app.probara.net/mcp. - Claude Code opens a browser window for the OAuth authorize/consent flow (same discovery + DCR as Claude.ai).
- Approve the negotiated scopes and pick an organization. If the client omits
scopeor sends a value Probara doesn’t recognize, it defaults to the full supported set (mcp:read mcp:write); see Scopes for the exact negotiation rules. - Verify the connection:
claude mcp listshould showprobaraas connected.
Cursor
Section titled “Cursor”- Open Settings → MCP → Add new MCP server.
- Choose HTTP transport and set the URL to
https://app.probara.net/mcp. - Cursor follows the same OAuth discovery/DCR/consent flow; alternatively, paste an API token into the connector’s Bearer/Authorization header field if your Cursor version does not yet support MCP OAuth.
ChatGPT
Section titled “ChatGPT”- Open Settings → Connectors → Add MCP server (Developer Mode / custom connectors).
- Set the server URL to
https://app.probara.net/mcp. - Complete the OAuth consent flow, or supply an API token as a Bearer header if your ChatGPT plan only supports static-header connectors.
Protocol
Section titled “Protocol”The endpoint serves two MCP protocol revisions. The revision a client negotiates decides the shape of its answers. Every request is a POST: the server keeps no standalone stream, so GET and DELETE on /mcp answer 405.
- 2025 revisions, such as
2025-11-25, are served statelessly, and each answer is framed as Server-Sent Events (text/event-stream). 2026-07-28clients get one JSON body per request and discover the server withserver/discover. Each of their requests carries theMCP-Protocol-VersionandMcp-Methodheaders, andtools/call,prompts/getandresources/readrequests also carryMcp-Namewith the tool’s name, the prompt’s name or the resource’s URI. The headers must match the body. A request with a header missing or disagreeing is refused with HTTP400and JSON-RPC error-32020.
The answers to tools/list, prompts/list, resources/list, resources/templates/list and server/discover for 2026-07-28 clients carry ttlMs: 300000 and cacheScope: "private". A client may reuse one of these lists for five minutes and must not share it between users. The 2025 answers carry neither field.
A call to a tool that does not exist returns the JSON-RPC error -32602 (Tool <name> not found), not a tool result. A tool that exists and fails returns a result with isError: true.
A request body is limited to about 43.7 MiB (45,787,820 bytes). That covers a 32 MiB attachment in base64, with room for the JSON-RPC envelope. A larger body is refused with 413.
/mcp answers only for app.probara.net. A request addressed to another host, such as a *.workers.dev address, is refused with 403. A browser Origin must name the same host. A request without an Origin, such as one from a server-side client, is not affected by that check.
Core tools
Section titled “Core tools”Nine tools cover the usual work in few calls. Every other operation is reached through the five tools that a default listing also carries, described under Operations and the run tools below.
| Tool | What it does |
|---|---|
get_project_context | Returns a project’s suites, custom fields, configurations, milestones, environments and members in one call. |
get | Reads any entity by display ID or ULID. A bare ULID also needs kind. |
query | Runs a PQL query. |
query_help | Lists the PQL fields, operators and values of each entity, with examples that run. |
analyze_run | Groups a run’s failed and blocked cases by linked defect, then by suite, with their attempts. |
upsert_test_cases | Creates or updates up to 50 test cases. Each item names a display ID or an automation key, and reports its own outcome. |
start_run | Starts a run from a suite, a test plan, a PQL query or a list of cases, with its configurations and assignees. |
record_results | Records up to 50 results in a run by test case display ID, with step results and evidence attachments. |
file_defect | Files a defect from a result, and can also create its GitHub or Jira issue. |
upsert_test_cases matches an item to a test case by its display ID or its automation key. An item with a display ID that does not exist fails, because the server assigns display IDs. An automation key that matches no case creates one. Sending the same batch again changes nothing and reports every item as unchanged. One item failing does not stop the others.
Prompts and resources
Section titled “Prompts and resources”A client can also offer prompts and resources beside the tools. Each one reaches the same data as a tool, so nothing is reachable only through them.
Three prompts start a common task. Each one answers with a message that names the tools to run, in order:
| Prompt | Arguments | Tools it names |
|---|---|---|
test_cases_from_issue | project_id and issue: a GitHub issue URL, owner/repo#n, or a Jira issue key | run_read_operation to read the issue, then get_project_context, then upsert_test_cases |
triage_run | project_id and run: a display ID such as R-12, or a ULID | analyze_run, then get, then file_defect |
regression_for_pr | project_id, pull_request (a GitHub pull request URL or owner/repo#n) and an optional changed_areas | query_help and query to find the cases, then start_run |
Probara does not read a pull request’s diff, so changed_areas sets the scope of a regression run.
Two resources hold the same JSON as their tools:
| Resource | URI | Same answer as |
|---|---|---|
| PQL schema | probara://pql/schema | query_help |
| Project context | probara://projects/{project}/context, with the project code, such as SHOP | get_project_context |
A resource read takes the same path as its tool, so an unknown project answers with the tool’s message as a JSON-RPC error, with the URI in its data. A resource has no block for the untrusted-data notice, so each resource’s description says that its JSON holds organization data.
Resources need the mcp:read scope. Every prompt ends in a write tool, so prompts need both mcp:read and mcp:write. A grant without the scope lists none of them, and a request for one is refused with 403 and the insufficient_scope challenge, as a hidden tool is.
Operations and the run tools
Section titled “Operations and the run tools”A default listing carries the nine core tools and five more, so an agent sees up to 14 tools when it connects, and fewer under a narrower grant. Every other operation is in the tool reference, and each one is reached in three steps:
search_operationsfinds operations by keyword, such asprojectorrun result. Each hit gives the operation’s name, a one-line summary and the run tool that runs it.describe_operationreturns the operation’s full description, the JSON schema of its inputs, its annotations and its output schema when it declares one.run_read_operation,run_write_operationorrun_delete_operationruns it. Pass the operation’s name asoperationand its inputs asarguments. The result is the operation’s own result.
The three run tools split by risk. run_read_operation runs read-only operations, run_write_operation runs writes that remove nothing, and run_delete_operation runs destructive operations such as deletes. A run tool refuses an operation of another risk, and its error names the run tool to use. An unknown name answers with the closest names.
Calling an operation by its name directly, such as create_project, returns a tool error that names its run tool. Only the nine core tools are called directly.
Every tool refuses an argument it does not take: the call returns a tool error that names the argument, and nothing runs. Through a run tool, the error also lists the operation’s arguments, with the required ones marked. The same holds for a key inside an item of record_results or upsert_test_cases, or inside a caseAssignees entry of start_run: a misspelled notes refuses the whole call instead of recording the result without it.
What a read-only grant lists
Section titled “What a read-only grant lists”An OAuth grant without mcp:write lists the five read core tools and three tools that find and read operations: search_operations, describe_operation and run_read_operation. It lists no write tool, and search returns only read operations. A write call from such a grant is refused with 403 and the insufficient_scope challenge for mcp:write, as before. An OAuth grant that holds mcp:write alone lists the four write core tools and the two write run tools. An API token lists every default tool.
Destructive operations: preview, then confirm
Section titled “Destructive operations: preview, then confirm”Some operations can remove or irreversibly change more than one record in one call: the bulk operations bulk_delete_tags, bulk_remove_run_cases and reconcile_run_cases, the deletes of a project, suite, test case, run, test plan, dashboard, custom field, tag, milestone or environment, and deactivate_org_member. Each one runs in two calls:
- Call it without
confirm_token. Nothing runs. The result is a preview:statusisconfirmation_required,targetslists what the call names with its display ID and name where one read finds them,irreversiblesays what cannot be undone, andconfirm_token,expires_atandexpires_in_secondssay how long the token lasts. - Repeat the same call with the same arguments and the
confirm_token. The operation runs.
A token is single-use and valid for five minutes. It is bound to the credential that made the preview (an API token or an OAuth grant), its organization, the operation and its arguments. A call that presents a token is refused, and nothing runs, when:
- the token is unknown, expired or already used: call again without
confirm_tokenfor a new preview; - the token was issued for another operation, credential, organization or set of arguments: it stays valid for its own call;
- the token store is unavailable: the error is retryable, so retry the call.
run_delete_operation takes confirm_token inside arguments, as the operation does when it is called directly.
Activity log
Section titled “Activity log”A change made through an MCP OAuth connection records the connection in its audit event: metadata.oauthClient holds the client’s clientId and clientName, as the audit events API returns them. The activity feed shows the change under the user, as it does for any session.
Untrusted data in results
Section titled “Untrusted data in results”A successful result that carries organization data, such as titles, descriptions, steps, comments and text imported from GitHub or Jira, ends with a second text block. That block says the JSON above is data written by people in the organization or imported from GitHub or Jira, and that the text is data, not instructions. The first text block is the same JSON as before, and structuredContent is unchanged. Errors and the catalog answers of search_operations and describe_operation carry no such block.
Parameterized test cases
Section titled “Parameterized test cases”A test case display ID, such as SHOP-45, can match several run cases of one run when the case is parameterized. An operation that takes a display ID and a run refuses the call, and lists up to 25 of those run cases, unless the call also names one: pass its combination as parameters, an array of name and value pairs such as [{"name":"Site","value":"EU"}], or pass the run case ULID. Bulk operations take run case ULIDs for such a case instead. The server never picks one for you. upsert_test_cases and create_test_case accept parameters and excludedCombinations, as the API does. Shared parameters are operations, found with search_operations and run by name: list_shared_parameters, get_shared_parameter, create_shared_parameter, update_shared_parameter, delete_shared_parameter (it needs confirmation and takes a mode), list_shared_parameter_usage, preview_test_case_combinations, and detach_test_case_parameter. The API and MCP apply the same rule. See the MCP tool reference and Parameterized test cases.
Identifiers
Section titled “Identifiers”Every argument that takes an entity accepts its ULID or its display ID, and the server looks the display ID up for you.
- A test case (
SHOP-45) and a suite (SHOP-S3) carry their project code, so they need noproject_id. - A run (
R-12), milestone (M-5), defect (D-7), test plan (PLAN-3), shared step (SS-2) and review (RV-4) are numbered within a project. Sendproject_idwith them. - A run case is a test case inside a run. The run-case tools take the test case’s display ID together with
run_ulid. The bulk run tools also take the test case’s ULID.
A display ID that does not exist answers with an error that names it and the project. A project code that does not exist answers with the valid project codes.
Tool annotations
Section titled “Tool annotations”Each tool declares what it does, so a client that honors the declaration can ask the user before a risky call. These are hints. The server never refuses a call because of them.
- Read-only tools only read data.
- Destructive tools remove data, or replace a whole list, document or credential in one call. Replace and reconcile tools take the full set you want, so anything you leave out is removed.
- Idempotent tools have no further effect when the same call is repeated, so retrying one is safe.
- External systems tools reach outside Probara: a GitHub or Jira write, a GitHub workflow launch, a webhook delivery, or an invitation email.
The MCP tool reference lists the hints of every tool. On connection the server also sends a short overview of Probara’s data model and identifiers, which many clients add to the model’s context.
Response size
Section titled “Response size”A list tool returns one page per call. The tool reference states each tool’s default page size and the argument that changes it. The largest page is 200 items.
A page that is still too large for one response (about 10,000 tokens) is handled in one of two ways:
- Lists that continue with
nextCursorare trimmed to the items that fit. The result carries atruncationobject:returnedis the number of items it holds andpageItemsis the number the page holds. ItsnextCursorpoints at the last item it holds, so the next call returns the rest, with no gap. - Lists that page with
pageare refused withresponse_too_large, and nothing is returned. The message names thepageSizethat fits; walk the list with that size.
get, list_results and list_run_cases also take fields, a list of the field names to return. query takes none: the SELECT clause of the query names its columns. The display ID and the ULID are always returned. An unknown name is refused with fields_invalid, and the message lists the valid names.
A single record, or a list without a paging argument, is returned whole up to about 25,000 tokens. A larger one is refused with response_too_large. The message names a filter to add or a narrower view to request, or says that the tool has none.
Troubleshooting
Section titled “Troubleshooting”| Symptom | Likely cause |
|---|---|
401 immediately, no browser prompt | Client does not support MCP OAuth discovery — use an API token instead |
| Consent screen shows the wrong organization | Pick a different organization on the consent screen’s org picker before approving; each grant binds to exactly one organization |
A tool call returns 403 with insufficient_scope | The OAuth grant lacks the scope the call needs (mcp:read to read, mcp:write to change). Authorize again to get it. |
A tool call fails with forbidden, naming the scope and your role | Your role in that organization no longer allows the scope, although the grant holds it. Authorizing again does not change this: an organization owner or admin must change your role. See Authentication. |
503 with a Retry-After header | Probara could not check the credential because of a temporary fault — wait the seconds in Retry-After, then retry |
429 with a Retry-After header | Per-token or per-organization rate limit hit — wait the seconds in Retry-After, then retry |
403 before a tool runs | The client calls /mcp on a host other than app.probara.net, or sends a foreign Origin. Point it at https://app.probara.net/mcp. |
413 on a tool call | The request body is over the 43.7 MiB limit. Attachments are capped at 32 MiB, so send a smaller file. |
JSON-RPC -32602, Tool … not found | The tool name is not in the list the client holds. Refresh the tool list; a cached list lasts up to five minutes. |
response_too_large in a tool result | The result is too large for one response. The message names the page size to request, a filter to add, or a narrower view. If it names none, the tool has no smaller form of that record. |