mirror of
https://github.com/czlonkowski/n8n-mcp.git
synced 2026-01-30 22:42:04 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ddf9556759 |
68
CHANGELOG.md
68
CHANGELOG.md
@@ -7,6 +7,74 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [2.27.2] - 2025-11-29
|
||||
|
||||
### ✨ Enhanced Features
|
||||
|
||||
**n8n_deploy_template: Deploy-First with Auto-Fix**
|
||||
|
||||
Improved the template deployment tool to deploy first, then automatically fix common issues. This change dramatically improves deployment success rates for templates with expression format issues.
|
||||
|
||||
#### Key Changes
|
||||
|
||||
1. **Deploy-First Behavior**
|
||||
- Templates are now deployed first without pre-validation
|
||||
- Auto-fix runs automatically after deployment (configurable via `autoFix` parameter)
|
||||
- Returns `fixesApplied` array showing all corrections made
|
||||
|
||||
2. **Fixed Expression Validator False Positive**
|
||||
- Fixed "nested expressions" detection that incorrectly flagged valid patterns
|
||||
- Multiple expressions in one string like `={{ $a }} text {{ $b }}` now correctly pass validation
|
||||
- Only truly nested patterns like `{{ {{ $json }} }}` are flagged as errors
|
||||
|
||||
3. **Fixed Zod Schema Validation**
|
||||
- Added missing `typeversion-upgrade` and `version-migration` fix types to autofix schema
|
||||
- Prevents silent validation failures when autofix runs
|
||||
|
||||
#### Usage
|
||||
|
||||
```javascript
|
||||
// Deploy with auto-fix (default behavior)
|
||||
n8n_deploy_template({
|
||||
templateId: 2776,
|
||||
name: "My Workflow"
|
||||
})
|
||||
|
||||
// Deploy without auto-fix (not recommended)
|
||||
n8n_deploy_template({
|
||||
templateId: 2776,
|
||||
autoFix: false
|
||||
})
|
||||
```
|
||||
|
||||
#### Response
|
||||
|
||||
```json
|
||||
{
|
||||
"workflowId": "abc123",
|
||||
"name": "My Workflow",
|
||||
"fixesApplied": [
|
||||
{
|
||||
"node": "HTTP Request",
|
||||
"field": "url",
|
||||
"type": "expression-format",
|
||||
"before": "https://api.com/{{ $json.id }}",
|
||||
"after": "=https://api.com/{{ $json.id }}",
|
||||
"confidence": "high"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### Testing Results
|
||||
|
||||
- 87% deployment success rate across 15 diverse templates
|
||||
- Auto-fix correctly adds `=` prefix to expressions missing it
|
||||
- Auto-fix correctly upgrades outdated typeVersions
|
||||
- Failed deployments are legitimate issues (missing community nodes, incomplete templates)
|
||||
|
||||
**Conceived by Romuald Członkowski - [AiAdvisors](https://www.aiadvisors.pl/en)**
|
||||
|
||||
## [2.27.1] - 2025-11-29
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
@@ -974,7 +974,7 @@ These tools require `N8N_API_URL` and `N8N_API_KEY` in your configuration.
|
||||
- **`n8n_validate_workflow`** - Validate workflows in n8n by ID
|
||||
- **`n8n_autofix_workflow`** - Automatically fix common workflow errors
|
||||
- **`n8n_workflow_versions`** - Manage version history and rollback
|
||||
- **`n8n_deploy_template`** - Deploy templates from n8n.io directly to your instance (NEW!)
|
||||
- **`n8n_deploy_template`** - Deploy templates from n8n.io directly to your instance with auto-fix
|
||||
|
||||
#### Execution Management
|
||||
- **`n8n_trigger_webhook_workflow`** - Trigger workflows via webhook URL
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "n8n-mcp",
|
||||
"version": "2.27.1",
|
||||
"version": "2.27.2",
|
||||
"description": "Integration between n8n workflow automation and Model Context Protocol (MCP)",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "n8n-mcp-runtime",
|
||||
"version": "2.27.1",
|
||||
"version": "2.27.2",
|
||||
"description": "n8n MCP Server Runtime Dependencies Only",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
|
||||
@@ -85,6 +85,31 @@ interface CloudPlatformGuide {
|
||||
troubleshooting: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Applied Fix from Auto-Fix Operation
|
||||
*/
|
||||
interface AppliedFix {
|
||||
node: string;
|
||||
field: string;
|
||||
type: string;
|
||||
before: string;
|
||||
after: string;
|
||||
confidence: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto-Fix Result Data from handleAutofixWorkflow
|
||||
*/
|
||||
interface AutofixResultData {
|
||||
fixesApplied?: number;
|
||||
fixes?: AppliedFix[];
|
||||
workflowId?: string;
|
||||
workflowName?: string;
|
||||
message?: string;
|
||||
summary?: string;
|
||||
stats?: Record<string, number>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Workflow Validation Response Data
|
||||
*/
|
||||
@@ -396,7 +421,9 @@ const autofixWorkflowSchema = z.object({
|
||||
'typeversion-correction',
|
||||
'error-output-config',
|
||||
'node-type-correction',
|
||||
'webhook-missing-path'
|
||||
'webhook-missing-path',
|
||||
'typeversion-upgrade',
|
||||
'version-migration'
|
||||
])).optional(),
|
||||
confidenceThreshold: z.enum(['high', 'medium', 'low']).optional().default('medium'),
|
||||
maxFixes: z.number().optional().default(50)
|
||||
@@ -2199,7 +2226,7 @@ const deployTemplateSchema = z.object({
|
||||
templateId: z.number().positive().int(),
|
||||
name: z.string().optional(),
|
||||
autoUpgradeVersions: z.boolean().default(true),
|
||||
validate: z.boolean().default(true),
|
||||
autoFix: z.boolean().default(true), // Auto-apply fixes after deployment
|
||||
stripCredentials: z.boolean().default(true)
|
||||
});
|
||||
|
||||
@@ -2318,32 +2345,6 @@ export async function handleDeployTemplate(
|
||||
}
|
||||
}
|
||||
|
||||
// Validate workflow if requested
|
||||
if (input.validate) {
|
||||
const validator = new WorkflowValidator(repository, EnhancedConfigValidator);
|
||||
const validationResult = await validator.validateWorkflow(workflow, {
|
||||
validateNodes: true,
|
||||
validateConnections: true,
|
||||
validateExpressions: true,
|
||||
profile: 'runtime'
|
||||
});
|
||||
|
||||
if (validationResult.errors.length > 0) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'Workflow validation failed',
|
||||
details: {
|
||||
errors: validationResult.errors.map(e => ({
|
||||
node: e.nodeName,
|
||||
message: e.message
|
||||
})),
|
||||
warnings: validationResult.warnings.length,
|
||||
hint: 'Use validate=false to skip validation, or fix the template issues'
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Identify trigger type
|
||||
const triggerNode = workflow.nodes.find((n: any) =>
|
||||
n.type?.includes('Trigger') ||
|
||||
@@ -2353,6 +2354,7 @@ export async function handleDeployTemplate(
|
||||
const triggerType = triggerNode?.type?.split('.').pop() || 'manual';
|
||||
|
||||
// Create workflow via API (always creates inactive)
|
||||
// Deploy first, then fix - this ensures the workflow exists before we modify it
|
||||
const createdWorkflow = await client.createWorkflow({
|
||||
name: workflowName,
|
||||
nodes: workflow.nodes,
|
||||
@@ -2364,6 +2366,44 @@ export async function handleDeployTemplate(
|
||||
const apiConfig = context ? getN8nApiConfigFromContext(context) : getN8nApiConfig();
|
||||
const baseUrl = apiConfig?.baseUrl?.replace('/api/v1', '') || '';
|
||||
|
||||
// Auto-fix common issues after deployment (expression format, etc.)
|
||||
let fixesApplied: AppliedFix[] = [];
|
||||
let fixSummary = '';
|
||||
let autoFixStatus: 'success' | 'failed' | 'skipped' = 'skipped';
|
||||
|
||||
if (input.autoFix) {
|
||||
try {
|
||||
// Run autofix on the deployed workflow
|
||||
const autofixResult = await handleAutofixWorkflow(
|
||||
{
|
||||
id: createdWorkflow.id,
|
||||
applyFixes: true,
|
||||
fixTypes: ['expression-format', 'typeversion-upgrade'],
|
||||
confidenceThreshold: 'medium'
|
||||
},
|
||||
repository,
|
||||
context
|
||||
);
|
||||
|
||||
if (autofixResult.success && autofixResult.data) {
|
||||
const fixData = autofixResult.data as AutofixResultData;
|
||||
autoFixStatus = 'success';
|
||||
if (fixData.fixesApplied && fixData.fixesApplied > 0) {
|
||||
fixesApplied = fixData.fixes || [];
|
||||
fixSummary = ` Auto-fixed ${fixData.fixesApplied} issue(s).`;
|
||||
}
|
||||
}
|
||||
} catch (fixError) {
|
||||
// Log but don't fail - autofix is best-effort
|
||||
autoFixStatus = 'failed';
|
||||
logger.warn('Auto-fix failed after template deployment', {
|
||||
workflowId: createdWorkflow.id,
|
||||
error: fixError instanceof Error ? fixError.message : 'Unknown error'
|
||||
});
|
||||
fixSummary = ' Auto-fix failed (workflow deployed successfully).';
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
@@ -2375,9 +2415,11 @@ export async function handleDeployTemplate(
|
||||
requiredCredentials: requiredCredentials.length > 0 ? requiredCredentials : undefined,
|
||||
url: baseUrl ? `${baseUrl}/workflow/${createdWorkflow.id}` : undefined,
|
||||
templateId: input.templateId,
|
||||
templateUrl: template.url || `https://n8n.io/workflows/${input.templateId}`
|
||||
templateUrl: template.url || `https://n8n.io/workflows/${input.templateId}`,
|
||||
autoFixStatus,
|
||||
fixesApplied: fixesApplied.length > 0 ? fixesApplied : undefined
|
||||
},
|
||||
message: `Workflow "${createdWorkflow.name}" deployed successfully from template ${input.templateId}. ${
|
||||
message: `Workflow "${createdWorkflow.name}" deployed successfully from template ${input.templateId}.${fixSummary} ${
|
||||
requiredCredentials.length > 0
|
||||
? `Configure ${requiredCredentials.length} credential(s) in n8n to activate.`
|
||||
: ''
|
||||
|
||||
@@ -4,39 +4,39 @@ export const n8nDeployTemplateDoc: ToolDocumentation = {
|
||||
name: 'n8n_deploy_template',
|
||||
category: 'workflow_management',
|
||||
essentials: {
|
||||
description: 'Deploy a workflow template from n8n.io directly to your n8n instance. Fetches template, optionally upgrades node versions and validates, then creates workflow.',
|
||||
keyParameters: ['templateId', 'name', 'autoUpgradeVersions', 'validate', 'stripCredentials'],
|
||||
description: 'Deploy a workflow template from n8n.io directly to your n8n instance. Deploys first, then auto-fixes common issues (expression format, typeVersions).',
|
||||
keyParameters: ['templateId', 'name', 'autoUpgradeVersions', 'autoFix', 'stripCredentials'],
|
||||
example: 'n8n_deploy_template({templateId: 2776, name: "My Deployed Template"})',
|
||||
performance: 'Network-dependent',
|
||||
tips: [
|
||||
'Auto-fixes expression format issues after deployment',
|
||||
'Workflow created inactive - configure credentials in n8n UI first',
|
||||
'Returns list of required credentials',
|
||||
'Use search_templates to find template IDs',
|
||||
'Templates are upgraded to latest node versions by default'
|
||||
'Returns list of required credentials and fixes applied',
|
||||
'Use search_templates to find template IDs'
|
||||
]
|
||||
},
|
||||
full: {
|
||||
description: 'Deploys a workflow template from n8n.io directly to your n8n instance. This tool combines fetching a template and creating a workflow in a single operation. Templates are stored locally and fetched from the database. The workflow is always created in an inactive state, allowing you to configure credentials before activation.',
|
||||
description: 'Deploys a workflow template from n8n.io directly to your n8n instance. This tool deploys first, then automatically fixes common issues like missing expression prefixes (=) and outdated typeVersions. Templates are stored locally and fetched from the database. The workflow is always created in an inactive state, allowing you to configure credentials before activation.',
|
||||
parameters: {
|
||||
templateId: { type: 'number', required: true, description: 'Template ID from n8n.io (find via search_templates)' },
|
||||
name: { type: 'string', description: 'Custom workflow name (default: template name)' },
|
||||
autoUpgradeVersions: { type: 'boolean', description: 'Upgrade node typeVersions to latest supported (default: true)' },
|
||||
validate: { type: 'boolean', description: 'Validate workflow before deployment (default: true)' },
|
||||
autoFix: { type: 'boolean', description: 'Auto-apply fixes after deployment for expression format issues, missing = prefix, etc. (default: true)' },
|
||||
stripCredentials: { type: 'boolean', description: 'Remove credential references - user configures in n8n UI (default: true)' }
|
||||
},
|
||||
returns: 'Object with workflowId, name, nodeCount, triggerType, requiredCredentials array, url, templateId, templateUrl',
|
||||
returns: 'Object with workflowId, name, nodeCount, triggerType, requiredCredentials array, url, templateId, templateUrl, autoFixStatus (success/failed/skipped), and fixesApplied array',
|
||||
examples: [
|
||||
`// Deploy template with default settings
|
||||
`// Deploy template with default settings (auto-fix enabled)
|
||||
n8n_deploy_template({templateId: 2776})`,
|
||||
`// Deploy with custom name
|
||||
n8n_deploy_template({
|
||||
templateId: 2776,
|
||||
name: "My Google Drive to Airtable Sync"
|
||||
})`,
|
||||
`// Deploy without validation (faster, use for trusted templates)
|
||||
`// Deploy without auto-fix (not recommended)
|
||||
n8n_deploy_template({
|
||||
templateId: 2776,
|
||||
validate: false
|
||||
autoFix: false
|
||||
})`,
|
||||
`// Keep original node versions (useful for compatibility)
|
||||
n8n_deploy_template({
|
||||
@@ -50,10 +50,12 @@ n8n_deploy_template({
|
||||
'Bootstrap new projects with proven workflows',
|
||||
'Deploy templates found via search_templates'
|
||||
],
|
||||
performance: 'Network-dependent - Typically 200-500ms (template fetch + workflow creation)',
|
||||
performance: 'Network-dependent - Typically 300-800ms (template fetch + workflow creation + autofix)',
|
||||
bestPractices: [
|
||||
'Use search_templates to find templates by use case',
|
||||
'Review required credentials in the response',
|
||||
'Check autoFixStatus in response - "success", "failed", or "skipped"',
|
||||
'Check fixesApplied in response to see what was automatically corrected',
|
||||
'Configure credentials in n8n UI before activating',
|
||||
'Test workflow before connecting to production systems'
|
||||
],
|
||||
@@ -62,8 +64,8 @@ n8n_deploy_template({
|
||||
'Workflows created in INACTIVE state - must configure credentials and activate in n8n',
|
||||
'Templates may reference services you do not have (Slack, Google, etc.)',
|
||||
'Template database must be populated - run npm run fetch:templates if templates not found',
|
||||
'Validation may fail for templates with outdated node configurations'
|
||||
'Some issues may not be auto-fixable (e.g., missing required fields that need user input)'
|
||||
],
|
||||
relatedTools: ['search_templates', 'get_template', 'n8n_create_workflow', 'n8n_validate_workflow']
|
||||
relatedTools: ['search_templates', 'get_template', 'n8n_create_workflow', 'n8n_autofix_workflow']
|
||||
}
|
||||
};
|
||||
|
||||
@@ -450,7 +450,7 @@ export const n8nManagementTools: ToolDefinition[] = [
|
||||
// Template Deployment Tool
|
||||
{
|
||||
name: 'n8n_deploy_template',
|
||||
description: `Deploy a workflow template from n8n.io directly to your n8n instance. Fetches template, optionally upgrades node versions and validates, then creates workflow. Returns workflow ID and required credentials list.`,
|
||||
description: `Deploy a workflow template from n8n.io directly to your n8n instance. Deploys first, then auto-fixes common issues (expression format, typeVersions). Returns workflow ID, required credentials, and fixes applied.`,
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
@@ -467,10 +467,10 @@ export const n8nManagementTools: ToolDefinition[] = [
|
||||
default: true,
|
||||
description: 'Automatically upgrade node typeVersions to latest supported (default: true)'
|
||||
},
|
||||
validate: {
|
||||
autoFix: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: 'Validate workflow before deployment (default: true)'
|
||||
description: 'Auto-apply fixes after deployment for expression format issues, missing = prefix, etc. (default: true)'
|
||||
},
|
||||
stripCredentials: {
|
||||
type: 'boolean',
|
||||
|
||||
@@ -97,12 +97,12 @@ export class ExpressionValidator {
|
||||
errors.push('Unmatched expression brackets {{ }}');
|
||||
}
|
||||
|
||||
// Check for nested expressions (not supported in n8n)
|
||||
if (expression.includes('{{') && expression.includes('{{', expression.indexOf('{{') + 2)) {
|
||||
const match = expression.match(/\{\{.*\{\{/);
|
||||
if (match) {
|
||||
errors.push('Nested expressions are not supported');
|
||||
}
|
||||
// Check for truly nested expressions (not supported in n8n)
|
||||
// This means {{ inside another {{ }}, like {{ {{ $json }} }}
|
||||
// NOT multiple expressions like {{ $json.a }} text {{ $json.b }} (which is valid)
|
||||
const nestedPattern = /\{\{[^}]*\{\{/;
|
||||
if (nestedPattern.test(expression)) {
|
||||
errors.push('Nested expressions are not supported (expression inside another expression)');
|
||||
}
|
||||
|
||||
// Check for empty expressions
|
||||
|
||||
@@ -10,7 +10,7 @@ const deployTemplateSchema = z.object({
|
||||
templateId: z.number().positive().int(),
|
||||
name: z.string().optional(),
|
||||
autoUpgradeVersions: z.boolean().default(true),
|
||||
validate: z.boolean().default(true),
|
||||
autoFix: z.boolean().default(true),
|
||||
stripCredentials: z.boolean().default(true)
|
||||
});
|
||||
|
||||
@@ -78,11 +78,11 @@ describe('handleDeployTemplate Schema Validation', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('should default validate to true', () => {
|
||||
it('should default autoFix to true', () => {
|
||||
const result = deployTemplateSchema.safeParse({ templateId: 123 });
|
||||
expect(result.success).toBe(true);
|
||||
if (result.success) {
|
||||
expect(result.data.validate).toBe(true);
|
||||
expect(result.data.autoFix).toBe(true);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -99,7 +99,7 @@ describe('handleDeployTemplate Schema Validation', () => {
|
||||
templateId: 2776,
|
||||
name: 'My Deployed Workflow',
|
||||
autoUpgradeVersions: false,
|
||||
validate: false,
|
||||
autoFix: false,
|
||||
stripCredentials: false
|
||||
});
|
||||
|
||||
@@ -108,7 +108,7 @@ describe('handleDeployTemplate Schema Validation', () => {
|
||||
expect(result.data.templateId).toBe(2776);
|
||||
expect(result.data.name).toBe('My Deployed Workflow');
|
||||
expect(result.data.autoUpgradeVersions).toBe(false);
|
||||
expect(result.data.validate).toBe(false);
|
||||
expect(result.data.autoFix).toBe(false);
|
||||
expect(result.data.stripCredentials).toBe(false);
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user