Skip to content
axxbetadocs
v0.2.26next
GitHub

MCP servers

View .mdOpen in Claude

Call the tools of MCP servers (Model Context Protocol) as an AI assistant calls them, and check their results, with the tools’ own schemas as the contract. Read their resources and request their prompts.

Given the parcels mcp server with the following properties:
| url | http://localhost:8400/mcp |
When the track_parcel tool is called on the parcels mcp server with the following arguments:
| reference | PX-MCP-9101 |
Then the track_parcel tool's result is not an error
And the track_parcel tool's result has the following properties:
| status | OUT_FOR_DELIVERY |
  • Over stdio or streamable HTTP: a command runs the server for the scenario, which ends it when it ends; a url reaches it. The session agrees on the protocol version with the server: 2026-07-28, or an earlier one the server speaks.
  • Run it as its clients do: a server assistants start over stdio is the scenario’s command, like a command-line tool, and not a service in axx.yaml; a server that runs on its own over HTTP is a service, and the url reaches it.
  • A tool’s schemas are the contract: its arguments take the types its input schema gives them (10115 is text for a string), and are checked against it before the call; its structured result is checked against its output schema.
  • Two kinds of failure: a tool’s result that is an error (the tool ran, and says what went wrong), and a call the server refused (an unknown tool, invalid parameters), with its JSON-RPC error code.

Findings have keys and levels, like the REST pack’s OpenAPI findings:

Key Found when
validation.arguments.schema.<keyword> the arguments break the tool’s input schema (required, type, enum…)
validation.result.schema.<keyword> the structured result breaks the tool’s output schema
validation.result.missing a tool with an output schema answered no structured result
  • Levels: ERROR (or FAIL) fails the step, WARN and INFO log the finding, IGNORE drops it. Every finding is an ERROR unless packs.mcp.levels in axx.yaml, or the scenario’s the MCP validation levels are:, says otherwise.
  • Secrets stay secret: ${env:..} values in the properties are masked in logs and failures, and ${token:..} names a token of the scenario.

Add it to a project with axx pack add mcp.

In axx.yaml, under packs.mcp:

Setting Takes Values
levels Validation key -> ERROR (or FAIL), WARN, INFO or IGNORE, for every scenario. names and values
Given the {word} mcp server with the following properties:
| property | value |

The table may start with the row naming its columns, as above, or go straight to its rows.

Register an MCP server under a name, and connect to it: over stdio (a command the scenario runs, and ends when it ends) or over streamable HTTP (a url). The session agrees on the protocol version with the server.

  • A server its clients start over stdio is the scenario’s command, as assistants run it, not a service in axx.yaml; a server that runs on its own over HTTP is a service, which the url reaches.
Parameter Takes For example
{word} one word, with no spaces parcels, PX-4101
Property Takes Default
command the command that runs the server over stdio, split into words without a shell, like docker compose exec -T app parcels mcp
url the server’s streamable HTTP endpoint
dir the folder the command runs in, relative to the project a folder of the scenario's own
env.<NAME> an environment variable of the command, such as env.PARCELS_DB_URL
header.<name> a header sent with every request to the url, such as header.Authorization
timeout how long connecting, or a call, may take, like 10s 30s
protocol version the MCP version to speak, to check that the server still answers an earlier one, like 2025-06-18 the latest the server speaks

A command or a url is required. Values are expanded (${env:..}, ${sys:..}, ${token:..}); what ${env:..} references expand to is masked everywhere.

Example:

Given the parcels mcp server with the following properties:
| url | http://localhost:8400/mcp |
Given the parcels mcp server with the following properties:
| command | ${sys:parcels.mcp} |
| dir | ../infra |

Since 0.1.5.

Then the {word} mcp server has the {word} tool[[ with the following properties:]]

Check that the server lists the tool, and that it has the table’s properties: what an AI assistant reads before it calls the tool, like its description and its hints.

Parameter Takes For example
{word} one word, with no spaces parcels, PX-4101

A row is a path into the tool as the server lists it (description, annotations.readOnlyHint, inputSchema.required[0]) and its value.

Variants, the parts in [[...]] said or left out:

  • the {word} mcp server has the {word} tool
  • the {word} mcp server has the {word} tool with the following properties:

Example:

Then the parcels mcp server has the track_parcel tool
Then the parcels mcp server has the track_parcel tool with the following properties:
| title | Track a parcel |
| annotations.readOnlyHint | true |

Since 0.1.5.

Then the {word} mcp server does not have the {word} tool

Check that the server does not list the tool: one it must not offer an AI assistant.

Parameter Takes For example
{word} one word, with no spaces parcels, PX-4101

Example:

Then the parcels mcp server does not have the cancel_parcel tool

Since 0.1.5.

When the {word} tool is called on the {word} mcp server[[ with the following arguments:]]

Call the tool, with the table’s arguments when the step has one. The arguments are checked against the tool’s input schema before they are sent, and its structured result against its output schema.

  • A result that is an error does not fail the step: check it with the {word} tool's result is an error.
  • Nor does a call the server refuses: check it with the {word} tool's call failed with the error code {int}.
Parameter Takes For example
{word} one word, with no spaces parcels, PX-4101

Each row is an argument, or a path into one (address.postcode), and its value: a value takes the type the tool’s input schema gives its argument, double quotes make it text, null is null and undefined leaves it out.

Variants, the parts in [[...]] said or left out:

  • the {word} tool is called on the {word} mcp server
  • the {word} tool is called on the {word} mcp server with the following arguments:

Example:

When the track_parcel tool is called on the parcels mcp server with the following arguments:
| reference | PX-MCP-9101 |

Since 0.1.5.

When the {word} tool is called on the {word} mcp server with the {filepath} arguments

Call the tool with the arguments of a file of the project: a JSON or YAML object of the arguments’ names and values. They are checked against the tool’s input schema before they are sent.

Parameter Takes For example
{word} one word, with no spaces parcels, PX-4101
{filepath} a file of the project: a path relative to the feature file’s directory, the resources directories or axx.yaml’s directory (looked for in that order), or an absolute path, quoted when it has a space seeds/parcels.yaml, kafka/scan-delivered.json

Example:

When the hold_parcel tool is called on the parcels mcp server with the mcp/hold-parcel.json arguments

Since 0.1.5.

Then the {word} tool's result[[ on the {word} mcp server]] is not an error

Check that the tool answered, with a result that is not an error.

The result is the tool’s latest in the scenario, on the server the step names, or on the only server the scenario called the tool on.

Parameter Takes For example
{word} one word, with no spaces parcels, PX-4101

Variants, the parts in [[...]] said or left out:

  • the {word} tool's result is not an error
  • the {word} tool's result on the {word} mcp server is not an error

Example:

Then the track_parcel tool's result is not an error

Since 0.1.5.

Then the {word} tool's result[[ on the {word} mcp server]] is an error

Check that the tool answered with a result that is an error: the tool ran, and says what went wrong, for the assistant to tell the user or try again. Check what it says with contains.

The result is the tool’s latest in the scenario, on the server the step names, or on the only server the scenario called the tool on.

Parameter Takes For example
{word} one word, with no spaces parcels, PX-4101

Variants, the parts in [[...]] said or left out:

  • the {word} tool's result is an error
  • the {word} tool's result on the {word} mcp server is an error

Example:

Then the hold_parcel tool's result is an error

Since 0.1.5.

Then the {word} tool's result[[ on the {word} mcp server]] contains {string}

Check that the tool’s result has a text: in its content (text, and embedded resources’ text), or in its structured content’s JSON.

The result is the tool’s latest in the scenario, on the server the step names, or on the only server the scenario called the tool on.

Parameter Takes For example
{word} one word, with no spaces parcels, PX-4101
{string} text in double or single quotes, which the step leaves out "Get a quote", 'Express'

Variants, the parts in [[...]] said or left out:

  • the {word} tool's result contains {string}
  • the {word} tool's result on the {word} mcp server contains {string}

Example:

Then the hold_parcel tool's result contains 'already out for delivery'

Since 0.1.5.

Then the {word} tool's result[[ on the {word} mcp server]] has the following properties:
| property | value |

The table may start with the row naming its columns, as above, or go straight to its rows.

Check the tool’s structured result: its structured content, or the JSON of its text for a tool that answers JSON as text.

  • Paths are dotted (lastScan.location) or JSONPath; null and undefined work as in the other property steps.
  • The result is the tool’s latest in the scenario, on the server the step names, or on the only server the scenario called the tool on.
Parameter Takes For example
{word} one word, with no spaces parcels, PX-4101

A row is a path into the structured result, and its value.

Variants, the parts in [[...]] said or left out:

  • the {word} tool's result has the following properties:
  • the {word} tool's result on the {word} mcp server has the following properties:

Example:

Then the track_parcel tool's result has the following properties:
| status | OUT_FOR_DELIVERY |
| lastLocation | Leipzig |

Since 0.1.5.

Then the {word} tool's call[[ on the {word} mcp server]] failed with the error code {int}

Check that the server refused the call, with a JSON-RPC error of that code: -32602 for a tool it does not have or invalid parameters, -32603 for an internal error.

The result is the tool’s latest in the scenario, on the server the step names, or on the only server the scenario called the tool on.

Parameter Takes For example
{word} one word, with no spaces parcels, PX-4101
{int} a whole number 200, 3

Variants, the parts in [[...]] said or left out:

  • the {word} tool's call failed with the error code {int}
  • the {word} tool's call on the {word} mcp server failed with the error code {int}

Example:

Then the cancel_parcel tool's call failed with the error code -32602

Since 0.1.5.

When the {word} resource is read from the {word} mcp server

Read a resource of the server, by its URI.

Parameter Takes For example
{word} one word, with no spaces parcels, PX-4101

Example:

When the parcels://PX-MCP-9103/label resource is read from the parcels mcp server

Since 0.1.5.

Then the {word} resource contains {string}

Check that the text of the resource, as the scenario last read it, has a text.

Parameter Takes For example
{word} one word, with no spaces parcels, PX-4101
{string} text in double or single quotes, which the step leaves out "Get a quote", 'Express'

Example:

Then the parcels://PX-MCP-9103/label resource contains 'PX-MCP-9103'

Since 0.1.5.

Then the {word} resource has the following properties:
| property | value |

The table may start with the row naming its columns, as above, or go straight to its rows.

Check the resource, as the scenario last read it, when its text is JSON.

Parameter Takes For example
{word} one word, with no spaces parcels, PX-4101

A row is a path into the resource’s JSON, and its value.

Example:

Then the parcels://PX-MCP-9103/label resource has the following properties:
| reference | PX-MCP-9103 |
| service | EXPRESS |

Since 0.1.5.

When the {word} prompt is requested from the {word} mcp server[[ with the following arguments:]]

Request a prompt of the server, with the table’s arguments when the step has one: the messages it offers an AI assistant.

Parameter Takes For example
{word} one word, with no spaces parcels, PX-4101

A row is an argument of the prompt, and its value, as text.

Variants, the parts in [[...]] said or left out:

  • the {word} prompt is requested from the {word} mcp server
  • the {word} prompt is requested from the {word} mcp server with the following arguments:

Example:

When the delivery_update prompt is requested from the parcels mcp server with the following arguments:
| reference | PX-MCP-9104 |

Since 0.1.5.

Then the {word} prompt contains {string}

Check that the text of the prompt’s messages, as the scenario last requested it, has a text.

Parameter Takes For example
{word} one word, with no spaces parcels, PX-4101
{string} text in double or single quotes, which the step leaves out "Get a quote", 'Express'

Example:

Then the delivery_update prompt contains 'PX-MCP-9104'

Since 0.1.5.

Given the MCP validation levels are:
| validation key | level |

The table may start with the row naming its columns, as above, or go straight to its rows.

Relax, for this scenario, the checks of tools’ arguments and results against their schemas.

A row is a validation key and its level: ERROR (or FAIL), WARN, INFO or IGNORE. The key must be one the pack reports, or a prefix of such keys, like validation.arguments: any other fails the step, which names the closest keys.

Example:

Given the MCP validation levels are:
| validation.arguments.schema.required | IGNORE |

Since 0.1.5.