Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [2.65.0] - 2026-07-15

### Added

- **`n8n_evaluations` tool — read evaluation test runs.** n8n 2.30 shipped Public API read endpoints for evaluation test runs (n8n-io/n8n#33455); the new consolidated tool exposes them: `action='list_runs'` (paginated, status filter), `action='get_run'` (aggregated metrics and final result), and `action='list_cases'` (per-case inputs/outputs/metrics, default limit 20 — paginate, cases can be large). Requires n8n ≥ 2.30 **and an API key created on 2.30+**: older keys silently lack the `testRun` scopes and get a 403 — the tool's error messages spell this out, and a 404 on a pre-2.30 instance is disambiguated from a wrong workflow/run id via the cached instance version. Read-only; triggering or cancelling runs via API is not yet supported by n8n (upstream n8n-io/n8n#33979 is merged but unreleased) and will be added as new actions when it ships.

## [2.64.1] - 2026-07-14

### Changed
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,7 @@ These tools require `N8N_API_URL` and `N8N_API_KEY` in your configuration.
#### Execution Management
- **`n8n_test_workflow`** - Test/trigger workflow execution (webhook, form, chat)
- **`n8n_executions`** - Unified execution management (list, get, delete)
- **`n8n_evaluations`** - Read evaluation test runs (list runs, aggregated metrics, per-case results; n8n 2.30+)

#### Data Table Management
- **`n8n_manage_datatable`** - Manage n8n data tables and rows (list, get, create, update, delete)
Expand Down
1 change: 1 addition & 0 deletions data/skills/n8n-mcp-tools-expert/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ See [OPERATIONS_GUIDE.md](OPERATIONS_GUIDE.md) for examples.
- n8n_list_workflows, n8n_get_workflow, n8n_delete_workflow
- n8n_test_workflow
- n8n_executions
- n8n_evaluations (n8n 2.30+; read evaluation test runs — API key must be created on 2.30+ for testRun scopes)
- n8n_deploy_template
- n8n_workflow_versions
- n8n_autofix_workflow
Expand Down
48 changes: 48 additions & 0 deletions data/skills/n8n-mcp-tools-expert/WORKFLOW_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -843,6 +843,53 @@ n8n_executions({

---

## n8n_evaluations (EVALUATION TEST RUNS)

**Use when**: Reading evaluation test runs — polling a run started in the editor, comparing metrics across runs, pulling per-case results into a report or dashboard.

Read-only. Requires n8n >= 2.30 **and an API key created on 2.30+** — keys created earlier silently lack the `testRun` scopes, so a 403 means "re-create the API key", not a bug. Runs exist only for workflows with an evaluation trigger that have been run from the n8n editor; triggering runs via the public API is not yet supported by n8n (planned upstream, will arrive as `run`/`cancel` actions).

### List Test Runs
```javascript
n8n_evaluations({
action: "list_runs",
workflowId: "workflow-id",
status: "completed" // new, running, completed, error, cancelled
})
```

### Get a Run (aggregated metrics)
```javascript
n8n_evaluations({
action: "get_run",
workflowId: "workflow-id",
runId: "run-id"
})
// → status, finalResult (success/error/warning), metrics, testCaseCount
// metrics is a flat name → number/boolean map: your custom metrics plus
// n8n's automatic ones (promptTokens, completionTokens, totalTokens, executionTime)
```

### Per-Case Results
```javascript
n8n_evaluations({
action: "list_cases",
workflowId: "workflow-id",
runId: "run-id"
})
// Default limit 20 — per-case inputs/outputs can be large; paginate with
// cursor rather than raising the limit.
// Each case carries an executionId — drill into the underlying execution
// with n8n_executions({action: "get", id: executionId, mode: "error"})
```

**Gotchas**:
- A 404 can mean three things: the instance predates 2.30, the workflowId is wrong, or the runId belongs to a different workflow (the tool's error message disambiguates using the instance version)
- Evaluations are license/quota-gated in n8n — an instance without the feature simply has no runs
- Compare `metrics` across runs of the same workflow to catch prompt/model regressions

---

## Workflow Lifecycle

**Standard pattern**:
Expand Down Expand Up @@ -946,6 +993,7 @@ update → update → update → ... (56s avg between edits)
- `n8n_workflow_versions` - Version control & rollback
- `n8n_test_workflow` - Trigger execution
- `n8n_executions` - Manage executions
- `n8n_evaluations` - Read evaluation test runs (n8n 2.30+, read-only)
- `n8n_manage_datatable` - Data table and row management
- `n8n_manage_credentials` - Credential CRUD + schema discovery
- `n8n_audit_instance` - Security audit (built-in + custom scan)
Expand Down
3 changes: 2 additions & 1 deletion data/skills/using-n8n-mcp-skills/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: using-n8n-mcp-skills
description: Use when building, editing, validating, testing, or debugging an n8n workflow through the n8n-mcp MCP server — designing a flow, configuring a node, writing an expression or Code node, wiring credentials, or fixing one that misbehaves. The entry-point skill for the n8n-mcp-skills pack: it routes you to the right specialist skill, gives working knowledge of every n8n-mcp tool from turn one, and states the rules that keep workflows from breaking in production. Always consult it first on any n8n, workflow, node, or automation task — even a quick one-off, and even when the user names no skill — because n8n's surface drifts between versions and the specialist skills prevent silent failures.
description: "Use when building, editing, validating, testing, or debugging an n8n workflow through the n8n-mcp MCP server — designing a flow, configuring a node, writing an expression or Code node, wiring credentials, or fixing one that misbehaves. The entry-point skill for the n8n-mcp-skills pack: it routes you to the right specialist skill, gives working knowledge of every n8n-mcp tool from turn one, and states the rules that keep workflows from breaking in production. Always consult it first on any n8n, workflow, node, or automation task — even a quick one-off, and even when the user names no skill — because n8n's surface drifts between versions and the specialist skills prevent silent failures."
---

# Using the n8n-mcp Skills
Expand Down Expand Up @@ -130,6 +130,7 @@ closes the gap where a tool's full description isn't loaded until first use.
**Test & run**
- `n8n_test_workflow` — runs real nodes (Code, HTTP, DB writes, sends all fire). Ask the user before running when side effects exist.
- `n8n_executions` — list/inspect executions. **There is no `execute_workflow` tool.**
- `n8n_evaluations` — read evaluation test runs (n8n ≥ 2.30): list runs, aggregated metrics, per-case results. Read-only — runs are started from the n8n editor, not the API; a 403 usually means the API key predates 2.30 (re-create it for the testRun scopes).

**Data, credentials, audit**
- `n8n_manage_datatable` — Data Table CRUD, filtering, dry-run.
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "n8n-mcp",
"version": "2.64.1",
"version": "2.65.0",
"description": "Integration between n8n workflow automation and Model Context Protocol (MCP)",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
146 changes: 146 additions & 0 deletions src/mcp/handlers-n8n-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
ExecutionFilterOptions,
ExecutionMode,
Credential,
TestRunStatus,
} from '../types/n8n-api';
import type { TriggerType, TestWorkflowInput } from '../triggers/types';
import {
Expand Down Expand Up @@ -525,6 +526,25 @@ const listExecutionsSchema = z.object({
includeData: z.boolean().optional(),
});

const listTestRunsSchema = z.object({
workflowId: z.string(),
status: optionalEmptyAware(z.enum(['new', 'running', 'completed', 'error', 'cancelled'])),
limit: z.number().min(1).max(250).optional(),
cursor: optionalEmptyAware(z.string()),
});

const getTestRunSchema = z.object({
workflowId: z.string(),
runId: z.string(),
});

const listTestCasesSchema = z.object({
workflowId: z.string(),
runId: z.string(),
limit: z.number().min(1).max(250).optional(),
cursor: optionalEmptyAware(z.string()),
});

const workflowVersionsSchema = z.object({
mode: z.enum(['list', 'get', 'rollback', 'delete', 'prune']),
workflowId: z.string().optional(),
Expand Down Expand Up @@ -1915,6 +1935,132 @@ export async function handleDeleteExecution(args: unknown, context?: InstanceCon
}
}

// Evaluation Test Run Handlers (n8n >= 2.30)

const TEST_RUN_SCOPE_HINT =
'n8n rejected the request (403). The API key lacks testRun scopes - keys created before n8n 2.30 do not have them; re-create the API key on n8n 2.30+. Other causes: evaluations not licensed on this plan, or the key\'s owner lacks access to this workflow.';

/**
* Builds the error response for the evaluation handlers. Mirrors handleCrudError
* but adds evaluation-specific guidance for the three failure modes that are easy
* to confuse from raw HTTP statuses alone: a pre-2.30 instance, an API key without
* testRun scopes, and a runId that does not belong to the given workflow.
*/
async function handleTestRunError(error: unknown, context?: InstanceContext): Promise<McpToolResponse> {
if (error instanceof z.ZodError) {
return { success: false, error: 'Invalid input', details: { errors: error.errors } };
}
if (error instanceof N8nApiError) {
if (error.statusCode === 403) {
return { success: false, error: TEST_RUN_SCOPE_HINT, code: error.code };
}
if (error.statusCode === 404) {
// A 404 from a pre-2.30 instance means the endpoint doesn't exist, not
// that the ids are wrong. The version cache is usually cold on a fresh
// session, so fetch it (one extra request, failure path only).
const client = getN8nApiClient(context);
let version = client?.getCachedVersionInfo() ?? null;
if (!version && client) {
version = await client.getVersion().catch(() => null);
}
if (version && (version.major < 2 || (version.major === 2 && version.minor < 30))) {
return {
success: false,
error: `The evaluation API requires n8n 2.30 or later; this instance runs ${version.version}. Upgrade the instance to read test runs.`,
code: error.code,
};
}
return {
success: false,
error: 'Workflow or test run not found. A runId must belong to the given workflowId; check both ids.',
code: error.code,
};
Comment on lines +1973 to +1977
}
return { success: false, error: getUserFriendlyErrorMessage(error), code: error.code };
}
return { success: false, error: error instanceof Error ? error.message : 'Unknown error occurred' };
}

export async function handleListTestRuns(args: unknown, context?: InstanceContext): Promise<McpToolResponse> {
try {
const client = ensureApiConfigured(context);
const input = listTestRunsSchema.parse(args || {});

// Send limit only when the caller sets one: n8n's server default is the
// same 100, and pre-2.30 instances (no test-runs routes) reject unknown
// query params before returning the 404 our error mapping explains.
const response = await client.listTestRuns(input.workflowId, {
status: input.status as TestRunStatus | undefined,
limit: input.limit,
cursor: input.cursor,
});

const note = response.data.length === 0
? (input.status
? `No test runs with status '${input.status}' for this workflow.`
: 'No test runs. Runs exist only for workflows with an evaluation trigger that have been executed at least once.')
: response.nextCursor
? 'More test runs available. Use cursor to get next page.'
: undefined;

return {
success: true,
data: {
testRuns: response.data,
returned: response.data.length,
nextCursor: response.nextCursor,
hasMore: !!response.nextCursor,
...(note ? { _note: note } : {})
}
};
} catch (error) {
return handleTestRunError(error, context);
}
}

export async function handleGetTestRun(args: unknown, context?: InstanceContext): Promise<McpToolResponse> {
try {
const client = ensureApiConfigured(context);
const input = getTestRunSchema.parse(args || {});

const response = await client.getTestRun(input.workflowId, input.runId);

return {
success: true,
data: response
};
} catch (error) {
return handleTestRunError(error, context);
}
}

export async function handleListTestCases(args: unknown, context?: InstanceContext): Promise<McpToolResponse> {
try {
const client = ensureApiConfigured(context);
const input = listTestCasesSchema.parse(args || {});

const response = await client.listTestCases(input.workflowId, input.runId, {
limit: input.limit || 20,
cursor: input.cursor,
});

return {
success: true,
data: {
testCases: response.data,
returned: response.data.length,
nextCursor: response.nextCursor,
hasMore: !!response.nextCursor,
...(response.nextCursor ? {
_note: 'More test cases available. Paginate rather than raising limit - per-case inputs/outputs can be large.'
} : {})
}
};
} catch (error) {
return handleTestRunError(error, context);
}
}

// System Tools Handlers

export async function handleHealthCheck(context?: InstanceContext): Promise<McpToolResponse> {
Expand Down
31 changes: 31 additions & 0 deletions src/mcp/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1337,6 +1337,17 @@ export class N8NDocumentationMCPServer {
? { valid: true, errors: [] }
: { valid: false, errors: [{ field: 'action', message: 'action is required' }] };
break;
case 'n8n_evaluations': {
// Every action of this tool requires action and workflowId;
// runId validation is done in dispatch based on action.
const evalErrors: Array<{ field: string; message: string }> = [];
if (!args.action) evalErrors.push({ field: 'action', message: 'action is required' });
if (!args.workflowId) evalErrors.push({ field: 'workflowId', message: 'workflowId is required' });
validationResult = evalErrors.length === 0
? { valid: true, errors: [] }
: { valid: false, errors: evalErrors };
break;
}
case 'n8n_manage_datatable':
validationResult = args.action
? { valid: true, errors: [] }
Expand Down Expand Up @@ -1839,6 +1850,26 @@ export class N8NDocumentationMCPServer {
throw new Error(`Unknown action: ${execAction}. Valid actions: get, list, delete`);
}
}
case 'n8n_evaluations': {
this.validateToolParams(name, args, ['action', 'workflowId']);
const evalAction = args.action;
switch (evalAction) {
case 'list_runs':
return n8nHandlers.handleListTestRuns(args, this.instanceContext);
case 'get_run':
if (!args.runId) {
throw new Error('runId is required for action=get_run');
}
return n8nHandlers.handleGetTestRun(args, this.instanceContext);
case 'list_cases':
if (!args.runId) {
throw new Error('runId is required for action=list_cases');
}
return n8nHandlers.handleListTestCases(args, this.instanceContext);
default:
throw new Error(`Unknown action: ${evalAction}. Valid actions: list_runs, get_run, list_cases`);
}
}
case 'n8n_health_check':
// No required parameters - supports mode='status' (default) or mode='diagnostic'
if (args.mode === 'diagnostic') {
Expand Down
2 changes: 2 additions & 0 deletions src/mcp/tool-docs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
n8nAutofixWorkflowDoc,
n8nTestWorkflowDoc,
n8nExecutionsDoc,
n8nEvaluationsDoc,
n8nWorkflowVersionsDoc,
n8nDeployTemplateDoc,
n8nManageDatatableDoc,
Expand Down Expand Up @@ -63,6 +64,7 @@ export const toolsDocumentation: Record<string, ToolDocumentation> = {
n8n_autofix_workflow: n8nAutofixWorkflowDoc,
n8n_test_workflow: n8nTestWorkflowDoc,
n8n_executions: n8nExecutionsDoc,
n8n_evaluations: n8nEvaluationsDoc,
n8n_workflow_versions: n8nWorkflowVersionsDoc,
n8n_deploy_template: n8nDeployTemplateDoc,
n8n_manage_datatable: n8nManageDatatableDoc,
Expand Down
1 change: 1 addition & 0 deletions src/mcp/tool-docs/workflow_management/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export { n8nValidateWorkflowDoc } from './n8n-validate-workflow';
export { n8nAutofixWorkflowDoc } from './n8n-autofix-workflow';
export { n8nTestWorkflowDoc } from './n8n-test-workflow';
export { n8nExecutionsDoc } from './n8n-executions';
export { n8nEvaluationsDoc } from './n8n-evaluations';
export { n8nWorkflowVersionsDoc } from './n8n-workflow-versions';
export { n8nDeployTemplateDoc } from './n8n-deploy-template';
export { n8nManageDatatableDoc } from './n8n-manage-datatable';
Expand Down
Loading
Loading