Compare commits

...

8 Commits

Author SHA1 Message Date
Romuald Członkowski
3570c1669f chore: bump version to 2.31.8 and add CHANGELOG entry
- Fix comment inaccuracy: "deprecated" not "deprecated and removed"
- Bump version from 2.31.7 to 2.31.8
- Add CHANGELOG entry documenting USE_FIXED_HTTP deprecation
- Update all deprecation messages to reference v2.31.8

Concieved by Romuald Członkowski - www.aiadvisors.pl/en

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 13:24:43 +01:00
Romuald Członkowski
abc01151f4 fix: deprecate USE_FIXED_HTTP for SSE streaming support (Issue #524)
The fixed HTTP implementation does not support SSE streaming required
by clients like OpenAI Codex. This commit deprecates USE_FIXED_HTTP
and makes SingleSessionHTTPServer the default.

Changes:
- Add deprecation warnings in src/mcp/index.ts and src/http-server.ts
- Remove USE_FIXED_HTTP from docker-compose.yml and Dockerfile.railway
- Update .env.example with deprecation notice
- Rename npm script to start:http:fixed:deprecated
- Update all documentation to remove USE_FIXED_HTTP references
- Mark test case as deprecated

Users should unset USE_FIXED_HTTP to use the modern SingleSessionHTTPServer
which supports both JSON-RPC and SSE streaming.

Closes #524

Concieved by Romuald Członkowski - www.aiadvisors.pl/en

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 13:21:31 +01:00
Romuald Członkowski
7b0ff990ec chore: update n8n to 2.2.3 and bump version to 2.31.7 (#523)
- Updated n8n from 2.1.5 to 2.2.3
- Updated n8n-core from 2.1.4 to 2.2.2
- Updated n8n-workflow from 2.1.1 to 2.2.2
- Updated @n8n/n8n-nodes-langchain from 2.1.4 to 2.2.2
- Rebuilt node database with 540 nodes (434 from n8n-nodes-base, 106 from @n8n/n8n-nodes-langchain)
- Updated README badge with new n8n version
- Updated CHANGELOG with dependency changes

Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Romuald Członkowski <romualdczlonkowski@MacBook-Pro-Romuald.local>
Co-authored-by: Claude <noreply@anthropic.com>
2026-01-06 13:18:56 +01:00
Romuald Członkowski
25cb8bb455 chore: update n8n to 2.1.5 and bump version to 2.31.6 (#521)
- Updated n8n from 2.1.4 to 2.1.5
- Updated n8n-core from 2.1.3 to 2.1.4
- Updated @n8n/n8n-nodes-langchain from 2.1.3 to 2.1.4
- Rebuilt node database with 540 nodes (434 from n8n-nodes-base, 106 from @n8n/n8n-nodes-langchain)
- Updated README badge with new n8n version
- Updated CHANGELOG with dependency changes

Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Romuald Członkowski <romualdczlonkowski@MacBook-Pro-Romuald.local>
Co-authored-by: Claude <noreply@anthropic.com>
2026-01-04 10:43:35 +01:00
Bryan Thompson
2713db6d10 feat: add MCP tool annotations to all 20 tools (#512)
* feat: add MCP tool annotations to all 20 tools

Add MCP tool annotations per specification to help AI assistants
understand tool behavior and capabilities.

Documentation tools (7):
- tools_documentation, search_nodes, get_node, validate_node,
  get_template, search_templates, validate_workflow
- All marked readOnlyHint=true (local database queries)

Management tools (13):
- n8n_create_workflow, n8n_get_workflow, n8n_update_full_workflow,
  n8n_update_partial_workflow, n8n_delete_workflow, n8n_list_workflows,
  n8n_validate_workflow, n8n_autofix_workflow, n8n_test_workflow,
  n8n_executions, n8n_health_check, n8n_workflow_versions,
  n8n_deploy_template
- All marked openWorldHint=true (n8n API access)
- Destructive operations (delete_workflow, executions delete,
  workflow_versions delete/truncate) marked destructiveHint=true

Annotations follow MCP spec:
https://spec.modelcontextprotocol.io/specification/2025-03-26/server/tools/#annotations

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat: add idempotentHint to all read-only tools

Adds idempotentHint: true annotation to all read-only tools that produce
the same output when called multiple times:
- 7 documentation tools (tools.ts)
- 4 management tools (tools-n8n-manager.ts): n8n_get_workflow,
  n8n_list_workflows, n8n_validate_workflow, n8n_health_check

Also adds trailing newline to tools-n8n-manager.ts.

Conceived by Romuald Członkowski - www.aiadvisors.pl/en

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat: add idempotentHint to update operations, bump to 2.31.5

Adds idempotentHint: true to update operations that produce the same
result when called repeatedly with the same arguments:
- n8n_update_full_workflow
- n8n_update_partial_workflow
- n8n_autofix_workflow

Also bumps version to 2.31.5 and updates CHANGELOG.md with complete
documentation of all MCP tool annotations added in this PR.

Conceived by Romuald Członkowski - www.aiadvisors.pl/en

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: triepod-ai <noreply@github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Romuald Członkowski <romualdczlonkowski@MacBook-Pro-Romuald.local>
2026-01-02 15:48:47 +01:00
Romuald Członkowski
f10772a9d2 fix: preserve workflow data during serialization (Issue #517) (#519)
Fixed a critical bug where workflow mutation data was corrupted during
serialization to Supabase. The recursive toSnakeCase() function was
converting nested workflow data, mangling:
- Connection keys (node names like "Webhook" → "_webhook")
- Node field names (typeVersion → type_version)

Solution: Replace recursive conversion with selective mutationToSupabaseFormat()
that only converts top-level field names to snake_case while preserving
nested workflow data exactly as-is.

Impact:
- 98.9% of workflow mutations had corrupted data
- Deployability rate improved from ~21% to ~68%

Changes:
- src/telemetry/batch-processor.ts: New selective converter
- tests/unit/telemetry/batch-processor.test.ts: 3 new regression tests

Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Romuald Członkowski <romualdczlonkowski@MacBook-Pro-Romuald.local>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-02 10:44:13 +01:00
Romuald Członkowski
808088f25e docs: fix connection keys documentation to say "node names" not "node IDs" (#510) (#511)
The documentation incorrectly stated connection keys should be "node IDs"
when n8n actually requires "node names". This caused workflow creation
failures for AI-generated workflows.

Changes:
- tools-n8n-manager.ts: "Keys are source node names (the name field, not id)"
- n8n-create-workflow.ts: "Keys are source node names (not IDs)"
- Fixed example: "Webhook"/"Slack" instead of "webhook_1"/"slack_1"
- get-template.ts: clarified "source node names"

Closes #510

Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Romuald Członkowski <romualdczlonkowski@MacBook-Pro-Romuald.local>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-26 14:50:58 +01:00
Romuald Członkowski
20663dad0d chore: update n8n to 2.1.4 and bump version to 2.31.2 (#507)
- Updated n8n from 2.0.2 to 2.1.4
- Updated n8n-core from 2.0.1 to 2.1.3
- Updated n8n-workflow from 2.0.1 to 2.1.1
- Updated @n8n/n8n-nodes-langchain from 2.0.1 to 2.1.3
- Rebuilt node database with 540 nodes (434 from n8n-nodes-base, 106 from @n8n/n8n-nodes-langchain)
- Refreshed template database with 2,737 workflow templates from n8n.io
- Updated README badge with new n8n version
- Updated CHANGELOG with dependency changes

Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Romuald Członkowski <romualdczlonkowski@MacBook-Pro-Romuald.local>
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-24 15:15:22 +01:00
49 changed files with 1277 additions and 7175 deletions

View File

@@ -37,9 +37,11 @@ MCP_SERVER_HOST=localhost
# Server mode: stdio (local) or http (remote) # Server mode: stdio (local) or http (remote)
MCP_MODE=stdio MCP_MODE=stdio
# Use fixed HTTP implementation (recommended for stability) # DEPRECATED: USE_FIXED_HTTP is deprecated as of v2.31.8
# Set to true to bypass StreamableHTTPServerTransport issues # The fixed HTTP implementation does not support SSE streaming required by
USE_FIXED_HTTP=true # clients like OpenAI Codex. Use the default SingleSessionHTTPServer instead.
# See: https://github.com/czlonkowski/n8n-mcp/issues/524
# USE_FIXED_HTTP=true # DO NOT USE - deprecated
# HTTP Server Configuration (only used when MCP_MODE=http) # HTTP Server Configuration (only used when MCP_MODE=http)
PORT=3000 PORT=3000

File diff suppressed because it is too large Load Diff

View File

@@ -74,7 +74,8 @@ ENV AUTH_TOKEN="REPLACE_THIS_AUTH_TOKEN_32_CHARS_MIN_abcdefgh"
ENV NODE_ENV=production ENV NODE_ENV=production
ENV IS_DOCKER=true ENV IS_DOCKER=true
ENV MCP_MODE=http ENV MCP_MODE=http
ENV USE_FIXED_HTTP=true # NOTE: USE_FIXED_HTTP is deprecated. SingleSessionHTTPServer is now the default.
# See: https://github.com/czlonkowski/n8n-mcp/issues/524
ENV LOG_LEVEL=info ENV LOG_LEVEL=info
ENV TRUST_PROXY=1 ENV TRUST_PROXY=1
ENV HOST=0.0.0.0 ENV HOST=0.0.0.0

View File

@@ -5,7 +5,7 @@
[![npm version](https://img.shields.io/npm/v/n8n-mcp.svg)](https://www.npmjs.com/package/n8n-mcp) [![npm version](https://img.shields.io/npm/v/n8n-mcp.svg)](https://www.npmjs.com/package/n8n-mcp)
[![codecov](https://codecov.io/gh/czlonkowski/n8n-mcp/graph/badge.svg?token=YOUR_TOKEN)](https://codecov.io/gh/czlonkowski/n8n-mcp) [![codecov](https://codecov.io/gh/czlonkowski/n8n-mcp/graph/badge.svg?token=YOUR_TOKEN)](https://codecov.io/gh/czlonkowski/n8n-mcp)
[![Tests](https://img.shields.io/badge/tests-3336%20passing-brightgreen.svg)](https://github.com/czlonkowski/n8n-mcp/actions) [![Tests](https://img.shields.io/badge/tests-3336%20passing-brightgreen.svg)](https://github.com/czlonkowski/n8n-mcp/actions)
[![n8n version](https://img.shields.io/badge/n8n-2.0.2-orange.svg)](https://github.com/n8n-io/n8n) [![n8n version](https://img.shields.io/badge/n8n-2.2.3-orange.svg)](https://github.com/n8n-io/n8n)
[![Docker](https://img.shields.io/badge/docker-ghcr.io%2Fczlonkowski%2Fn8n--mcp-green.svg)](https://github.com/czlonkowski/n8n-mcp/pkgs/container/n8n-mcp) [![Docker](https://img.shields.io/badge/docker-ghcr.io%2Fczlonkowski%2Fn8n--mcp-green.svg)](https://github.com/czlonkowski/n8n-mcp/pkgs/container/n8n-mcp)
[![Deploy on Railway](https://railway.com/button.svg)](https://railway.com/deploy/n8n-mcp?referralCode=n8n-mcp) [![Deploy on Railway](https://railway.com/button.svg)](https://railway.com/deploy/n8n-mcp?referralCode=n8n-mcp)

Binary file not shown.

View File

@@ -1 +1 @@
{"version":3,"file":"handlers-n8n-manager.d.ts","sourceRoot":"","sources":["../../src/mcp/handlers-n8n-manager.ts"],"names":[],"mappings":"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"} {"version":3,"file":"handlers-n8n-manager.d.ts","sourceRoot":"","sources":["../../src/mcp/handlers-n8n-manager.ts"],"names":[],"mappings":"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"}

View File

@@ -1024,14 +1024,18 @@ async function handleGetExecution(args, context) {
const client = ensureApiConfigured(context); const client = ensureApiConfigured(context);
const schema = zod_1.z.object({ const schema = zod_1.z.object({
id: zod_1.z.string(), id: zod_1.z.string(),
mode: zod_1.z.enum(['preview', 'summary', 'filtered', 'full']).optional(), mode: zod_1.z.enum(['preview', 'summary', 'filtered', 'full', 'error']).optional(),
nodeNames: zod_1.z.array(zod_1.z.string()).optional(), nodeNames: zod_1.z.array(zod_1.z.string()).optional(),
itemsLimit: zod_1.z.number().optional(), itemsLimit: zod_1.z.number().optional(),
includeInputData: zod_1.z.boolean().optional(), includeInputData: zod_1.z.boolean().optional(),
includeData: zod_1.z.boolean().optional() includeData: zod_1.z.boolean().optional(),
errorItemsLimit: zod_1.z.number().min(0).max(100).optional(),
includeStackTrace: zod_1.z.boolean().optional(),
includeExecutionPath: zod_1.z.boolean().optional(),
fetchWorkflow: zod_1.z.boolean().optional()
}); });
const params = schema.parse(args); const params = schema.parse(args);
const { id, mode, nodeNames, itemsLimit, includeInputData, includeData } = params; const { id, mode, nodeNames, itemsLimit, includeInputData, includeData, errorItemsLimit, includeStackTrace, includeExecutionPath, fetchWorkflow } = params;
let effectiveMode = mode; let effectiveMode = mode;
if (!effectiveMode && includeData !== undefined) { if (!effectiveMode && includeData !== undefined) {
effectiveMode = includeData ? 'summary' : undefined; effectiveMode = includeData ? 'summary' : undefined;
@@ -1044,13 +1048,28 @@ async function handleGetExecution(args, context) {
data: execution data: execution
}; };
} }
let workflow;
if (effectiveMode === 'error' && fetchWorkflow !== false && execution.workflowId) {
try {
workflow = await client.getWorkflow(execution.workflowId);
}
catch (e) {
logger_1.logger.debug('Could not fetch workflow for error analysis', {
workflowId: execution.workflowId,
error: e instanceof Error ? e.message : 'Unknown error'
});
}
}
const filterOptions = { const filterOptions = {
mode: effectiveMode, mode: effectiveMode,
nodeNames, nodeNames,
itemsLimit, itemsLimit,
includeInputData includeInputData,
errorItemsLimit,
includeStackTrace,
includeExecutionPath
}; };
const processedExecution = (0, execution_processor_1.processExecution)(execution, filterOptions); const processedExecution = (0, execution_processor_1.processExecution)(execution, filterOptions, workflow);
return { return {
success: true, success: true,
data: processedExecution data: processedExecution

File diff suppressed because one or more lines are too long

View File

@@ -43,7 +43,7 @@ exports.getTemplateDoc = {
- url: Link to template on n8n.io - url: Link to template on n8n.io
- workflow: Complete workflow JSON with structure: - workflow: Complete workflow JSON with structure:
- nodes: Array of node objects (id, name, type, typeVersion, position, parameters) - nodes: Array of node objects (id, name, type, typeVersion, position, parameters)
- connections: Object mapping source nodes to targets - connections: Object mapping source node names to targets
- settings: Workflow configuration (timezone, error handling, etc.) - settings: Workflow configuration (timezone, error handling, etc.)
- usage: Instructions for using the workflow`, - usage: Instructions for using the workflow`,
examples: [ examples: [

View File

@@ -21,7 +21,7 @@ exports.n8nCreateWorkflowDoc = {
parameters: { parameters: {
name: { type: 'string', required: true, description: 'Workflow name' }, name: { type: 'string', required: true, description: 'Workflow name' },
nodes: { type: 'array', required: true, description: 'Array of nodes with id, name, type, typeVersion, position, parameters' }, nodes: { type: 'array', required: true, description: 'Array of nodes with id, name, type, typeVersion, position, parameters' },
connections: { type: 'object', required: true, description: 'Node connections. Keys are source node IDs' }, connections: { type: 'object', required: true, description: 'Node connections. Keys are source node names (not IDs)' },
settings: { type: 'object', description: 'Optional workflow settings (timezone, error handling, etc.)' } settings: { type: 'object', description: 'Optional workflow settings (timezone, error handling, etc.)' }
}, },
returns: 'Minimal summary (id, name, active, nodeCount) for token efficiency. Use n8n_get_workflow with mode "structure" to verify current state if needed.', returns: 'Minimal summary (id, name, active, nodeCount) for token efficiency. Use n8n_get_workflow with mode "structure" to verify current state if needed.',
@@ -56,8 +56,8 @@ n8n_create_workflow({
} }
], ],
connections: { connections: {
"webhook_1": { "Webhook": {
"main": [[{node: "slack_1", type: "main", index: 0}]] "main": [[{node: "Slack", type: "main", index: 0}]]
} }
} }
})`, })`,

View File

@@ -1 +1 @@
{"version":3,"file":"n8n-create-workflow.js","sourceRoot":"","sources":["../../../../src/mcp/tool-docs/workflow_management/n8n-create-workflow.ts"],"names":[],"mappings":";;;AAEa,QAAA,oBAAoB,GAAsB;IACrD,IAAI,EAAE,qBAAqB;IAC3B,QAAQ,EAAE,qBAAqB;IAC/B,UAAU,EAAE;QACV,WAAW,EAAE,sGAAsG;QACnH,aAAa,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,aAAa,CAAC;QAC/C,OAAO,EAAE,0EAA0E;QACnF,WAAW,EAAE,mBAAmB;QAChC,IAAI,EAAE;YACJ,2BAA2B;YAC3B,+BAA+B;YAC/B,uCAAuC;YACvC,kFAAkF;SACnF;KACF;IACD,IAAI,EAAE;QACJ,WAAW,EAAE,uLAAuL;QACpM,UAAU,EAAE;YACV,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,eAAe,EAAE;YACtE,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,uEAAuE,EAAE;YAC9H,WAAW,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,4CAA4C,EAAE;YAC1G,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,6DAA6D,EAAE;SACzG;QACD,OAAO,EAAE,mJAAmJ;QAC5J,QAAQ,EAAE;YACR;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCH;YACG;;;;;;;;;;;GAWH;SACE;QACD,QAAQ,EAAE;YACR,4BAA4B;YAC5B,4BAA4B;YAC5B,2BAA2B;YAC3B,qBAAqB;SACtB;QACD,WAAW,EAAE,oEAAoE;QACjF,aAAa,EAAE;YACb,uCAAuC;YACvC,qBAAqB;YACrB,gCAAgC;YAChC,6BAA6B;SAC9B;QACD,QAAQ,EAAE;YACR,0GAA0G;YAC1G,gEAAgE;YAChE,yCAAyC;YACzC,kDAAkD;YAClD,4EAA4E;YAC5E,yIAAyI;YACzI,uIAAuI;SACxI;QACD,YAAY,EAAE,CAAC,mBAAmB,EAAE,6BAA6B,EAAE,mBAAmB,CAAC;KACxF;CACF,CAAC"} {"version":3,"file":"n8n-create-workflow.js","sourceRoot":"","sources":["../../../../src/mcp/tool-docs/workflow_management/n8n-create-workflow.ts"],"names":[],"mappings":";;;AAEa,QAAA,oBAAoB,GAAsB;IACrD,IAAI,EAAE,qBAAqB;IAC3B,QAAQ,EAAE,qBAAqB;IAC/B,UAAU,EAAE;QACV,WAAW,EAAE,sGAAsG;QACnH,aAAa,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,aAAa,CAAC;QAC/C,OAAO,EAAE,0EAA0E;QACnF,WAAW,EAAE,mBAAmB;QAChC,IAAI,EAAE;YACJ,2BAA2B;YAC3B,+BAA+B;YAC/B,uCAAuC;YACvC,kFAAkF;SACnF;KACF;IACD,IAAI,EAAE;QACJ,WAAW,EAAE,uLAAuL;QACpM,UAAU,EAAE;YACV,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,eAAe,EAAE;YACtE,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,uEAAuE,EAAE;YAC9H,WAAW,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,wDAAwD,EAAE;YACtH,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,6DAA6D,EAAE;SACzG;QACD,OAAO,EAAE,mJAAmJ;QAC5J,QAAQ,EAAE;YACR;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCH;YACG;;;;;;;;;;;GAWH;SACE;QACD,QAAQ,EAAE;YACR,4BAA4B;YAC5B,4BAA4B;YAC5B,2BAA2B;YAC3B,qBAAqB;SACtB;QACD,WAAW,EAAE,oEAAoE;QACjF,aAAa,EAAE;YACb,uCAAuC;YACvC,qBAAqB;YACrB,gCAAgC;YAChC,6BAA6B;SAC9B;QACD,QAAQ,EAAE;YACR,0GAA0G;YAC1G,gEAAgE;YAChE,yCAAyC;YACzC,kDAAkD;YAClD,4EAA4E;YAC5E,yIAAyI;YACzI,uIAAuI;SACxI;QACD,YAAY,EAAE,CAAC,mBAAmB,EAAE,6BAA6B,EAAE,mBAAmB,CAAC;KACxF;CACF,CAAC"}

View File

@@ -1 +1 @@
{"version":3,"file":"n8n-executions.d.ts","sourceRoot":"","sources":["../../../../src/mcp/tool-docs/workflow_management/n8n-executions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAE7C,eAAO,MAAM,gBAAgB,EAAE,iBA+E9B,CAAC"} {"version":3,"file":"n8n-executions.d.ts","sourceRoot":"","sources":["../../../../src/mcp/tool-docs/workflow_management/n8n-executions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAE7C,eAAO,MAAM,gBAAgB,EAAE,iBAwG9B,CAAC"}

View File

@@ -6,13 +6,14 @@ exports.n8nExecutionsDoc = {
category: 'workflow_management', category: 'workflow_management',
essentials: { essentials: {
description: 'Manage workflow executions: get details, list, or delete. Unified tool for all execution operations.', description: 'Manage workflow executions: get details, list, or delete. Unified tool for all execution operations.',
keyParameters: ['action', 'id', 'workflowId', 'status'], keyParameters: ['action', 'id', 'workflowId', 'status', 'mode'],
example: 'n8n_executions({action: "list", workflowId: "abc123", status: "error"})', example: 'n8n_executions({action: "get", id: "exec_456", mode: "error"})',
performance: 'Fast (50-200ms)', performance: 'Fast (50-200ms)',
tips: [ tips: [
'action="get": Get execution details by ID', 'action="get": Get execution details by ID',
'action="list": List executions with filters', 'action="list": List executions with filters',
'action="delete": Delete execution record', 'action="delete": Delete execution record',
'Use mode="error" for efficient failure debugging (80-90% token savings)',
'Use mode parameter for action=get to control detail level' 'Use mode parameter for action=get to control detail level'
] ]
}, },
@@ -26,14 +27,26 @@ exports.n8nExecutionsDoc = {
- preview: Structure only, no data - preview: Structure only, no data
- summary: 2 items per node (default) - summary: 2 items per node (default)
- filtered: Custom items limit, optionally filter by node names - filtered: Custom items limit, optionally filter by node names
- full: All execution data (can be very large)`, - full: All execution data (can be very large)
- error: Optimized for debugging failures - extracts error info, upstream context, and AI suggestions
**Error Mode Features:**
- Extracts error message, type, and node configuration
- Samples input data from upstream node (configurable limit)
- Shows execution path leading to error
- Provides AI-friendly fix suggestions based on error patterns
- Token-efficient (80-90% smaller than full mode)`,
parameters: { parameters: {
action: { type: 'string', required: true, description: 'Operation: "get", "list", or "delete"' }, action: { type: 'string', required: true, description: 'Operation: "get", "list", or "delete"' },
id: { type: 'string', required: false, description: 'Execution ID (required for action=get or action=delete)' }, id: { type: 'string', required: false, description: 'Execution ID (required for action=get or action=delete)' },
mode: { type: 'string', required: false, description: 'For action=get: "preview", "summary" (default), "filtered", "full"' }, mode: { type: 'string', required: false, description: 'For action=get: "preview", "summary" (default), "filtered", "full", "error"' },
nodeNames: { type: 'array', required: false, description: 'For action=get with mode=filtered: Filter to specific nodes by name' }, nodeNames: { type: 'array', required: false, description: 'For action=get with mode=filtered: Filter to specific nodes by name' },
itemsLimit: { type: 'number', required: false, description: 'For action=get with mode=filtered: Items per node (0=structure, 2=default, -1=unlimited)' }, itemsLimit: { type: 'number', required: false, description: 'For action=get with mode=filtered: Items per node (0=structure, 2=default, -1=unlimited)' },
includeInputData: { type: 'boolean', required: false, description: 'For action=get: Include input data in addition to output (default: false)' }, includeInputData: { type: 'boolean', required: false, description: 'For action=get: Include input data in addition to output (default: false)' },
errorItemsLimit: { type: 'number', required: false, description: 'For action=get with mode=error: Sample items from upstream (default: 2, max: 100)' },
includeStackTrace: { type: 'boolean', required: false, description: 'For action=get with mode=error: Include full stack trace (default: false, shows truncated)' },
includeExecutionPath: { type: 'boolean', required: false, description: 'For action=get with mode=error: Include execution path (default: true)' },
fetchWorkflow: { type: 'boolean', required: false, description: 'For action=get with mode=error: Fetch workflow for accurate upstream detection (default: true)' },
workflowId: { type: 'string', required: false, description: 'For action=list: Filter by workflow ID' }, workflowId: { type: 'string', required: false, description: 'For action=list: Filter by workflow ID' },
status: { type: 'string', required: false, description: 'For action=list: Filter by status ("success", "error", "waiting")' }, status: { type: 'string', required: false, description: 'For action=list: Filter by status ("success", "error", "waiting")' },
limit: { type: 'number', required: false, description: 'For action=list: Number of results (1-100, default: 100)' }, limit: { type: 'number', required: false, description: 'For action=list: Number of results (1-100, default: 100)' },
@@ -42,10 +55,15 @@ exports.n8nExecutionsDoc = {
includeData: { type: 'boolean', required: false, description: 'For action=list: Include execution data (default: false)' } includeData: { type: 'boolean', required: false, description: 'For action=list: Include execution data (default: false)' }
}, },
returns: `Depends on action: returns: `Depends on action:
- get: Execution object with data based on mode - get (error mode): { errorInfo: { primaryError, upstreamContext, executionPath, suggestions }, summary }
- get (other modes): Execution object with data based on mode
- list: { data: [...executions], nextCursor?: string } - list: { data: [...executions], nextCursor?: string }
- delete: { success: boolean, message: string }`, - delete: { success: boolean, message: string }`,
examples: [ examples: [
'// Debug a failed execution (recommended for errors)\nn8n_executions({action: "get", id: "exec_456", mode: "error"})',
'// Debug with more sample data from upstream\nn8n_executions({action: "get", id: "exec_456", mode: "error", errorItemsLimit: 5})',
'// Debug with full stack trace\nn8n_executions({action: "get", id: "exec_456", mode: "error", includeStackTrace: true})',
'// Debug without workflow fetch (faster but less accurate)\nn8n_executions({action: "get", id: "exec_456", mode: "error", fetchWorkflow: false})',
'// List recent executions for a workflow\nn8n_executions({action: "list", workflowId: "abc123", limit: 10})', '// List recent executions for a workflow\nn8n_executions({action: "list", workflowId: "abc123", limit: 10})',
'// List failed executions\nn8n_executions({action: "list", status: "error"})', '// List failed executions\nn8n_executions({action: "list", status: "error"})',
'// Get execution summary\nn8n_executions({action: "get", id: "exec_456"})', '// Get execution summary\nn8n_executions({action: "get", id: "exec_456"})',
@@ -54,7 +72,10 @@ exports.n8nExecutionsDoc = {
'// Delete an execution\nn8n_executions({action: "delete", id: "exec_456"})' '// Delete an execution\nn8n_executions({action: "delete", id: "exec_456"})'
], ],
useCases: [ useCases: [
'Debug workflow failures (get with mode=full)', 'Debug workflow failures efficiently (mode=error) - 80-90% token savings',
'Get AI suggestions for fixing common errors',
'Analyze input data that caused failure',
'Debug workflow failures with full data (mode=full)',
'Monitor workflow health (list with status filter)', 'Monitor workflow health (list with status filter)',
'Audit execution history', 'Audit execution history',
'Clean up old execution records', 'Clean up old execution records',
@@ -63,18 +84,22 @@ exports.n8nExecutionsDoc = {
performance: `Response times: performance: `Response times:
- list: 50-150ms depending on filters - list: 50-150ms depending on filters
- get (preview/summary): 30-100ms - get (preview/summary): 30-100ms
- get (error): 50-200ms (includes optional workflow fetch)
- get (full): 100-500ms+ depending on data size - get (full): 100-500ms+ depending on data size
- delete: 30-80ms`, - delete: 30-80ms`,
bestPractices: [ bestPractices: [
'Use mode="summary" (default) for debugging - shows enough data', 'Use mode="error" for debugging failed executions - 80-90% token savings vs full',
'Use mode="summary" (default) for quick inspection',
'Use mode="filtered" with nodeNames for large workflows', 'Use mode="filtered" with nodeNames for large workflows',
'Filter by workflowId when listing to reduce results', 'Filter by workflowId when listing to reduce results',
'Use cursor for pagination through large result sets', 'Use cursor for pagination through large result sets',
'Set fetchWorkflow=false if you already know the workflow structure',
'Delete old executions to save storage' 'Delete old executions to save storage'
], ],
pitfalls: [ pitfalls: [
'Requires N8N_API_URL and N8N_API_KEY configured', 'Requires N8N_API_URL and N8N_API_KEY configured',
'mode="full" can return very large responses for complex workflows', 'mode="full" can return very large responses for complex workflows',
'mode="error" fetches workflow by default (adds ~50-100ms), disable with fetchWorkflow=false',
'Execution must exist or returns 404', 'Execution must exist or returns 404',
'Delete is permanent - cannot undo' 'Delete is permanent - cannot undo'
], ],

View File

@@ -1 +1 @@
{"version":3,"file":"n8n-executions.js","sourceRoot":"","sources":["../../../../src/mcp/tool-docs/workflow_management/n8n-executions.ts"],"names":[],"mappings":";;;AAEa,QAAA,gBAAgB,GAAsB;IACjD,IAAI,EAAE,gBAAgB;IACtB,QAAQ,EAAE,qBAAqB;IAC/B,UAAU,EAAE;QACV,WAAW,EAAE,sGAAsG;QACnH,aAAa,EAAE,CAAC,QAAQ,EAAE,IAAI,EAAE,YAAY,EAAE,QAAQ,CAAC;QACvD,OAAO,EAAE,yEAAyE;QAClF,WAAW,EAAE,iBAAiB;QAC9B,IAAI,EAAE;YACJ,2CAA2C;YAC3C,6CAA6C;YAC7C,0CAA0C;YAC1C,2DAA2D;SAC5D;KACF;IACD,IAAI,EAAE;QACJ,WAAW,EAAE;;;;;;;;;+CAS8B;QAC3C,UAAU,EAAE;YACV,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,uCAAuC,EAAE;YAChG,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,yDAAyD,EAAE;YAC/G,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,oEAAoE,EAAE;YAC5H,SAAS,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,qEAAqE,EAAE;YACjI,UAAU,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,0FAA0F,EAAE;YACxJ,gBAAgB,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,2EAA2E,EAAE;YAChJ,UAAU,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,wCAAwC,EAAE;YACtG,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,mEAAmE,EAAE;YAC7H,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,0DAA0D,EAAE;YACnH,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,2DAA2D,EAAE;YACrH,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,oDAAoD,EAAE;YACjH,WAAW,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,0DAA0D,EAAE;SAC3H;QACD,OAAO,EAAE;;;gDAGmC;QAC5C,QAAQ,EAAE;YACR,6GAA6G;YAC7G,8EAA8E;YAC9E,2EAA2E;YAC3E,2FAA2F;YAC3F,+IAA+I;YAC/I,4EAA4E;SAC7E;QACD,QAAQ,EAAE;YACR,8CAA8C;YAC9C,mDAAmD;YACnD,yBAAyB;YACzB,gCAAgC;YAChC,+BAA+B;SAChC;QACD,WAAW,EAAE;;;;kBAIC;QACd,aAAa,EAAE;YACb,gEAAgE;YAChE,wDAAwD;YACxD,qDAAqD;YACrD,qDAAqD;YACrD,uCAAuC;SACxC;QACD,QAAQ,EAAE;YACR,iDAAiD;YACjD,mEAAmE;YACnE,qCAAqC;YACrC,mCAAmC;SACpC;QACD,YAAY,EAAE,CAAC,kBAAkB,EAAE,mBAAmB,EAAE,uBAAuB,CAAC;KACjF;CACF,CAAC"} {"version":3,"file":"n8n-executions.js","sourceRoot":"","sources":["../../../../src/mcp/tool-docs/workflow_management/n8n-executions.ts"],"names":[],"mappings":";;;AAEa,QAAA,gBAAgB,GAAsB;IACjD,IAAI,EAAE,gBAAgB;IACtB,QAAQ,EAAE,qBAAqB;IAC/B,UAAU,EAAE;QACV,WAAW,EAAE,sGAAsG;QACnH,aAAa,EAAE,CAAC,QAAQ,EAAE,IAAI,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,CAAC;QAC/D,OAAO,EAAE,gEAAgE;QACzE,WAAW,EAAE,iBAAiB;QAC9B,IAAI,EAAE;YACJ,2CAA2C;YAC3C,6CAA6C;YAC7C,0CAA0C;YAC1C,yEAAyE;YACzE,2DAA2D;SAC5D;KACF;IACD,IAAI,EAAE;QACJ,WAAW,EAAE;;;;;;;;;;;;;;;;;kDAiBiC;QAC9C,UAAU,EAAE;YACV,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,uCAAuC,EAAE;YAChG,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,yDAAyD,EAAE;YAC/G,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,6EAA6E,EAAE;YACrI,SAAS,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,qEAAqE,EAAE;YACjI,UAAU,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,0FAA0F,EAAE;YACxJ,gBAAgB,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,2EAA2E,EAAE;YAChJ,eAAe,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,mFAAmF,EAAE;YACtJ,iBAAiB,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,4FAA4F,EAAE;YAClK,oBAAoB,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,wEAAwE,EAAE;YACjJ,aAAa,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,gGAAgG,EAAE;YAClK,UAAU,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,wCAAwC,EAAE;YACtG,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,mEAAmE,EAAE;YAC7H,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,0DAA0D,EAAE;YACnH,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,2DAA2D,EAAE;YACrH,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,oDAAoD,EAAE;YACjH,WAAW,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,0DAA0D,EAAE;SAC3H;QACD,OAAO,EAAE;;;;gDAImC;QAC5C,QAAQ,EAAE;YACR,sHAAsH;YACtH,kIAAkI;YAClI,yHAAyH;YACzH,kJAAkJ;YAClJ,6GAA6G;YAC7G,8EAA8E;YAC9E,2EAA2E;YAC3E,2FAA2F;YAC3F,+IAA+I;YAC/I,4EAA4E;SAC7E;QACD,QAAQ,EAAE;YACR,yEAAyE;YACzE,6CAA6C;YAC7C,wCAAwC;YACxC,oDAAoD;YACpD,mDAAmD;YACnD,yBAAyB;YACzB,gCAAgC;YAChC,+BAA+B;SAChC;QACD,WAAW,EAAE;;;;;kBAKC;QACd,aAAa,EAAE;YACb,iFAAiF;YACjF,mDAAmD;YACnD,wDAAwD;YACxD,qDAAqD;YACrD,qDAAqD;YACrD,oEAAoE;YACpE,uCAAuC;SACxC;QACD,QAAQ,EAAE;YACR,iDAAiD;YACjD,mEAAmE;YACnE,6FAA6F;YAC7F,qCAAqC;YACrC,mCAAmC;SACpC;QACD,YAAY,EAAE,CAAC,kBAAkB,EAAE,mBAAmB,EAAE,uBAAuB,CAAC;KACjF;CACF,CAAC"}

View File

@@ -1 +1 @@
{"version":3,"file":"tools-n8n-manager.d.ts","sourceRoot":"","sources":["../../src/mcp/tools-n8n-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAQ1C,eAAO,MAAM,kBAAkB,EAAE,cAAc,EAmf9C,CAAC"} {"version":3,"file":"tools-n8n-manager.d.ts","sourceRoot":"","sources":["../../src/mcp/tools-n8n-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAQ1C,eAAO,MAAM,kBAAkB,EAAE,cAAc,EAqlB9C,CAAC"}

View File

@@ -42,7 +42,7 @@ exports.n8nManagementTools = [
}, },
connections: { connections: {
type: 'object', type: 'object',
description: 'Workflow connections object. Keys are source node IDs, values define output connections' description: 'Workflow connections object. Keys are source node names (the name field, not id), values define output connections'
}, },
settings: { settings: {
type: 'object', type: 'object',
@@ -60,7 +60,13 @@ exports.n8nManagementTools = [
} }
}, },
required: ['name', 'nodes', 'connections'] required: ['name', 'nodes', 'connections']
} },
annotations: {
title: 'Create Workflow',
readOnlyHint: false,
destructiveHint: false,
openWorldHint: true,
},
}, },
{ {
name: 'n8n_get_workflow', name: 'n8n_get_workflow',
@@ -80,7 +86,13 @@ exports.n8nManagementTools = [
} }
}, },
required: ['id'] required: ['id']
} },
annotations: {
title: 'Get Workflow',
readOnlyHint: true,
idempotentHint: true,
openWorldHint: true,
},
}, },
{ {
name: 'n8n_update_full_workflow', name: 'n8n_update_full_workflow',
@@ -114,7 +126,14 @@ exports.n8nManagementTools = [
} }
}, },
required: ['id'] required: ['id']
} },
annotations: {
title: 'Update Full Workflow',
readOnlyHint: false,
destructiveHint: false,
idempotentHint: true,
openWorldHint: true,
},
}, },
{ {
name: 'n8n_update_partial_workflow', name: 'n8n_update_partial_workflow',
@@ -145,7 +164,14 @@ exports.n8nManagementTools = [
} }
}, },
required: ['id', 'operations'] required: ['id', 'operations']
} },
annotations: {
title: 'Update Partial Workflow',
readOnlyHint: false,
destructiveHint: false,
idempotentHint: true,
openWorldHint: true,
},
}, },
{ {
name: 'n8n_delete_workflow', name: 'n8n_delete_workflow',
@@ -159,7 +185,13 @@ exports.n8nManagementTools = [
} }
}, },
required: ['id'] required: ['id']
} },
annotations: {
title: 'Delete Workflow',
readOnlyHint: false,
destructiveHint: true,
openWorldHint: true,
},
}, },
{ {
name: 'n8n_list_workflows', name: 'n8n_list_workflows',
@@ -193,7 +225,13 @@ exports.n8nManagementTools = [
description: 'Exclude pinned data from response (default: true)' description: 'Exclude pinned data from response (default: true)'
} }
} }
} },
annotations: {
title: 'List Workflows',
readOnlyHint: true,
idempotentHint: true,
openWorldHint: true,
},
}, },
{ {
name: 'n8n_validate_workflow', name: 'n8n_validate_workflow',
@@ -230,7 +268,13 @@ exports.n8nManagementTools = [
} }
}, },
required: ['id'] required: ['id']
} },
annotations: {
title: 'Validate Workflow',
readOnlyHint: true,
idempotentHint: true,
openWorldHint: true,
},
}, },
{ {
name: 'n8n_autofix_workflow', name: 'n8n_autofix_workflow',
@@ -265,7 +309,14 @@ exports.n8nManagementTools = [
} }
}, },
required: ['id'] required: ['id']
} },
annotations: {
title: 'Autofix Workflow',
readOnlyHint: false,
destructiveHint: false,
idempotentHint: true,
openWorldHint: true,
},
}, },
{ {
name: 'n8n_test_workflow', name: 'n8n_test_workflow',
@@ -317,7 +368,13 @@ exports.n8nManagementTools = [
} }
}, },
required: ['workflowId'] required: ['workflowId']
} },
annotations: {
title: 'Test Workflow',
readOnlyHint: false,
destructiveHint: false,
openWorldHint: true,
},
}, },
{ {
name: 'n8n_executions', name: 'n8n_executions',
@@ -336,8 +393,8 @@ exports.n8nManagementTools = [
}, },
mode: { mode: {
type: 'string', type: 'string',
enum: ['preview', 'summary', 'filtered', 'full'], enum: ['preview', 'summary', 'filtered', 'full', 'error'],
description: 'For action=get: preview=structure only, summary=2 items (default), filtered=custom, full=all data' description: 'For action=get: preview=structure only, summary=2 items (default), filtered=custom, full=all data, error=optimized error debugging'
}, },
nodeNames: { nodeNames: {
type: 'array', type: 'array',
@@ -352,6 +409,22 @@ exports.n8nManagementTools = [
type: 'boolean', type: 'boolean',
description: 'For action=get: include input data in addition to output (default: false)' description: 'For action=get: include input data in addition to output (default: false)'
}, },
errorItemsLimit: {
type: 'number',
description: 'For action=get with mode=error: sample items from upstream node (default: 2, max: 100)'
},
includeStackTrace: {
type: 'boolean',
description: 'For action=get with mode=error: include full stack trace (default: false, shows truncated)'
},
includeExecutionPath: {
type: 'boolean',
description: 'For action=get with mode=error: include execution path leading to error (default: true)'
},
fetchWorkflow: {
type: 'boolean',
description: 'For action=get with mode=error: fetch workflow for accurate upstream detection (default: true)'
},
limit: { limit: {
type: 'number', type: 'number',
description: 'For action=list: number of executions to return (1-100, default: 100)' description: 'For action=list: number of executions to return (1-100, default: 100)'
@@ -379,7 +452,13 @@ exports.n8nManagementTools = [
} }
}, },
required: ['action'] required: ['action']
} },
annotations: {
title: 'Manage Executions',
readOnlyHint: false,
destructiveHint: true,
openWorldHint: true,
},
}, },
{ {
name: 'n8n_health_check', name: 'n8n_health_check',
@@ -398,7 +477,13 @@ exports.n8nManagementTools = [
description: 'Include extra details in diagnostic mode (default: false)' description: 'Include extra details in diagnostic mode (default: false)'
} }
} }
} },
annotations: {
title: 'Health Check',
readOnlyHint: true,
idempotentHint: true,
openWorldHint: true,
},
}, },
{ {
name: 'n8n_workflow_versions', name: 'n8n_workflow_versions',
@@ -452,7 +537,13 @@ exports.n8nManagementTools = [
} }
}, },
required: ['mode'] required: ['mode']
} },
annotations: {
title: 'Workflow Versions',
readOnlyHint: false,
destructiveHint: true,
openWorldHint: true,
},
}, },
{ {
name: 'n8n_deploy_template', name: 'n8n_deploy_template',
@@ -485,7 +576,13 @@ exports.n8nManagementTools = [
} }
}, },
required: ['templateId'] required: ['templateId']
} },
annotations: {
title: 'Deploy Template',
readOnlyHint: false,
destructiveHint: false,
openWorldHint: true,
},
} }
]; ];
//# sourceMappingURL=tools-n8n-manager.js.map //# sourceMappingURL=tools-n8n-manager.js.map

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/mcp/tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAQ1C,eAAO,MAAM,0BAA0B,EAAE,cAAc,EA+XtD,CAAC"} {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/mcp/tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAQ1C,eAAO,MAAM,0BAA0B,EAAE,cAAc,EAkatD,CAAC"}

35
dist/mcp/tools.js vendored
View File

@@ -20,6 +20,11 @@ exports.n8nDocumentationToolsFinal = [
}, },
}, },
}, },
annotations: {
title: 'Tools Documentation',
readOnlyHint: true,
idempotentHint: true,
},
}, },
{ {
name: 'search_nodes', name: 'search_nodes',
@@ -50,6 +55,11 @@ exports.n8nDocumentationToolsFinal = [
}, },
required: ['query'], required: ['query'],
}, },
annotations: {
title: 'Search Nodes',
readOnlyHint: true,
idempotentHint: true,
},
}, },
{ {
name: 'get_node', name: 'get_node',
@@ -103,6 +113,11 @@ exports.n8nDocumentationToolsFinal = [
}, },
required: ['nodeType'], required: ['nodeType'],
}, },
annotations: {
title: 'Get Node Info',
readOnlyHint: true,
idempotentHint: true,
},
}, },
{ {
name: 'validate_node', name: 'validate_node',
@@ -183,6 +198,11 @@ exports.n8nDocumentationToolsFinal = [
}, },
required: ['nodeType', 'displayName', 'valid'] required: ['nodeType', 'displayName', 'valid']
}, },
annotations: {
title: 'Validate Node Config',
readOnlyHint: true,
idempotentHint: true,
},
}, },
{ {
name: 'get_template', name: 'get_template',
@@ -203,6 +223,11 @@ exports.n8nDocumentationToolsFinal = [
}, },
required: ['templateId'], required: ['templateId'],
}, },
annotations: {
title: 'Get Template',
readOnlyHint: true,
idempotentHint: true,
},
}, },
{ {
name: 'search_templates', name: 'search_templates',
@@ -293,6 +318,11 @@ exports.n8nDocumentationToolsFinal = [
}, },
}, },
}, },
annotations: {
title: 'Search Templates',
readOnlyHint: true,
idempotentHint: true,
},
}, },
{ {
name: 'validate_workflow', name: 'validate_workflow',
@@ -378,6 +408,11 @@ exports.n8nDocumentationToolsFinal = [
}, },
required: ['valid', 'summary'] required: ['valid', 'summary']
}, },
annotations: {
title: 'Validate Workflow',
readOnlyHint: true,
idempotentHint: true,
},
}, },
]; ];
//# sourceMappingURL=tools.js.map //# sourceMappingURL=tools.js.map

File diff suppressed because one or more lines are too long

View File

@@ -1,8 +1,8 @@
import { Execution, ExecutionPreview, ExecutionRecommendation, ExecutionFilterOptions, FilteredExecutionResponse } from '../types/n8n-api'; import { Execution, ExecutionPreview, ExecutionRecommendation, ExecutionFilterOptions, FilteredExecutionResponse, Workflow } from '../types/n8n-api';
export declare function generatePreview(execution: Execution): { export declare function generatePreview(execution: Execution): {
preview: ExecutionPreview; preview: ExecutionPreview;
recommendation: ExecutionRecommendation; recommendation: ExecutionRecommendation;
}; };
export declare function filterExecutionData(execution: Execution, options: ExecutionFilterOptions): FilteredExecutionResponse; export declare function filterExecutionData(execution: Execution, options: ExecutionFilterOptions, workflow?: Workflow): FilteredExecutionResponse;
export declare function processExecution(execution: Execution, options?: ExecutionFilterOptions): FilteredExecutionResponse | Execution; export declare function processExecution(execution: Execution, options?: ExecutionFilterOptions, workflow?: Workflow): FilteredExecutionResponse | Execution;
//# sourceMappingURL=execution-processor.d.ts.map //# sourceMappingURL=execution-processor.d.ts.map

View File

@@ -1 +1 @@
{"version":3,"file":"execution-processor.d.ts","sourceRoot":"","sources":["../../src/services/execution-processor.ts"],"names":[],"mappings":"AAaA,OAAO,EACL,SAAS,EAET,gBAAgB,EAEhB,uBAAuB,EACvB,sBAAsB,EACtB,yBAAyB,EAG1B,MAAM,kBAAkB,CAAC;AA+G1B,wBAAgB,eAAe,CAAC,SAAS,EAAE,SAAS,GAAG;IACrD,OAAO,EAAE,gBAAgB,CAAC;IAC1B,cAAc,EAAE,uBAAuB,CAAC;CACzC,CA2EA;AAoID,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,sBAAsB,GAC9B,yBAAyB,CA2J3B;AAMD,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,SAAS,EACpB,OAAO,GAAE,sBAA2B,GACnC,yBAAyB,GAAG,SAAS,CAOvC"} {"version":3,"file":"execution-processor.d.ts","sourceRoot":"","sources":["../../src/services/execution-processor.ts"],"names":[],"mappings":"AAaA,OAAO,EACL,SAAS,EAET,gBAAgB,EAEhB,uBAAuB,EACvB,sBAAsB,EACtB,yBAAyB,EAGzB,QAAQ,EACT,MAAM,kBAAkB,CAAC;AAgH1B,wBAAgB,eAAe,CAAC,SAAS,EAAE,SAAS,GAAG;IACrD,OAAO,EAAE,gBAAgB,CAAC;IAC1B,cAAc,EAAE,uBAAuB,CAAC;CACzC,CA2EA;AAoID,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,sBAAsB,EAC/B,QAAQ,CAAC,EAAE,QAAQ,GAClB,yBAAyB,CAsL3B;AAMD,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,SAAS,EACpB,OAAO,GAAE,sBAA2B,EACpC,QAAQ,CAAC,EAAE,QAAQ,GAClB,yBAAyB,GAAG,SAAS,CAOvC"}

View File

@@ -4,6 +4,7 @@ exports.generatePreview = generatePreview;
exports.filterExecutionData = filterExecutionData; exports.filterExecutionData = filterExecutionData;
exports.processExecution = processExecution; exports.processExecution = processExecution;
const logger_1 = require("../utils/logger"); const logger_1 = require("../utils/logger");
const error_execution_processor_1 = require("./error-execution-processor");
const THRESHOLDS = { const THRESHOLDS = {
CHAR_SIZE_BYTES: 2, CHAR_SIZE_BYTES: 2,
OVERHEAD_PER_OBJECT: 50, OVERHEAD_PER_OBJECT: 50,
@@ -231,7 +232,7 @@ function truncateItems(items, limit) {
}, },
}; };
} }
function filterExecutionData(execution, options) { function filterExecutionData(execution, options, workflow) {
const mode = options.mode || 'summary'; const mode = options.mode || 'summary';
let itemsLimit = options.itemsLimit !== undefined ? options.itemsLimit : 2; let itemsLimit = options.itemsLimit !== undefined ? options.itemsLimit : 2;
if (itemsLimit !== -1) { if (itemsLimit !== -1) {
@@ -265,6 +266,27 @@ function filterExecutionData(execution, options) {
response.recommendation = recommendation; response.recommendation = recommendation;
return response; return response;
} }
if (mode === 'error') {
const errorAnalysis = (0, error_execution_processor_1.processErrorExecution)(execution, {
itemsLimit: options.errorItemsLimit ?? 2,
includeStackTrace: options.includeStackTrace ?? false,
includeExecutionPath: options.includeExecutionPath !== false,
workflow
});
const runData = execution.data?.resultData?.runData || {};
const executedNodes = Object.keys(runData).length;
response.errorInfo = errorAnalysis;
response.summary = {
totalNodes: executedNodes,
executedNodes,
totalItems: 0,
hasMoreData: false
};
if (execution.data?.resultData?.error) {
response.error = execution.data.resultData.error;
}
return response;
}
if (!execution.data?.resultData?.runData) { if (!execution.data?.resultData?.runData) {
response.summary = { response.summary = {
totalNodes: 0, totalNodes: 0,
@@ -350,10 +372,10 @@ function filterExecutionData(execution, options) {
} }
return response; return response;
} }
function processExecution(execution, options = {}) { function processExecution(execution, options = {}, workflow) {
if (!options.mode && !options.nodeNames && options.itemsLimit === undefined) { if (!options.mode && !options.nodeNames && options.itemsLimit === undefined) {
return execution; return execution;
} }
return filterExecutionData(execution, options); return filterExecutionData(execution, options, workflow);
} }
//# sourceMappingURL=execution-processor.js.map //# sourceMappingURL=execution-processor.js.map

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
{"version":3,"file":"batch-processor.d.ts","sourceRoot":"","sources":["../../src/telemetry/batch-processor.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,sBAAsB,EAAoB,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAyBlI,qBAAa,uBAAuB;IAoBhC,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,SAAS;IApBnB,OAAO,CAAC,UAAU,CAAC,CAAiB;IACpC,OAAO,CAAC,gBAAgB,CAAkB;IAC1C,OAAO,CAAC,mBAAmB,CAAkB;IAC7C,OAAO,CAAC,mBAAmB,CAAkB;IAC7C,OAAO,CAAC,cAAc,CAA0B;IAChD,OAAO,CAAC,OAAO,CAQb;IACF,OAAO,CAAC,UAAU,CAAgB;IAClC,OAAO,CAAC,eAAe,CAAuE;IAC9F,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAO;gBAG/B,QAAQ,EAAE,cAAc,GAAG,IAAI,EAC/B,SAAS,EAAE,MAAM,OAAO;IAQlC,KAAK,IAAI,IAAI;IA+Bb,IAAI,IAAI,IAAI;IAWN,KAAK,CAAC,MAAM,CAAC,EAAE,cAAc,EAAE,EAAE,SAAS,CAAC,EAAE,iBAAiB,EAAE,EAAE,SAAS,CAAC,EAAE,sBAAsB,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;YAgD9G,WAAW;YAmDX,cAAc;YAuDd,cAAc;YAiEd,gBAAgB;IAgD9B,OAAO,CAAC,aAAa;IAarB,OAAO,CAAC,oBAAoB;IAiB5B,OAAO,CAAC,oBAAoB;YAmBd,sBAAsB;IAgCpC,OAAO,CAAC,eAAe;IAiBvB,UAAU,IAAI,gBAAgB,GAAG;QAAE,mBAAmB,EAAE,GAAG,CAAC;QAAC,mBAAmB,EAAE,MAAM,CAAA;KAAE;IAW1F,YAAY,IAAI,IAAI;CAarB"} {"version":3,"file":"batch-processor.d.ts","sourceRoot":"","sources":["../../src/telemetry/batch-processor.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,sBAAsB,EAAoB,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAoClI,qBAAa,uBAAuB;IAoBhC,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,SAAS;IApBnB,OAAO,CAAC,UAAU,CAAC,CAAiB;IACpC,OAAO,CAAC,gBAAgB,CAAkB;IAC1C,OAAO,CAAC,mBAAmB,CAAkB;IAC7C,OAAO,CAAC,mBAAmB,CAAkB;IAC7C,OAAO,CAAC,cAAc,CAA0B;IAChD,OAAO,CAAC,OAAO,CAQb;IACF,OAAO,CAAC,UAAU,CAAgB;IAClC,OAAO,CAAC,eAAe,CAAuE;IAC9F,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAO;gBAG/B,QAAQ,EAAE,cAAc,GAAG,IAAI,EAC/B,SAAS,EAAE,MAAM,OAAO;IAQlC,KAAK,IAAI,IAAI;IA+Bb,IAAI,IAAI,IAAI;IAWN,KAAK,CAAC,MAAM,CAAC,EAAE,cAAc,EAAE,EAAE,SAAS,CAAC,EAAE,iBAAiB,EAAE,EAAE,SAAS,CAAC,EAAE,sBAAsB,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;YAgD9G,WAAW;YAmDX,cAAc;YAuDd,cAAc;YAiEd,gBAAgB;IAgD9B,OAAO,CAAC,aAAa;IAarB,OAAO,CAAC,oBAAoB;IAiB5B,OAAO,CAAC,oBAAoB;YAmBd,sBAAsB;IAgCpC,OAAO,CAAC,eAAe;IAiBvB,UAAU,IAAI,gBAAgB,GAAG;QAAE,mBAAmB,EAAE,GAAG,CAAC;QAAC,mBAAmB,EAAE,MAAM,CAAA;KAAE;IAW1F,YAAY,IAAI,IAAI;CAarB"}

View File

@@ -4,19 +4,13 @@ exports.TelemetryBatchProcessor = void 0;
const telemetry_types_1 = require("./telemetry-types"); const telemetry_types_1 = require("./telemetry-types");
const telemetry_error_1 = require("./telemetry-error"); const telemetry_error_1 = require("./telemetry-error");
const logger_1 = require("../utils/logger"); const logger_1 = require("../utils/logger");
function toSnakeCase(obj) { function keyToSnakeCase(key) {
if (obj === null || obj === undefined) return key.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`);
return obj; }
if (Array.isArray(obj)) function mutationToSupabaseFormat(mutation) {
return obj.map(toSnakeCase);
if (typeof obj !== 'object')
return obj;
const result = {}; const result = {};
for (const key in obj) { for (const [key, value] of Object.entries(mutation)) {
if (obj.hasOwnProperty(key)) { result[keyToSnakeCase(key)] = value;
const snakeKey = key.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`);
result[snakeKey] = toSnakeCase(obj[key]);
}
} }
return result; return result;
} }
@@ -185,7 +179,7 @@ class TelemetryBatchProcessor {
const batches = this.createBatches(mutations, telemetry_types_1.TELEMETRY_CONFIG.MAX_BATCH_SIZE); const batches = this.createBatches(mutations, telemetry_types_1.TELEMETRY_CONFIG.MAX_BATCH_SIZE);
for (const batch of batches) { for (const batch of batches) {
const result = await this.executeWithRetry(async () => { const result = await this.executeWithRetry(async () => {
const snakeCaseBatch = batch.map(mutation => toSnakeCase(mutation)); const snakeCaseBatch = batch.map(mutation => mutationToSupabaseFormat(mutation));
const { error } = await this.supabase const { error } = await this.supabase
.from('workflow_mutations') .from('workflow_mutations')
.insert(snakeCaseBatch); .insert(snakeCaseBatch);

File diff suppressed because one or more lines are too long

View File

@@ -7,6 +7,13 @@ export interface MCPServerConfig {
host: string; host: string;
authToken?: string; authToken?: string;
} }
export interface ToolAnnotations {
title?: string;
readOnlyHint?: boolean;
destructiveHint?: boolean;
idempotentHint?: boolean;
openWorldHint?: boolean;
}
export interface ToolDefinition { export interface ToolDefinition {
name: string; name: string;
description: string; description: string;
@@ -22,6 +29,7 @@ export interface ToolDefinition {
required?: string[]; required?: string[];
additionalProperties?: boolean | Record<string, any>; additionalProperties?: boolean | Record<string, any>;
}; };
annotations?: ToolAnnotations;
} }
export interface ResourceDefinition { export interface ResourceDefinition {
uri: string; uri: string;

View File

@@ -1 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AACA,cAAc,cAAc,CAAC;AAC7B,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAEhC,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE;QACX,IAAI,EAAE,MAAM,CAAC;QACb,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAChC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;QACpB,oBAAoB,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;KACtD,CAAC;IACF,YAAY,CAAC,EAAE;QACb,IAAI,EAAE,MAAM,CAAC;QACb,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAChC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;QACpB,oBAAoB,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;KACtD,CAAC;CACH;AAED,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,KAAK,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;KACpB,CAAC,CAAC;CACJ"} {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AACA,cAAc,cAAc,CAAC;AAC7B,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAEhC,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAMD,MAAM,WAAW,eAAe;IAE9B,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B,cAAc,CAAC,EAAE,OAAO,CAAC;IAEzB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE;QACX,IAAI,EAAE,MAAM,CAAC;QACb,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAChC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;QACpB,oBAAoB,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;KACtD,CAAC;IACF,YAAY,CAAC,EAAE;QACb,IAAI,EAAE,MAAM,CAAC;QACb,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAChC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;QACpB,oBAAoB,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;KACtD,CAAC;IAEF,WAAW,CAAC,EAAE,eAAe,CAAC;CAC/B;AAED,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,KAAK,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;KACpB,CAAC,CAAC;CACJ"}

View File

@@ -267,7 +267,7 @@ export interface McpToolResponse {
executionId?: string; executionId?: string;
workflowId?: string; workflowId?: string;
} }
export type ExecutionMode = 'preview' | 'summary' | 'filtered' | 'full'; export type ExecutionMode = 'preview' | 'summary' | 'filtered' | 'full' | 'error';
export interface ExecutionPreview { export interface ExecutionPreview {
totalNodes: number; totalNodes: number;
executedNodes: number; executedNodes: number;
@@ -296,6 +296,9 @@ export interface ExecutionFilterOptions {
itemsLimit?: number; itemsLimit?: number;
includeInputData?: boolean; includeInputData?: boolean;
fieldsToInclude?: string[]; fieldsToInclude?: string[];
errorItemsLimit?: number;
includeStackTrace?: boolean;
includeExecutionPath?: boolean;
} }
export interface FilteredExecutionResponse { export interface FilteredExecutionResponse {
id: string; id: string;
@@ -316,6 +319,7 @@ export interface FilteredExecutionResponse {
}; };
nodes?: Record<string, FilteredNodeData>; nodes?: Record<string, FilteredNodeData>;
error?: Record<string, unknown>; error?: Record<string, unknown>;
errorInfo?: ErrorAnalysis;
} }
export interface FilteredNodeData { export interface FilteredNodeData {
executionTime?: number; executionTime?: number;
@@ -333,4 +337,39 @@ export interface FilteredNodeData {
}; };
}; };
} }
export interface ErrorAnalysis {
primaryError: {
message: string;
errorType: string;
nodeName: string;
nodeType: string;
nodeId?: string;
nodeParameters?: Record<string, unknown>;
stackTrace?: string;
};
upstreamContext?: {
nodeName: string;
nodeType: string;
itemCount: number;
sampleItems: unknown[];
dataStructure: Record<string, unknown>;
};
executionPath?: Array<{
nodeName: string;
status: 'success' | 'error' | 'skipped';
itemCount: number;
executionTime?: number;
}>;
additionalErrors?: Array<{
nodeName: string;
message: string;
}>;
suggestions?: ErrorSuggestion[];
}
export interface ErrorSuggestion {
type: 'fix' | 'investigate' | 'workaround';
title: string;
description: string;
confidence: 'high' | 'medium' | 'low';
}
//# sourceMappingURL=n8n-api.d.ts.map //# sourceMappingURL=n8n-api.d.ts.map

File diff suppressed because one or more lines are too long

View File

@@ -12,7 +12,8 @@ services:
environment: environment:
# Mode configuration # Mode configuration
MCP_MODE: ${MCP_MODE:-http} MCP_MODE: ${MCP_MODE:-http}
USE_FIXED_HTTP: ${USE_FIXED_HTTP:-true} # Use fixed implementation for stability # NOTE: USE_FIXED_HTTP is deprecated. SingleSessionHTTPServer is now the default.
# See: https://github.com/czlonkowski/n8n-mcp/issues/524
AUTH_TOKEN: ${AUTH_TOKEN:?AUTH_TOKEN is required for HTTP mode} AUTH_TOKEN: ${AUTH_TOKEN:?AUTH_TOKEN is required for HTTP mode}
# Application settings # Application settings

View File

@@ -21,7 +21,6 @@ cd n8n-mcp
# Create .env file with auth token # Create .env file with auth token
cat > .env << EOF cat > .env << EOF
AUTH_TOKEN=$(openssl rand -base64 32) AUTH_TOKEN=$(openssl rand -base64 32)
USE_FIXED_HTTP=true
EOF EOF
# Start the server # Start the server
@@ -46,7 +45,6 @@ docker pull ghcr.io/czlonkowski/n8n-mcp:latest
docker run -d \ docker run -d \
--name n8n-mcp \ --name n8n-mcp \
-e MCP_MODE=http \ -e MCP_MODE=http \
-e USE_FIXED_HTTP=true \
-e AUTH_TOKEN=your-secure-token \ -e AUTH_TOKEN=your-secure-token \
-p 3000:3000 \ -p 3000:3000 \
ghcr.io/czlonkowski/n8n-mcp:latest ghcr.io/czlonkowski/n8n-mcp:latest

View File

@@ -67,7 +67,6 @@ Claude Desktop → mcp-remote → https://your-server.com
# 1. Create environment file # 1. Create environment file
cat > .env << EOF cat > .env << EOF
AUTH_TOKEN=$(openssl rand -base64 32) AUTH_TOKEN=$(openssl rand -base64 32)
USE_FIXED_HTTP=true
MCP_MODE=http MCP_MODE=http
PORT=3000 PORT=3000
# Optional: Enable n8n management tools # Optional: Enable n8n management tools
@@ -106,7 +105,6 @@ npm run rebuild
# 2. Configure environment # 2. Configure environment
export MCP_MODE=http export MCP_MODE=http
export USE_FIXED_HTTP=true # Important: Use fixed implementation
export AUTH_TOKEN=$(openssl rand -base64 32) export AUTH_TOKEN=$(openssl rand -base64 32)
export PORT=3000 export PORT=3000
@@ -144,7 +142,6 @@ Skip HTTP entirely and use stdio mode directly:
| Variable | Description | Example | | Variable | Description | Example |
|----------|-------------|------| |----------|-------------|------|
| `MCP_MODE` | Must be set to `http` | `http` | | `MCP_MODE` | Must be set to `http` | `http` |
| `USE_FIXED_HTTP` | **Important**: Set to `true` for stable implementation | `true` |
| `AUTH_TOKEN` or `AUTH_TOKEN_FILE` | Authentication method | See security section | | `AUTH_TOKEN` or `AUTH_TOKEN_FILE` | Authentication method | See security section |
### Optional Settings ### Optional Settings
@@ -417,7 +414,6 @@ services:
environment: environment:
# Core configuration # Core configuration
MCP_MODE: http MCP_MODE: http
USE_FIXED_HTTP: true
NODE_ENV: production NODE_ENV: production
# Security - Using file-based secret # Security - Using file-based secret
@@ -500,7 +496,6 @@ WorkingDirectory=/opt/n8n-mcp
# Use file-based secret # Use file-based secret
Environment="AUTH_TOKEN_FILE=/etc/n8n-mcp/auth_token" Environment="AUTH_TOKEN_FILE=/etc/n8n-mcp/auth_token"
Environment="MCP_MODE=http" Environment="MCP_MODE=http"
Environment="USE_FIXED_HTTP=true"
Environment="NODE_ENV=production" Environment="NODE_ENV=production"
Environment="TRUST_PROXY=1" Environment="TRUST_PROXY=1"
Environment="BASE_URL=https://n8n-mcp.example.com" Environment="BASE_URL=https://n8n-mcp.example.com"
@@ -772,8 +767,8 @@ sudo ufw status # Linux
``` ```
**"Stream is not readable":** **"Stream is not readable":**
- Ensure `USE_FIXED_HTTP=true` is set - This issue was fixed in v2.3.2+ with the SingleSessionHTTPServer
- Fixed in v2.3.2+ - No additional configuration needed
**Bridge script not working:** **Bridge script not working:**
```bash ```bash

View File

@@ -18,7 +18,6 @@ The fastest way to get n8n-MCP running:
# Using Docker (recommended) # Using Docker (recommended)
cat > .env << EOF cat > .env << EOF
AUTH_TOKEN=$(openssl rand -base64 32) AUTH_TOKEN=$(openssl rand -base64 32)
USE_FIXED_HTTP=true
EOF EOF
docker compose up -d docker compose up -d
``` ```
@@ -49,7 +48,6 @@ docker compose up -d
environment: environment:
MCP_MODE: ${MCP_MODE:-http} MCP_MODE: ${MCP_MODE:-http}
USE_FIXED_HTTP: ${USE_FIXED_HTTP:-true}
AUTH_TOKEN: ${AUTH_TOKEN:?AUTH_TOKEN is required} AUTH_TOKEN: ${AUTH_TOKEN:?AUTH_TOKEN is required}
NODE_ENV: ${NODE_ENV:-production} NODE_ENV: ${NODE_ENV:-production}
LOG_LEVEL: ${LOG_LEVEL:-info} LOG_LEVEL: ${LOG_LEVEL:-info}

View File

@@ -98,7 +98,6 @@ These are automatically set by the Railway template:
|----------|--------------|-------------| |----------|--------------|-------------|
| `AUTH_TOKEN` | `REPLACE_THIS...` | **⚠️ CHANGE IMMEDIATELY** | | `AUTH_TOKEN` | `REPLACE_THIS...` | **⚠️ CHANGE IMMEDIATELY** |
| `MCP_MODE` | `http` | Required for cloud deployment | | `MCP_MODE` | `http` | Required for cloud deployment |
| `USE_FIXED_HTTP` | `true` | Stable HTTP implementation |
| `NODE_ENV` | `production` | Production optimizations | | `NODE_ENV` | `production` | Production optimizations |
| `LOG_LEVEL` | `info` | Balanced logging | | `LOG_LEVEL` | `info` | Balanced logging |
| `TRUST_PROXY` | `1` | Railway runs behind proxy | | `TRUST_PROXY` | `1` | Railway runs behind proxy |

View File

@@ -40,7 +40,6 @@ Key configuration options:
| Variable | Description | Default | | Variable | Description | Default |
|----------|-------------|---------| |----------|-------------|---------|
| `MCP_MODE` | Server mode: `stdio` or `http` | `stdio` | | `MCP_MODE` | Server mode: `stdio` or `http` | `stdio` |
| `USE_FIXED_HTTP` | Use fixed HTTP implementation (v2.3.2+) | `true` |
| `AUTH_TOKEN` | Authentication token for HTTP mode | Required | | `AUTH_TOKEN` | Authentication token for HTTP mode | Required |
| `PORT` | HTTP server port | `3000` | | `PORT` | HTTP server port | `3000` |
| `LOG_LEVEL` | Logging verbosity | `info` | | `LOG_LEVEL` | Logging verbosity | `info` |

747
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "n8n-mcp", "name": "n8n-mcp",
"version": "2.31.1", "version": "2.31.8",
"description": "Integration between n8n workflow automation and Model Context Protocol (MCP)", "description": "Integration between n8n workflow automation and Model Context Protocol (MCP)",
"main": "dist/index.js", "main": "dist/index.js",
"types": "dist/index.d.ts", "types": "dist/index.d.ts",
@@ -22,9 +22,9 @@
"test-nodes": "node dist/scripts/test-nodes.js", "test-nodes": "node dist/scripts/test-nodes.js",
"start": "node dist/mcp/index.js", "start": "node dist/mcp/index.js",
"start:http": "MCP_MODE=http node dist/mcp/index.js", "start:http": "MCP_MODE=http node dist/mcp/index.js",
"start:http:fixed": "MCP_MODE=http USE_FIXED_HTTP=true node dist/mcp/index.js", "start:http:fixed:deprecated": "echo 'DEPRECATED: USE_FIXED_HTTP is deprecated. Use npm run start:http instead.' && MCP_MODE=http USE_FIXED_HTTP=true node dist/mcp/index.js",
"start:n8n": "N8N_MODE=true MCP_MODE=http node dist/mcp/index.js", "start:n8n": "N8N_MODE=true MCP_MODE=http node dist/mcp/index.js",
"http": "npm run build && npm run start:http:fixed", "http": "npm run build && npm run start:http",
"dev": "npm run build && npm run rebuild && npm run validate", "dev": "npm run build && npm run rebuild && npm run validate",
"dev:http": "MCP_MODE=http nodemon --watch src --ext ts --exec 'npm run build && npm run start:http'", "dev:http": "MCP_MODE=http nodemon --watch src --ext ts --exec 'npm run build && npm run start:http'",
"test:single-session": "./scripts/test-single-session.sh", "test:single-session": "./scripts/test-single-session.sh",
@@ -141,16 +141,16 @@
}, },
"dependencies": { "dependencies": {
"@modelcontextprotocol/sdk": "1.20.1", "@modelcontextprotocol/sdk": "1.20.1",
"@n8n/n8n-nodes-langchain": "^2.0.1", "@n8n/n8n-nodes-langchain": "^2.2.2",
"@supabase/supabase-js": "^2.57.4", "@supabase/supabase-js": "^2.57.4",
"dotenv": "^16.5.0", "dotenv": "^16.5.0",
"express": "^5.1.0", "express": "^5.1.0",
"express-rate-limit": "^7.1.5", "express-rate-limit": "^7.1.5",
"form-data": "^4.0.5", "form-data": "^4.0.5",
"lru-cache": "^11.2.1", "lru-cache": "^11.2.1",
"n8n": "^2.0.2", "n8n": "^2.2.3",
"n8n-core": "^2.0.1", "n8n-core": "^2.2.2",
"n8n-workflow": "^2.0.1", "n8n-workflow": "^2.2.2",
"openai": "^4.77.0", "openai": "^4.77.0",
"sql.js": "^1.13.0", "sql.js": "^1.13.0",
"tslib": "^2.6.2", "tslib": "^2.6.2",

View File

@@ -71,10 +71,12 @@ const testCases: TestCase[] = [
} }
}, },
{ {
name: 'Fixed HTTP implementation', // DEPRECATED: This test case tests the deprecated fixed HTTP implementation
// See: https://github.com/czlonkowski/n8n-mcp/issues/524
name: 'Fixed HTTP implementation (DEPRECATED)',
env: { env: {
MCP_MODE: 'http', MCP_MODE: 'http',
USE_FIXED_HTTP: 'true', USE_FIXED_HTTP: 'true', // DEPRECATED: Will be removed in future version
AUTH_TOKEN: 'test-token-for-testing-only', AUTH_TOKEN: 'test-token-for-testing-only',
PORT: '3005', PORT: '3005',
BASE_URL: 'https://fixed.example.com' BASE_URL: 'https://fixed.example.com'

View File

@@ -1,7 +1,14 @@
#!/usr/bin/env node #!/usr/bin/env node
/** /**
* Fixed HTTP server for n8n-MCP that properly handles StreamableHTTPServerTransport initialization * @deprecated This fixed HTTP server is deprecated as of v2.31.8.
* This implementation ensures the transport is properly initialized before handling requests * Use SingleSessionHTTPServer from http-server-single-session.ts instead.
*
* This implementation does not support SSE streaming required by clients like OpenAI Codex.
* See: https://github.com/czlonkowski/n8n-mcp/issues/524
*
* Original purpose: Fixed HTTP server for n8n-MCP that properly handles
* StreamableHTTPServerTransport initialization by bypassing it entirely.
* This implementation ensures the transport is properly initialized before handling requests.
*/ */
import express from 'express'; import express from 'express';
import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { Server } from '@modelcontextprotocol/sdk/server/index.js';
@@ -125,7 +132,18 @@ async function shutdown() {
} }
} }
/**
* @deprecated Use SingleSessionHTTPServer from http-server-single-session.ts instead.
* This function does not support SSE streaming required by clients like OpenAI Codex.
*/
export async function startFixedHTTPServer() { export async function startFixedHTTPServer() {
// Log deprecation warning
logger.warn(
'DEPRECATION: startFixedHTTPServer() is deprecated as of v2.31.8. ' +
'Use SingleSessionHTTPServer which supports SSE streaming. ' +
'See: https://github.com/czlonkowski/n8n-mcp/issues/524'
);
validateEnvironment(); validateEnvironment();
const app = express(); const app = express();

View File

@@ -124,9 +124,23 @@ Learn more: https://github.com/czlonkowski/n8n-mcp/blob/main/PRIVACY.md
checkpoints.push(STARTUP_CHECKPOINTS.MCP_HANDSHAKE_STARTING); checkpoints.push(STARTUP_CHECKPOINTS.MCP_HANDSHAKE_STARTING);
if (mode === 'http') { if (mode === 'http') {
// Check if we should use the fixed implementation // Check if we should use the fixed implementation (DEPRECATED)
if (process.env.USE_FIXED_HTTP === 'true') { if (process.env.USE_FIXED_HTTP === 'true') {
// Use the fixed HTTP implementation that bypasses StreamableHTTPServerTransport issues // DEPRECATION WARNING: Fixed HTTP implementation is deprecated
// It does not support SSE streaming required by clients like OpenAI Codex
logger.warn(
'DEPRECATION WARNING: USE_FIXED_HTTP=true is deprecated as of v2.31.8. ' +
'The fixed HTTP implementation does not support SSE streaming required by clients like OpenAI Codex. ' +
'Please unset USE_FIXED_HTTP to use the modern SingleSessionHTTPServer which supports both JSON-RPC and SSE. ' +
'This option will be removed in a future version. See: https://github.com/czlonkowski/n8n-mcp/issues/524'
);
console.warn('\n⚠ DEPRECATION WARNING ⚠️');
console.warn('USE_FIXED_HTTP=true is deprecated as of v2.31.8.');
console.warn('The fixed HTTP implementation does not support SSE streaming.');
console.warn('Please unset USE_FIXED_HTTP to use SingleSessionHTTPServer.');
console.warn('See: https://github.com/czlonkowski/n8n-mcp/issues/524\n');
// Use the deprecated fixed HTTP implementation
const { startFixedHTTPServer } = await import('../http-server'); const { startFixedHTTPServer } = await import('../http-server');
await startFixedHTTPServer(); await startFixedHTTPServer();
} else { } else {

View File

@@ -42,7 +42,7 @@ export const getTemplateDoc: ToolDocumentation = {
- url: Link to template on n8n.io - url: Link to template on n8n.io
- workflow: Complete workflow JSON with structure: - workflow: Complete workflow JSON with structure:
- nodes: Array of node objects (id, name, type, typeVersion, position, parameters) - nodes: Array of node objects (id, name, type, typeVersion, position, parameters)
- connections: Object mapping source nodes to targets - connections: Object mapping source node names to targets
- settings: Workflow configuration (timezone, error handling, etc.) - settings: Workflow configuration (timezone, error handling, etc.)
- usage: Instructions for using the workflow`, - usage: Instructions for using the workflow`,
examples: [ examples: [

View File

@@ -20,7 +20,7 @@ export const n8nCreateWorkflowDoc: ToolDocumentation = {
parameters: { parameters: {
name: { type: 'string', required: true, description: 'Workflow name' }, name: { type: 'string', required: true, description: 'Workflow name' },
nodes: { type: 'array', required: true, description: 'Array of nodes with id, name, type, typeVersion, position, parameters' }, nodes: { type: 'array', required: true, description: 'Array of nodes with id, name, type, typeVersion, position, parameters' },
connections: { type: 'object', required: true, description: 'Node connections. Keys are source node IDs' }, connections: { type: 'object', required: true, description: 'Node connections. Keys are source node names (not IDs)' },
settings: { type: 'object', description: 'Optional workflow settings (timezone, error handling, etc.)' } settings: { type: 'object', description: 'Optional workflow settings (timezone, error handling, etc.)' }
}, },
returns: 'Minimal summary (id, name, active, nodeCount) for token efficiency. Use n8n_get_workflow with mode "structure" to verify current state if needed.', returns: 'Minimal summary (id, name, active, nodeCount) for token efficiency. Use n8n_get_workflow with mode "structure" to verify current state if needed.',
@@ -55,8 +55,8 @@ n8n_create_workflow({
} }
], ],
connections: { connections: {
"webhook_1": { "Webhook": {
"main": [[{node: "slack_1", type: "main", index: 0}]] "main": [[{node: "Slack", type: "main", index: 0}]]
} }
} }
})`, })`,

View File

@@ -46,9 +46,9 @@ export const n8nManagementTools: ToolDefinition[] = [
} }
} }
}, },
connections: { connections: {
type: 'object', type: 'object',
description: 'Workflow connections object. Keys are source node IDs, values define output connections' description: 'Workflow connections object. Keys are source node names (the name field, not id), values define output connections'
}, },
settings: { settings: {
type: 'object', type: 'object',
@@ -66,7 +66,13 @@ export const n8nManagementTools: ToolDefinition[] = [
} }
}, },
required: ['name', 'nodes', 'connections'] required: ['name', 'nodes', 'connections']
} },
annotations: {
title: 'Create Workflow',
readOnlyHint: false,
destructiveHint: false,
openWorldHint: true,
},
}, },
{ {
name: 'n8n_get_workflow', name: 'n8n_get_workflow',
@@ -86,7 +92,13 @@ export const n8nManagementTools: ToolDefinition[] = [
} }
}, },
required: ['id'] required: ['id']
} },
annotations: {
title: 'Get Workflow',
readOnlyHint: true,
idempotentHint: true,
openWorldHint: true,
},
}, },
{ {
name: 'n8n_update_full_workflow', name: 'n8n_update_full_workflow',
@@ -120,7 +132,14 @@ export const n8nManagementTools: ToolDefinition[] = [
} }
}, },
required: ['id'] required: ['id']
} },
annotations: {
title: 'Update Full Workflow',
readOnlyHint: false,
destructiveHint: false,
idempotentHint: true,
openWorldHint: true,
},
}, },
{ {
name: 'n8n_update_partial_workflow', name: 'n8n_update_partial_workflow',
@@ -151,7 +170,14 @@ export const n8nManagementTools: ToolDefinition[] = [
} }
}, },
required: ['id', 'operations'] required: ['id', 'operations']
} },
annotations: {
title: 'Update Partial Workflow',
readOnlyHint: false,
destructiveHint: false,
idempotentHint: true,
openWorldHint: true,
},
}, },
{ {
name: 'n8n_delete_workflow', name: 'n8n_delete_workflow',
@@ -165,7 +191,13 @@ export const n8nManagementTools: ToolDefinition[] = [
} }
}, },
required: ['id'] required: ['id']
} },
annotations: {
title: 'Delete Workflow',
readOnlyHint: false,
destructiveHint: true,
openWorldHint: true,
},
}, },
{ {
name: 'n8n_list_workflows', name: 'n8n_list_workflows',
@@ -194,12 +226,18 @@ export const n8nManagementTools: ToolDefinition[] = [
type: 'string', type: 'string',
description: 'Filter by project ID (enterprise feature)' description: 'Filter by project ID (enterprise feature)'
}, },
excludePinnedData: { excludePinnedData: {
type: 'boolean', type: 'boolean',
description: 'Exclude pinned data from response (default: true)' description: 'Exclude pinned data from response (default: true)'
} }
} }
} },
annotations: {
title: 'List Workflows',
readOnlyHint: true,
idempotentHint: true,
openWorldHint: true,
},
}, },
{ {
name: 'n8n_validate_workflow', name: 'n8n_validate_workflow',
@@ -227,16 +265,22 @@ export const n8nManagementTools: ToolDefinition[] = [
type: 'boolean', type: 'boolean',
description: 'Validate n8n expressions (default: true)' description: 'Validate n8n expressions (default: true)'
}, },
profile: { profile: {
type: 'string', type: 'string',
enum: ['minimal', 'runtime', 'ai-friendly', 'strict'], enum: ['minimal', 'runtime', 'ai-friendly', 'strict'],
description: 'Validation profile to use (default: runtime)' description: 'Validation profile to use (default: runtime)'
} }
} }
} }
}, },
required: ['id'] required: ['id']
} },
annotations: {
title: 'Validate Workflow',
readOnlyHint: true,
idempotentHint: true,
openWorldHint: true,
},
}, },
{ {
name: 'n8n_autofix_workflow', name: 'n8n_autofix_workflow',
@@ -271,7 +315,14 @@ export const n8nManagementTools: ToolDefinition[] = [
} }
}, },
required: ['id'] required: ['id']
} },
annotations: {
title: 'Autofix Workflow',
readOnlyHint: false,
destructiveHint: false,
idempotentHint: true,
openWorldHint: true,
},
}, },
// Execution Management Tools // Execution Management Tools
@@ -328,7 +379,13 @@ export const n8nManagementTools: ToolDefinition[] = [
} }
}, },
required: ['workflowId'] required: ['workflowId']
} },
annotations: {
title: 'Test Workflow',
readOnlyHint: false,
destructiveHint: false,
openWorldHint: true,
},
}, },
{ {
name: 'n8n_executions', name: 'n8n_executions',
@@ -410,7 +467,13 @@ export const n8nManagementTools: ToolDefinition[] = [
} }
}, },
required: ['action'] required: ['action']
} },
annotations: {
title: 'Manage Executions',
readOnlyHint: false,
destructiveHint: true,
openWorldHint: true,
},
}, },
// System Tools // System Tools
@@ -431,7 +494,13 @@ export const n8nManagementTools: ToolDefinition[] = [
description: 'Include extra details in diagnostic mode (default: false)' description: 'Include extra details in diagnostic mode (default: false)'
} }
} }
} },
annotations: {
title: 'Health Check',
readOnlyHint: true,
idempotentHint: true,
openWorldHint: true,
},
}, },
{ {
name: 'n8n_workflow_versions', name: 'n8n_workflow_versions',
@@ -485,7 +554,13 @@ export const n8nManagementTools: ToolDefinition[] = [
} }
}, },
required: ['mode'] required: ['mode']
} },
annotations: {
title: 'Workflow Versions',
readOnlyHint: false,
destructiveHint: true,
openWorldHint: true,
},
}, },
// Template Deployment Tool // Template Deployment Tool
@@ -520,6 +595,12 @@ export const n8nManagementTools: ToolDefinition[] = [
} }
}, },
required: ['templateId'] required: ['templateId']
} },
annotations: {
title: 'Deploy Template',
readOnlyHint: false,
destructiveHint: false,
openWorldHint: true,
},
} }
]; ];

View File

@@ -25,6 +25,11 @@ export const n8nDocumentationToolsFinal: ToolDefinition[] = [
}, },
}, },
}, },
annotations: {
title: 'Tools Documentation',
readOnlyHint: true,
idempotentHint: true,
},
}, },
{ {
name: 'search_nodes', name: 'search_nodes',
@@ -55,6 +60,11 @@ export const n8nDocumentationToolsFinal: ToolDefinition[] = [
}, },
required: ['query'], required: ['query'],
}, },
annotations: {
title: 'Search Nodes',
readOnlyHint: true,
idempotentHint: true,
},
}, },
{ {
name: 'get_node', name: 'get_node',
@@ -108,6 +118,11 @@ export const n8nDocumentationToolsFinal: ToolDefinition[] = [
}, },
required: ['nodeType'], required: ['nodeType'],
}, },
annotations: {
title: 'Get Node Info',
readOnlyHint: true,
idempotentHint: true,
},
}, },
{ {
name: 'validate_node', name: 'validate_node',
@@ -188,6 +203,11 @@ export const n8nDocumentationToolsFinal: ToolDefinition[] = [
}, },
required: ['nodeType', 'displayName', 'valid'] required: ['nodeType', 'displayName', 'valid']
}, },
annotations: {
title: 'Validate Node Config',
readOnlyHint: true,
idempotentHint: true,
},
}, },
{ {
name: 'get_template', name: 'get_template',
@@ -208,6 +228,11 @@ export const n8nDocumentationToolsFinal: ToolDefinition[] = [
}, },
required: ['templateId'], required: ['templateId'],
}, },
annotations: {
title: 'Get Template',
readOnlyHint: true,
idempotentHint: true,
},
}, },
{ {
name: 'search_templates', name: 'search_templates',
@@ -303,6 +328,11 @@ export const n8nDocumentationToolsFinal: ToolDefinition[] = [
}, },
}, },
}, },
annotations: {
title: 'Search Templates',
readOnlyHint: true,
idempotentHint: true,
},
}, },
{ {
name: 'validate_workflow', name: 'validate_workflow',
@@ -388,6 +418,11 @@ export const n8nDocumentationToolsFinal: ToolDefinition[] = [
}, },
required: ['valid', 'summary'] required: ['valid', 'summary']
}, },
annotations: {
title: 'Validate Workflow',
readOnlyHint: true,
idempotentHint: true,
},
}, },
]; ];

View File

@@ -9,23 +9,34 @@ import { TelemetryError, TelemetryErrorType, TelemetryCircuitBreaker } from './t
import { logger } from '../utils/logger'; import { logger } from '../utils/logger';
/** /**
* Convert camelCase object keys to snake_case * Convert camelCase key to snake_case
* Needed because Supabase PostgREST doesn't auto-convert
*/ */
function toSnakeCase(obj: any): any { function keyToSnakeCase(key: string): string {
if (obj === null || obj === undefined) return obj; return key.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`);
if (Array.isArray(obj)) return obj.map(toSnakeCase); }
if (typeof obj !== 'object') return obj;
const result: any = {}; /**
for (const key in obj) { * Convert WorkflowMutationRecord to Supabase-compatible format.
if (obj.hasOwnProperty(key)) { *
// Convert camelCase to snake_case * IMPORTANT: Only converts top-level field names to snake_case.
const snakeKey = key.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`); * Nested workflow data (workflowBefore, workflowAfter, operations, etc.)
// Recursively convert nested objects * is preserved EXACTLY as-is to maintain n8n API compatibility.
result[snakeKey] = toSnakeCase(obj[key]); *
} * The Supabase workflow_mutations table stores workflow_before and
* workflow_after as JSONB columns, which preserve the original structure.
* Only the top-level columns (user_id, session_id, etc.) require snake_case.
*
* Issue #517: Previously this used recursive conversion which mangled:
* - Connection keys (node names like "Webhook" → "_webhook")
* - Node field names (typeVersion → type_version)
*/
function mutationToSupabaseFormat(mutation: WorkflowMutationRecord): Record<string, any> {
const result: Record<string, any> = {};
for (const [key, value] of Object.entries(mutation)) {
result[keyToSnakeCase(key)] = value;
} }
return result; return result;
} }
@@ -266,7 +277,7 @@ export class TelemetryBatchProcessor {
for (const batch of batches) { for (const batch of batches) {
const result = await this.executeWithRetry(async () => { const result = await this.executeWithRetry(async () => {
// Convert camelCase to snake_case for Supabase // Convert camelCase to snake_case for Supabase
const snakeCaseBatch = batch.map(mutation => toSnakeCase(mutation)); const snakeCaseBatch = batch.map(mutation => mutationToSupabaseFormat(mutation));
const { error } = await this.supabase! const { error } = await this.supabase!
.from('workflow_mutations') .from('workflow_mutations')

View File

@@ -10,6 +10,23 @@ export interface MCPServerConfig {
authToken?: string; authToken?: string;
} }
/**
* MCP Tool annotations to help AI assistants understand tool behavior.
* Per MCP spec: https://spec.modelcontextprotocol.io/specification/2025-03-26/server/tools/#annotations
*/
export interface ToolAnnotations {
/** Human-readable title for the tool */
title?: string;
/** If true, the tool does not modify its environment */
readOnlyHint?: boolean;
/** If true, the tool may perform destructive updates to its environment */
destructiveHint?: boolean;
/** If true, calling the tool repeatedly with the same arguments has no additional effect */
idempotentHint?: boolean;
/** If true, the tool may interact with external entities (APIs, services) */
openWorldHint?: boolean;
}
export interface ToolDefinition { export interface ToolDefinition {
name: string; name: string;
description: string; description: string;
@@ -25,6 +42,8 @@ export interface ToolDefinition {
required?: string[]; required?: string[];
additionalProperties?: boolean | Record<string, any>; additionalProperties?: boolean | Record<string, any>;
}; };
/** Tool behavior hints for AI assistants */
annotations?: ToolAnnotations;
} }
export interface ResourceDefinition { export interface ResourceDefinition {

View File

@@ -1,7 +1,9 @@
import { describe, it, expect, beforeEach, vi, afterEach, beforeAll, afterAll, type MockInstance } from 'vitest'; import { describe, it, expect, beforeEach, vi, afterEach, beforeAll, afterAll, type MockInstance } from 'vitest';
import { TelemetryBatchProcessor } from '../../../src/telemetry/batch-processor'; import { TelemetryBatchProcessor } from '../../../src/telemetry/batch-processor';
import { TelemetryEvent, WorkflowTelemetry, TELEMETRY_CONFIG } from '../../../src/telemetry/telemetry-types'; import { TelemetryEvent, WorkflowTelemetry, WorkflowMutationRecord, TELEMETRY_CONFIG } from '../../../src/telemetry/telemetry-types';
import { TelemetryError, TelemetryErrorType } from '../../../src/telemetry/telemetry-error'; import { TelemetryError, TelemetryErrorType } from '../../../src/telemetry/telemetry-error';
import { IntentClassification, MutationToolName } from '../../../src/telemetry/mutation-types';
import { AddNodeOperation } from '../../../src/types/workflow-diff';
import type { SupabaseClient } from '@supabase/supabase-js'; import type { SupabaseClient } from '@supabase/supabase-js';
// Mock logger to avoid console output in tests // Mock logger to avoid console output in tests
@@ -679,4 +681,258 @@ describe('TelemetryBatchProcessor', () => {
expect(mockProcessExit).toHaveBeenCalledWith(0); expect(mockProcessExit).toHaveBeenCalledWith(0);
}); });
}); });
describe('Issue #517: workflow data preservation', () => {
// This test verifies that workflow mutation data is NOT recursively converted to snake_case
// Previously, the toSnakeCase function was applied recursively which caused:
// - Connection keys like "Webhook" to become "_webhook"
// - Node fields like "typeVersion" to become "type_version"
it('should preserve connection keys exactly as-is (node names)', async () => {
const mutation: WorkflowMutationRecord = {
userId: 'user1',
sessionId: 'session1',
workflowBefore: {
nodes: [],
connections: {}
},
workflowAfter: {
nodes: [
{ id: '1', name: 'Webhook', type: 'n8n-nodes-base.webhook', typeVersion: 1, position: [0, 0], parameters: {} }
],
// Connection keys are NODE NAMES - must be preserved exactly
connections: {
'Webhook': { main: [[{ node: 'AI Agent', type: 'main', index: 0 }]] },
'AI Agent': { main: [[{ node: 'HTTP Request', type: 'main', index: 0 }]] },
'HTTP Request': { main: [[{ node: 'Send Email', type: 'main', index: 0 }]] }
}
},
workflowHashBefore: 'hash1',
workflowHashAfter: 'hash2',
userIntent: 'Test',
intentClassification: IntentClassification.ADD_FUNCTIONALITY,
toolName: MutationToolName.UPDATE_PARTIAL,
operations: [],
operationCount: 0,
operationTypes: [],
validationImproved: null,
errorsResolved: 0,
errorsIntroduced: 0,
nodesAdded: 1,
nodesRemoved: 0,
nodesModified: 0,
connectionsAdded: 3,
connectionsRemoved: 0,
propertiesChanged: 0,
mutationSuccess: true,
durationMs: 100
};
let capturedData: any = null;
vi.mocked(mockSupabase.from).mockImplementation((table) => ({
insert: vi.fn().mockImplementation((data) => {
if (table === 'workflow_mutations') {
capturedData = data;
}
return Promise.resolve(createMockSupabaseResponse());
}),
url: { href: '' },
headers: {},
select: vi.fn(),
upsert: vi.fn(),
update: vi.fn(),
delete: vi.fn()
} as any));
await batchProcessor.flush(undefined, undefined, [mutation]);
expect(capturedData).toBeDefined();
expect(capturedData).toHaveLength(1);
const savedMutation = capturedData[0];
// Top-level keys should be snake_case for Supabase
expect(savedMutation).toHaveProperty('user_id');
expect(savedMutation).toHaveProperty('session_id');
expect(savedMutation).toHaveProperty('workflow_after');
// Connection keys should be preserved EXACTLY (not "_webhook", "_a_i _agent", etc.)
const connections = savedMutation.workflow_after.connections;
expect(connections).toHaveProperty('Webhook'); // NOT "_webhook"
expect(connections).toHaveProperty('AI Agent'); // NOT "_a_i _agent"
expect(connections).toHaveProperty('HTTP Request'); // NOT "_h_t_t_p _request"
});
it('should preserve node field names in camelCase', async () => {
const mutation: WorkflowMutationRecord = {
userId: 'user1',
sessionId: 'session1',
workflowBefore: { nodes: [], connections: {} },
workflowAfter: {
nodes: [
{
id: '1',
name: 'Webhook',
type: 'n8n-nodes-base.webhook',
// These fields MUST remain in camelCase for n8n API compatibility
typeVersion: 2,
webhookId: 'abc123',
onError: 'continueOnFail',
alwaysOutputData: true,
continueOnFail: false,
retryOnFail: true,
maxTries: 3,
notesInFlow: true,
waitBetweenTries: 1000,
executeOnce: false,
position: [100, 200],
parameters: {}
}
],
connections: {}
},
workflowHashBefore: 'hash1',
workflowHashAfter: 'hash2',
userIntent: 'Test',
intentClassification: IntentClassification.ADD_FUNCTIONALITY,
toolName: MutationToolName.UPDATE_PARTIAL,
operations: [],
operationCount: 0,
operationTypes: [],
validationImproved: null,
errorsResolved: 0,
errorsIntroduced: 0,
nodesAdded: 1,
nodesRemoved: 0,
nodesModified: 0,
connectionsAdded: 0,
connectionsRemoved: 0,
propertiesChanged: 0,
mutationSuccess: true,
durationMs: 100
};
let capturedData: any = null;
vi.mocked(mockSupabase.from).mockImplementation((table) => ({
insert: vi.fn().mockImplementation((data) => {
if (table === 'workflow_mutations') {
capturedData = data;
}
return Promise.resolve(createMockSupabaseResponse());
}),
url: { href: '' },
headers: {},
select: vi.fn(),
upsert: vi.fn(),
update: vi.fn(),
delete: vi.fn()
} as any));
await batchProcessor.flush(undefined, undefined, [mutation]);
expect(capturedData).toBeDefined();
const savedNode = capturedData[0].workflow_after.nodes[0];
// Node fields should be preserved in camelCase (NOT snake_case)
expect(savedNode).toHaveProperty('typeVersion'); // NOT type_version
expect(savedNode).toHaveProperty('webhookId'); // NOT webhook_id
expect(savedNode).toHaveProperty('onError'); // NOT on_error
expect(savedNode).toHaveProperty('alwaysOutputData'); // NOT always_output_data
expect(savedNode).toHaveProperty('continueOnFail'); // NOT continue_on_fail
expect(savedNode).toHaveProperty('retryOnFail'); // NOT retry_on_fail
expect(savedNode).toHaveProperty('maxTries'); // NOT max_tries
expect(savedNode).toHaveProperty('notesInFlow'); // NOT notes_in_flow
expect(savedNode).toHaveProperty('waitBetweenTries'); // NOT wait_between_tries
expect(savedNode).toHaveProperty('executeOnce'); // NOT execute_once
// Verify values are preserved
expect(savedNode.typeVersion).toBe(2);
expect(savedNode.webhookId).toBe('abc123');
expect(savedNode.maxTries).toBe(3);
});
it('should convert only top-level mutation record fields to snake_case', async () => {
const mutation: WorkflowMutationRecord = {
userId: 'user1',
sessionId: 'session1',
workflowBefore: { nodes: [], connections: {} },
workflowAfter: { nodes: [], connections: {} },
workflowHashBefore: 'hash1',
workflowHashAfter: 'hash2',
workflowStructureHashBefore: 'struct1',
workflowStructureHashAfter: 'struct2',
isTrulySuccessful: true,
userIntent: 'Test intent',
intentClassification: IntentClassification.ADD_FUNCTIONALITY,
toolName: MutationToolName.UPDATE_PARTIAL,
operations: [{ type: 'addNode', node: { name: 'Test', type: 'n8n-nodes-base.set', position: [0, 0] } } as AddNodeOperation],
operationCount: 1,
operationTypes: ['addNode'],
validationBefore: { valid: false, errors: [] },
validationAfter: { valid: true, errors: [] },
validationImproved: true,
errorsResolved: 1,
errorsIntroduced: 0,
nodesAdded: 1,
nodesRemoved: 0,
nodesModified: 0,
connectionsAdded: 0,
connectionsRemoved: 0,
propertiesChanged: 0,
mutationSuccess: true,
mutationError: undefined,
durationMs: 150
};
let capturedData: any = null;
vi.mocked(mockSupabase.from).mockImplementation((table) => ({
insert: vi.fn().mockImplementation((data) => {
if (table === 'workflow_mutations') {
capturedData = data;
}
return Promise.resolve(createMockSupabaseResponse());
}),
url: { href: '' },
headers: {},
select: vi.fn(),
upsert: vi.fn(),
update: vi.fn(),
delete: vi.fn()
} as any));
await batchProcessor.flush(undefined, undefined, [mutation]);
expect(capturedData).toBeDefined();
const saved = capturedData[0];
// Top-level fields should be converted to snake_case
expect(saved).toHaveProperty('user_id', 'user1');
expect(saved).toHaveProperty('session_id', 'session1');
expect(saved).toHaveProperty('workflow_before');
expect(saved).toHaveProperty('workflow_after');
expect(saved).toHaveProperty('workflow_hash_before', 'hash1');
expect(saved).toHaveProperty('workflow_hash_after', 'hash2');
expect(saved).toHaveProperty('workflow_structure_hash_before', 'struct1');
expect(saved).toHaveProperty('workflow_structure_hash_after', 'struct2');
expect(saved).toHaveProperty('is_truly_successful', true);
expect(saved).toHaveProperty('user_intent', 'Test intent');
expect(saved).toHaveProperty('intent_classification');
expect(saved).toHaveProperty('tool_name');
expect(saved).toHaveProperty('operation_count', 1);
expect(saved).toHaveProperty('operation_types');
expect(saved).toHaveProperty('validation_before');
expect(saved).toHaveProperty('validation_after');
expect(saved).toHaveProperty('validation_improved', true);
expect(saved).toHaveProperty('errors_resolved', 1);
expect(saved).toHaveProperty('errors_introduced', 0);
expect(saved).toHaveProperty('nodes_added', 1);
expect(saved).toHaveProperty('nodes_removed', 0);
expect(saved).toHaveProperty('nodes_modified', 0);
expect(saved).toHaveProperty('connections_added', 0);
expect(saved).toHaveProperty('connections_removed', 0);
expect(saved).toHaveProperty('properties_changed', 0);
expect(saved).toHaveProperty('mutation_success', true);
expect(saved).toHaveProperty('duration_ms', 150);
});
});
}); });