MCP tests
An MCP test connects to your MCP server on a schedule the way an agent would, and fails when
the server stops identifying itself, stops advertising a tool, or a tool call stops returning
a result you accept. An agent creates one through the create_api_test MCP tool with type
MCP; in the app, pick type MCP server in the test editor.
What a test checks
Section titled “What a test checks”Each attempt is one short MCP session, in the order an agent would run it: the server
identifies itself (the handshake), the test reads the tools the server advertises
(tools/list), and the test calls one tool (tools/call). Check depth sets how far into that
session a healthy server has to get. The tiers are cumulative; each checks everything the one
before it checks, and the test sends only what its tier needs.
| Tier | App label | What is sent | Passes when |
|---|---|---|---|
HANDSHAKE | Handshake | The handshake | The server identifies itself. |
TOOLS_LIST | Tools list | The handshake, then tools/list | The server lists every tool you expect. |
TOOL_CALL | Tool call | The handshake, tools/list, then one tools/call | The tool you name returns a result you accept. |
How the handshake works depends on the protocol generation: the current, stateless revision
answers server/discover, older revisions answer initialize, the oldest over a GET event
stream. The test identifies itself as fixter-monitoring. See
MCP protocol revisions.
Every tier also checks Max latency when it is set, and a pinned protocol revision. To check
that a server keeps accepting several revisions, create one pinned Handshake test per
revision; see
Watching several revisions.
Protocol revision
Section titled “Protocol revision”Protocol revision defaults to Newest supported: the test runs against the newest revision
the server speaks and moves with it when the server changes, and a move is not a failure. Pick a
dated revision instead to pin the test; it then fails when the server stops accepting that
revision. You do not have to know what your server speaks: once the URL looks complete, the
editor detects it (after a short pause in typing, at once on paste or when you leave the field),
sets the picker to Newest supported and shows currently <revision> beside it. The revisions,
detection and pinning are on MCP protocol revisions.
Fields per tier
Section titled “Fields per tier”| Field | MCP parameter | App label | Tier | What it takes |
|---|---|---|---|---|
| URL | mcpUrl | URL | All | An absolute http:// or https:// URL with a host, reachable from the public internet. Any other URL is never sent: the run fails with malformed target URL, so the test cannot be saved. Takes request variables. |
| Credential | mcpCredentialId | Credential | All | None, or a saved credential. See Credentials. |
| Check depth | mcpTier | Check depth | All | HANDSHAKE, TOOLS_LIST or TOOL_CALL. Required. See The three tiers. |
| Protocol revision | mcpProtocolRevision | Protocol revision | All | A dated revision to pin, or NEWEST_SUPPORTED. Optional; omitted, it is NEWEST_SUPPORTED. See Protocol revision. |
| Expected tools | expectedTools | Expected tools | TOOLS_LIST | Tool names the server must advertise. Required for this tier. |
| Tool name | toolName | Tool name | TOOL_CALL | The tool to call. Required. |
| Tool arguments | toolArgumentsJson | Tool arguments (JSON) | TOOL_CALL | The arguments, as a JSON object. Required: a tool that takes none needs {}. Takes request variables. |
| Result validation | mcpResultValidationTier, mcpResultValidationSample | Result validation, Result sample | TOOL_CALL | The same five tiers as an HTTP body. Optional; None checks nothing. |
| Max latency | mcpMaxLatencyMillis | Max latency (ms) | All | Optional; left empty, speed is not checked. |
An MCP test has no method and no request headers. Request variables such as {{uuid}} apply
to the URL and the tool arguments only, not to the tool name or the expected tools. Values are
minted once per attempt, so a retry gets fresh ones.
The result validation reads the tool result’s structuredContent as JSON when the result has
it, and otherwise the text of its text content items, joined. The tiers then apply exactly as
they do to an HTTP response body: see Body validation.
A result marked isError: true fails the test whatever the result validation says.
Max latency is compared with the attempt’s total time across the session’s requests.
Redirects and headers
Section titled “Redirects and headers”An MCP test follows 301, 302, 303, 307 and 308 redirects, up to five, and never from https to http. Past five, or on a downgrade, it stops following and takes the redirect response itself as the server’s answer. Every hop must be a public address.
Every request of a run carries:
User-Agent: FixterMonitoring/1.0 (+https://fixter.dev).- A
traceparentheader with the run’s trace id, the same on every attempt of the run. See Runs, states and uptime. - The credential’s headers, and its query parameters merged into the URL, when the test has a credential. See Credentials.
What a failed run says
Section titled “What a failed run says”A run checks every assertion its tier includes and lists every failure; none stops the others.
| Assertion | Fails when | The run says |
|---|---|---|
mcp.initialize | The server did not identify itself (every tier). | MCP server did not identify itself |
mcp.tools.contains | An expected tool is not advertised, one failure per tool (TOOLS_LIST). | Tool <name> is missing |
mcp.tool.isError | The tool call returned an error result (TOOL_CALL). | Tool <toolName> returned an error |
mcp.protocol.revision | The test is pinned and the server stops accepting that revision. | Server no longer speaks protocol <pin> |
maxLatencyMillis | The attempt took longer than Max latency (every tier). | Took <actual> ms, limit <limit> ms |
A failed result validation reads as it does for an HTTP body, for example
Response body is missing expected fields; the sentences say “Response body” for a tool result
too.
When nothing at the URL identified itself as an MCP server on any revision, the run fails
mcp.initialize with the message the target did not identify itself as an MCP server.
Once the server has identified itself, a JSON-RPC error in reply to tools/list leaves no tool
names, so every expected tool reads as missing. In a Tool call test it also reads as
Tool <toolName> returned an error, even though the tool was never called. A JSON-RPC error in
reply to tools/call reads the same. A plain error status with no JSON-RPC reply fails the run
with MCP server rejected <method>, for example MCP server rejected tools/list.
Example
Section titled “Example”Two tests on an orders MCP server, which assumes a Bearer token credential named
Orders MCP token already exists. The first checks that two tools are advertised; the second
calls one of them. In the app, Expected tools is shown only for Tools list, so the two
checks are two tests.
| Field | First test | Second test |
|---|---|---|
Name | Orders MCP tools | Orders MCP search |
Type | MCP server | MCP server |
URL | https://mcp.example.com/mcp | https://mcp.example.com/mcp |
Credential | Orders MCP token | Orders MCP token |
Protocol revision | Newest supported | Newest supported |
Check depth | Tools list | Tool call |
Expected tools | search_orders, get_order | |
Tool name | search_orders | |
Max latency | 2000 | 2000 |
Result validation | Contains fields |
The second test’s Tool arguments (JSON):
{"query": "{{uuid}}"}Its Result sample:
{"results": []}Each attempt searches for a fresh {{uuid}}, so the call never depends on data left by an
earlier run. The sample asks for a results array; an empty sample array accepts any array, so
{"results": [], "total": 0} passes.
If the server drops get_order, the first test fails with Tool get_order is missing. If the
search tool starts returning an error result, the second fails with
Tool search_orders returned an error. A session that took 2400 ms fails either test with
Took 2400 ms, limit 2000 ms.
An agent can fold both checks into one test: create_api_test with tier TOOL_CALL also
accepts expectedTools, and the run then checks the names as well as the call.
Create API test runs the test once before saving it, and saves it only if that run passes.