# MCP workflow evaluations
Use the Instance AI evaluation harness to score workflows built through the MCP
module: build each workflow by driving the instance MCP server with Claude, then
run the normal `eval:instance-ai` verifier against it.
For the full framework documentation, argument reference, outputs, and
troubleshooting, use the [Instance AI workflow evaluation README](../../../../../@n8n/instance-ai/evaluations/README.md).
## Two ways to run
- **Fused — recommended.** `eval:instance-ai --build-via-mcp` builds each
workflow through the MCP server and verifies it in the **same run**, across one
or more lanes. One command, one LangSmith experiment (`LANGSMITH_API_KEY`
required) — and it's what CI (`ci-mcp-evals.yml`) runs. See
[Building via MCP](../../../../../@n8n/instance-ai/evaluations/README.md#building-via-mcp---build-via-mcp).
- **Two-phase — decoupled (documented below).** `eval:build-mcp-manifest` writes
a manifest of workflow IDs, then `eval:instance-ai --prebuilt-workflows` scores
them. Reach for this when you want to build once and evaluate many times, A/B
different builders or models, or keep the built cohort around.
The rest of this guide covers the two-phase flow:
1. Start a local n8n instance with Instance AI enabled.
2. Generate an MCP-built workflow cohort with `eval:build-mcp-manifest`.
3. Evaluate that manifest with `eval:instance-ai --prebuilt-workflows`.
```text
+-------------------------+ writes +--------------------------+ reads +------------------------+
| eval:build-mcp-manifest | ------------------> | output-dir/manifest.json | ----------------> | eval:instance-ai |
| --output-dir
| | | | --prebuilt-workflows |
| | | | | /manifest.json |
+-------------------------+ +--------------------------+ +------------------------+
```
## Environment
Use a local `.env` file instead of exporting secrets directly in the terminal.
For example, create `.env.mcp-evals` at the repo root:
```env
N8N_ENABLED_MODULES=instance-ai
N8N_AI_ENABLED=true
N8N_INSTANCE_AI_MODEL=anthropic/claude-sonnet-4-5-20250929
N8N_INSTANCE_AI_MODEL_API_KEY=sk-ant-...
N8N_EVAL_EMAIL=eval@example.com
N8N_EVAL_PASSWORD=...
CONTEXT7_API_KEY=ctx7sk-...
# Optional — record this run to LangSmith. Pair with --dataset and
# --baseline-prefix (see "Record runs in LangSmith") so MCP runs never touch
# the Instance AI dataset or baseline.
# LANGSMITH_API_KEY=ls__...
# LANGSMITH_ENDPOINT=https://api.smith.langchain.com
```
Leave `N8N_AI_ANTHROPIC_KEY` unset unless you are intentionally testing that
path.
To record MCP eval runs in LangSmith, always pass a dedicated `--dataset` and
`--baseline-prefix` so the run lands in its own dataset and only compares
against MCP baselines — never the Instance AI dataset or the
`instance-ai-baseline-` experiments. See
[Record runs in LangSmith](#record-runs-in-langsmith).
Start n8n with the same env file in watch mode:
```bash
dotenvx run -f .env.mcp-evals -- pnpm dev:ai
```
Alternatively, use a regular local n8n instance started with `pnpm start` and
its existing users and projects:
```bash
dotenvx run -f .env.mcp-evals -- pnpm start
```
When using an existing local instance, set `N8N_EVAL_EMAIL` and
`N8N_EVAL_PASSWORD` to a user that can log in to that instance. If you pass
`--project-id`, use a project available to that user.
On a fresh local DB, create or seed the owner account with the email and
password from `.env.mcp-evals`. The full setup is documented in the linked
Instance AI evaluation README.
## Record runs in LangSmith
LangSmith recording is opt-in via `LANGSMITH_API_KEY` and reuses the Instance AI
eval pipeline (the `--prebuilt-workflows` path records exactly like a normal
run). To keep MCP runs isolated from the Instance AI dataset and baselines,
always pass a dedicated `--dataset` and `--baseline-prefix`:
```bash
LANGSMITH_API_KEY=ls__... dotenvx run -f .env.mcp-evals -- \
pnpm --filter @n8n/instance-ai run eval:instance-ai \
--base-url http://localhost:5678 \
--source langtracer --suite n8n-workflows \
--tier mcp \
--prebuilt-workflows /tmp/n8n-mcp-cohort/manifest.json \
--dataset mcp-workflow-evals \
--baseline-prefix mcp-baseline- \
--iterations 3 \
--concurrency 3 \
--output-dir /tmp/n8n-mcp-cohort-eval
```
- `--dataset` syncs only the `--tier mcp` examples into a dataset of its own;
the Instance AI `instance-ai-workflow-evals` dataset is never written to.
- `--baseline-prefix` scopes regression comparison to MCP baselines. Until an
MCP baseline exists the comparison is simply skipped — an MCP run is never
compared against `instance-ai-baseline-`.
- `--dataset` and `--baseline-prefix` are the two halves of isolation — pass
both together. Overriding only one logs a **partial isolation** warning, since
the run would still write to / compare against shared Instance AI data.
- The dataset and experiments are created in the workspace your
`LANGSMITH_API_KEY` belongs to. Use a personal key/workspace to avoid
cluttering the shared team workspace.
### Create or refresh a baseline
Refresh the MCP baseline the same way as the Instance AI one, but with the MCP
dataset and prefix (high `--iterations` for a low-noise reference point):
```bash
LANGSMITH_API_KEY=ls__... dotenvx run -f .env.mcp-evals -- \
pnpm --filter @n8n/instance-ai run eval:instance-ai \
--base-url http://localhost:5678 \
--source langtracer --suite n8n-workflows \
--tier mcp \
--prebuilt-workflows /tmp/n8n-mcp-cohort/manifest.json \
--dataset mcp-workflow-evals \
--baseline-prefix mcp-baseline- \
--experiment-name mcp-baseline \
--iterations 10
```
LangSmith appends a random suffix (e.g. `mcp-baseline-7abc1234`); the most
recently started `mcp-baseline-` experiment becomes the comparison target on the
next MCP run. The comparison is skipped on the baseline-creation run itself.
### Check baselines in LangSmith
A baseline is not a special LangSmith object — it's just an experiment whose name
starts with `--baseline-prefix` (`mcp-baseline-`). To find them, open your
workspace → **Datasets & Experiments** → `mcp-workflow-evals` → the
**Experiments** list: baselines are the rows named `mcp-baseline-`,
while normal runs (e.g. `local--`) are not. With no `mcp-baseline-*`
experiment yet, every run's comparison is skipped.
Two comparison views, kept separate:
- **Native LangSmith compare** — select two or more experiments in the dataset's
**Experiments** list and click **Compare** for a side-by-side metrics view
(the `?selectedSessions=…` link printed at the start of a run opens this view).
- **This tool's regression report** — computed locally by the eval CLI (reading
the latest baseline's runs), written to `eval-pr-comment.md` and
`eval-results.json`, and printed to the console. It is not rendered back inside
LangSmith.
## Prerequisites
- `claude` CLI is installed and authenticated.
- `~/.claude.json` contains an MCP server entry for the target n8n instance.
- The MCP server name used below, for example `n8n-local`, matches that Claude
config entry.
- The n8n instance is reachable at the URL configured in the MCP server block.
## Run size
Use the same size for generation and evaluation unless you have a reason to do
otherwise:
- `-n` on `eval:build-mcp-manifest` is the number of workflows
to generate per test case.
- `-j` on `eval:build-mcp-manifest` is the number of
concurrent Claude Code build processes.
- `--iterations` on `eval:instance-ai` is the number of verifier runs per test
case.
- `--concurrency` on `eval:instance-ai` is the number of concurrent eval
workers.
`-n` should usually match `eval:instance-ai --iterations` so each verifier
iteration can use a different prebuilt workflow. The concurrency flags affect
speed and load, not confidence, but keeping them aligned makes the recipes easy
to copy.
| Recipe | Build flags | Eval flags | Use |
|--------|-------------|------------|-----|
| Quick eval | `-n 1 -j 1` | `--iterations 1 --concurrency 1` | Fast smoke test |
| Medium confidence | `-n 3 -j 3` | `--iterations 3 --concurrency 3` | Local comparison run |
| Best confidence | `-n 5 -j 5` | `--iterations 5 --concurrency 5` | Higher-confidence batch |
## Run the `mcp` tier
The repository ships a curated `mcp` tier: test cases that can be scored fairly
when a workflow is built from a single MCP prompt (see
[Adding a case to the `mcp` tier](#adding-a-case-to-the-mcp-tier)). Pass
`--tier mcp` to **both** steps so the build and the eval select the same cases
and stay in lockstep:
```bash
# 1. Build the cohort — only mcp-tier cases
dotenvx run -f .env.mcp-evals -- pnpm --filter @n8n/instance-ai run eval:build-mcp-manifest \
--source langtracer --suite n8n-workflows \
--tier mcp \
-n 3 \
-j 3 \
--output-dir /tmp/n8n-mcp-cohort \
--mcp-server n8n-local
# 2. Evaluate the same cohort
dotenvx run -f .env.mcp-evals -- pnpm --filter @n8n/instance-ai run eval:instance-ai \
--base-url http://localhost:5678 \
--source langtracer --suite n8n-workflows \
--tier mcp \
--prebuilt-workflows /tmp/n8n-mcp-cohort/manifest.json \
--iterations 3 \
--concurrency 3 \
--output-dir /tmp/n8n-mcp-cohort-eval
```
`--tier` filters by the `datasets` array in each test case. Build the whole tier
so every case the eval selects is present in the manifest — any case missing
from the manifest falls back to the normal Instance AI build path.
`--filter` and `--exclude` are `eval:instance-ai` flags only — combine them with
`--tier` to narrow the eval further. `eval:build-mcp-manifest` does not accept
them; narrow its build set with positional slugs instead (e.g. append
`contact-form-automation` to build just that case).
## Generate a cohort
Without `--tier` or a positional slug, the build covers every test case in the
source. The commands below pull the real corpus from LangTracer
(`--source langtracer --suite n8n-workflows`); disk mode (`data/workflows/`)
only holds the seeded carve-out cases. From the repo root, build five
workflows per test case with five concurrent Claude Code builds:
```bash
dotenvx run -f .env.mcp-evals -- pnpm --filter @n8n/instance-ai run eval:build-mcp-manifest \
--source langtracer --suite n8n-workflows \
-n 5 \
-j 5 \
--output-dir /tmp/n8n-mcp-cohort \
--mcp-server n8n-local
```
The output directory contains:
- `manifest.json` for `eval:instance-ai --prebuilt-workflows`.
- `manifest-stats.json` with aggregate build cost, turns, and duration.
- `logs/` with one build log per generated workflow.
To use a different Claude model for the build step, add `--model`:
```bash
dotenvx run -f .env.mcp-evals -- pnpm --filter @n8n/instance-ai run eval:build-mcp-manifest \
-n 5 \
-j 5 \
--output-dir /tmp/n8n-mcp-cohort \
--mcp-server n8n-local \
--source langtracer --suite n8n-workflows \
--model claude-opus-4-5
```
## Generate from another Claude project
Use this when Claude should load skills or settings from a different working
directory while the evaluation script still runs from the n8n repo:
```bash
dotenvx run -f /path/to/n8n/.env.mcp-evals -- pnpm --dir /path/to/n8n \
--filter @n8n/instance-ai run eval:build-mcp-manifest \
-n 5 \
-j 5 \
--mcp-server n8n-local \
--project-id \
--source langtracer --suite n8n-workflows \
--build-cwd /path/to/mcp-workspace \
--output-dir /tmp/n8n-mcp-skills-cohort
```
Useful flags:
- `--project-id` tells the builder to create workflows in a specific n8n
project when the MCP server supports project-aware workflow creation, such as
the official n8n MCP server. Other MCP servers may ignore it and create
workflows in the user's personal project.
- `--source langtracer --suite ` pulls the test cases from LangTracer
(the corpus home). Alternatively `--workflow-dir` points at a directory of
case JSONs for disk mode.
- `--build-cwd` controls the working directory for the Claude subprocess, which
affects the Claude project config and skills that get loaded.
## Generate one test case
Pass the test-case slug as a positional argument. The slug is the case name in
the LangTracer suite (for disk mode, the JSON filename without `.json`).
```bash
dotenvx run -f .env.mcp-evals -- pnpm --filter @n8n/instance-ai run eval:build-mcp-manifest \
-n 5 \
-j 5 \
--output-dir /tmp/n8n-mcp-contact-form \
--mcp-server n8n-local \
--source langtracer --suite n8n-workflows \
contact-form-automation
```
When evaluating a single generated test case, always pass the same slug to
`eval:instance-ai --filter`. Otherwise, test cases missing from the manifest
fall back to the normal Instance AI build path, which can produce confusing
setup errors if that path is not configured for the run.
## Evaluate a cohort
Evaluate all workflows from a generated manifest with five iterations and five
concurrent eval workers:
```bash
dotenvx run -f .env.mcp-evals -- pnpm --filter @n8n/instance-ai run eval:instance-ai \
--base-url http://localhost:5678 \
--source langtracer --suite n8n-workflows \
--prebuilt-workflows /tmp/n8n-mcp-cohort/manifest.json \
--iterations 5 \
--concurrency 5 \
--output-dir /tmp/n8n-mcp-cohort-eval
```
The eval CLI reads `N8N_EVAL_EMAIL`, `N8N_EVAL_PASSWORD`, model keys, and
Context7 settings from `.env.mcp-evals`.
## Evaluate one generated test case
Use `--filter` with the same slug that was used during manifest generation:
```bash
dotenvx run -f .env.mcp-evals -- pnpm --filter @n8n/instance-ai run eval:instance-ai \
--base-url http://localhost:5678 \
--source langtracer --suite n8n-workflows \
--prebuilt-workflows /tmp/n8n-mcp-contact-form/manifest.json \
--filter contact-form-automation \
--iterations 5 \
--concurrency 5 \
--output-dir /tmp/n8n-mcp-contact-form-eval
```
## Adding a case to the `mcp` tier
Test cases live in the LangTracer suite `n8n-workflows` — see
[Adding test cases](../../../../../@n8n/instance-ai/evaluations/README.md#adding-test-cases)
for the full schema and authoring flow. To include a case in the MCP cohort,
add `"mcp"` to its `datasets` array (in LangTracer for existing cases; in the
local JSON before pushing for new ones); both `--tier mcp` steps then pick it
up, no registration:
```json
"datasets": ["mcp", "full"]
```
The same mechanism defines any custom grouping: use a distinct value (e.g.
`"datasets": ["mcp-regression"]`) and pass it to `--tier mcp-regression`.
### What makes a case MCP-evaluable
The MCP client builds each workflow from a **single flattened prompt** (the
conversation's user turns concatenated) and the eval scores the **resulting
workflow**. A good `mcp` case is therefore:
- **Fair when flattened** — single-turn, or multi-turn with _additive_ user
turns that refine earlier ones. Avoid _contradictory_ turns ("actually, use X
instead of Y") and `[bracketed]` stage directions (deliberate
withholding/timing): both make the flattened prompt ambiguous or misleading.
- **Scored on the artifact** — the `executionScenarios` success criteria and any
`outcomeExpectations` must be judgeable from the workflow JSON.
`processExpectations` (assertions about the build _conversation_) are
**skipped** in MCP runs because there is no transcript, so they must not be a
case's only signal.
### Build preconditions
Some workflows need the MCP client to perform setup before or while creating the
workflow. Such cases can still live in the tier — a build failure is itself a
useful signal — but expect them to fail until the client and instance support
the precondition:
- **Data tables** must be created in-session: workflow creation rejects
references to a data table that does not exist in the target project.
- **MCP registry nodes** require the registry to be available and seeded on the
instance.
## Cleanup
Prebuilt workflows are not deleted by default, so the same manifest can be
reused for comparison runs. If the workflows are throwaway, add
`--delete-prebuilt-workflows` to the `eval:instance-ai` command. It only deletes
workflows that were successfully used in the run.
### Data tables and other build leftovers
The manifest records only workflow IDs, so anything an MCP build creates on the
side is invisible to `--delete-prebuilt-workflows` and is left behind. The most
common case is **data tables**: cases like `workflow-data-table` need the MCP
client to create a data table before the workflow can reference it (see
[Build preconditions](#build-preconditions)), and those tables are not tracked
in the manifest.
For a clean slate, run the cohort against a throwaway project with `--project-id`
and delete the whole project afterwards — that removes the workflows and the data
tables they created in one step.