Pipelines: mcptest pipe and tools call chaining
mcptest pipe runs a declarative multi-step tool-call pipeline: each step calls one tool, and later steps reference earlier steps' output. It is the heavier sibling of mcptest tools call, which chains at most one extra call from the command line. Both share the same reference language and the same cumulative budget controls.
This page is the reference for the pipeline YAML grammar, the ${...} reference language, the when: guard, the on_error policy, and the budget flags. For the imperative single-call form, see the tools call section at the end.
When to reach for which
- A two-step chain you type once:
mcptest tools call ... --then .... - A three-or-more-step chain, anything with a conditional step, or a chain worth saving and replaying: a pipeline file run with
mcptest pipe.
Pipeline file
# search-then-update.yml
pipe:
- id: find
tool: search_users
args:
query: ${var.USER_QUERY}
- id: details
tool: get_user_details
args:
user_id: ${find.users[0].id}
when: ${find.users} | length > 0
- id: notify
tool: send_email
args:
to: ${details.email}
subject: "Account review"
body: "Hi ${details.name}, your account is up for review."
on_error: continue
Run it:
mcptest pipe search-then-update.yml --url http://localhost:8000/mcp \
--var USER_QUERY=alice
Step fields
The top-level key is pipe:, an ordered list of steps. Each step:
| Field | Required | Description |
|---|---|---|
id | yes | Unique step identifier. Later steps reference this step's output as ${<id>.field}. |
tool | yes | Tool name to call on the target server. |
args | no | Argument object. Values may carry ${...} references; the whole object defaults to {}. |
when | no | Guard expression. The step runs only when it evaluates truthy (see below). |
on_error | no | Failure policy: stop (default), continue, or retry:<n>. |
References (${...})
A ${...} reference resolves against one of three namespaces:
${<step_id>.field.path}reads an earlier step's output by JSONPath-style path (${find.users[0].id}). The step must have already run.${var.NAME}reads a--var NAME=valuepair supplied on the command line.${env.NAME}readsNAMEfrom the process environment.
Type is preserved. A string that is exactly one reference ("${find.users}") resolves to the referenced JSON value, so an array stays an array. A reference embedded in surrounding text ("Hi ${details.name}") interpolates as text.
Resolution errors are explicit and fail the run: an undefined ${var.X} ("pass it with --var"), an unset ${env.X}, or a ${step.field} pointing at a step that has not run yet.
when: guard
when: is an expression that decides whether a step runs. It combines a ${...} reference with an optional filter, for example:
when: ${find.users} | length > 0
The step is skipped (not failed) when the guard is falsy. A skipped step produces no output, so a later ${skipped_id.field} reference errors, which is the intended signal that the branch did not run.
on_error policy
| Value | Behavior |
|---|---|
stop (default) | The pipeline stops on the first failing step and exits non-zero, naming the offending step. |
continue | The failure is recorded and the pipeline moves to the next step. |
retry:<n> | The step is retried up to n times, then falls back to stop. |
Budgets
Budget is a property of the whole pipeline, not a single call. The same flags apply to mcptest pipe and (for the aggregate cost) to mcptest tools call.
| Flag | Effect |
|---|---|
--max-cost <USD> | Aggregate USD ceiling across every step. The run stops when the running total plus the next step's projected cost would exceed it. |
--max-tokens <N> | Aggregate ceiling on LLM tokens (input + output) across steps. |
--max-cost-per-call <USD> | Per-step ceiling, layered on top of the aggregate cap. |
--max-duration <DURATION> | Wall-clock ceiling (30s, 2m, 1h); whichever cap trips first wins. |
--on-budget-exceeded <MODE> | stop (default) fails fast on breach; continue runs to completion then exits non-zero; warn runs to completion and exits 0 with a stderr warning. |
--pricing-table <PATH> | Override the bundled pricing.yaml. Lets you update provider prices without waiting for a release. |
Cost projection
--dry-run --estimate-cost prints the planned execution and a projected cost without making any paid call. Steps that replay from a cassette are free and are counted as such in the estimate.
mcptest pipe my-pipeline.yml --dry-run --estimate-cost
Per-step breakdown
Every run emits per-step cost and token telemetry alongside the result, so a CI dashboard can chart spend over time. --format json includes the full per-step trace.
tools call chaining
For a single extra call, mcptest tools call carries the same reference ideas inline, which removes the need for jq between two calls:
mcptest tools call search --url "$URL" --args '{"query":"alice"}' \
--bind user_id=$.results[0].id \
--then fetch_user --then-arg user_id=:user_id \
--select $.email
| Flag | Effect |
|---|---|
--args <JSON> | Literal args object. |
--args-from-stdin | Read the entire args object from stdin JSON. |
--arg NAME=$.path | Extract a value from stdin JSON by JSONPath into the named arg. Repeatable. |
--bind NAME=$.path | Capture a value from this call's output for the chained step. Repeatable. |
--then <TOOL> | Chain one more call. |
--then-arg NAME=VALUE | Argument for the --then step. NAME=:bound references a --bind capture. |
--select $.path | Project the output before printing. |
--max-cost <USD> | Aggregate cap across both calls. |
--then is deliberately limited to a single chained step. Reach for mcptest pipe once a chain has three or more steps or needs a when: branch.
Exit codes
mcptest pipe exits 0 when every step succeeds (or is skipped by when:), and non-zero when a step fails under on_error: stop, when a budget cap is breached under the default --on-budget-exceeded stop, or when the pipeline file fails schema validation.
See also
- cli-reference.md: the flag tables for
pipeandtools call. - cassettes.md: record a pipeline once and replay it for free.
examples/pipe-search-then-update.yml: the worked example above.schemas/pipe/v0.json: the published pipeline schema.