mirror of
https://github.com/czlonkowski/n8n-mcp.git
synced 2026-01-30 06:22:04 +00:00
chore: release v2.13.1 - remove 5-operation limit
- Remove 5-operation limit from n8n_update_partial_workflow - Update CHANGELOG.md with version 2.13.1 entry - Bump version in package.json to 2.13.1 - Remove static version badge from README.md (npm badge remains) The workflow diff engine now supports unlimited operations per request, enabling complex workflow refactoring in single API calls.
This commit is contained in:
@@ -2,7 +2,6 @@
|
||||
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
[](https://github.com/czlonkowski/n8n-mcp)
|
||||
[](https://github.com/czlonkowski/n8n-mcp)
|
||||
[](https://www.npmjs.com/package/n8n-mcp)
|
||||
[](https://codecov.io/gh/czlonkowski/n8n-mcp)
|
||||
[](https://github.com/czlonkowski/n8n-mcp/actions)
|
||||
|
||||
@@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [2.13.1] - 2025-01-24
|
||||
|
||||
### Changed
|
||||
- **Removed 5-operation limit from n8n_update_partial_workflow**: The workflow diff engine now supports unlimited operations per request
|
||||
- Previously limited to 5 operations for "transactional integrity"
|
||||
- Analysis revealed the limit was unnecessary - the clone-validate-apply pattern already ensures atomicity
|
||||
- All operations are validated before any are applied, maintaining data integrity
|
||||
- Enables complex workflow refactoring in single API calls
|
||||
- Updated documentation and examples to demonstrate large batch operations (26+ operations)
|
||||
|
||||
## [2.13.0] - 2025-01-24
|
||||
|
||||
### Added
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "n8n-mcp",
|
||||
"version": "2.13.0",
|
||||
"version": "2.13.1",
|
||||
"description": "Integration between n8n workflow automation and Model Context Protocol (MCP)",
|
||||
"main": "dist/index.js",
|
||||
"bin": {
|
||||
|
||||
@@ -76,6 +76,6 @@ export const validateWorkflowDoc: ToolDocumentation = {
|
||||
'Validation cannot catch all runtime errors (e.g., API failures)',
|
||||
'Profile setting only affects node validation, not connection/expression checks'
|
||||
],
|
||||
relatedTools: ['validate_workflow_connections', 'validate_workflow_expressions', 'validate_node_operation', 'n8n_create_workflow', 'n8n_update_partial_workflow']
|
||||
relatedTools: ['validate_workflow_connections', 'validate_workflow_expressions', 'validate_node_operation', 'n8n_create_workflow', 'n8n_update_partial_workflow', 'n8n_autofix_workflow']
|
||||
}
|
||||
};
|
||||
@@ -66,6 +66,6 @@ Requires N8N_API_URL and N8N_API_KEY environment variables to be configured.`,
|
||||
'Profile affects validation time - strict is slower but more thorough',
|
||||
'Expression validation may flag working but non-standard syntax'
|
||||
],
|
||||
relatedTools: ['validate_workflow', 'n8n_get_workflow', 'validate_workflow_expressions', 'n8n_health_check']
|
||||
relatedTools: ['validate_workflow', 'n8n_get_workflow', 'validate_workflow_expressions', 'n8n_health_check', 'n8n_autofix_workflow']
|
||||
}
|
||||
};
|
||||
121
src/scripts/test-autofix-documentation.ts
Normal file
121
src/scripts/test-autofix-documentation.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
#!/usr/bin/env npx tsx
|
||||
|
||||
/**
|
||||
* Test script to verify n8n_autofix_workflow documentation is properly integrated
|
||||
*/
|
||||
|
||||
import { toolsDocumentation } from '../mcp/tool-docs';
|
||||
import { getToolDocumentation } from '../mcp/tools-documentation';
|
||||
import { Logger } from '../utils/logger';
|
||||
|
||||
const logger = new Logger({ prefix: '[AutofixDoc Test]' });
|
||||
|
||||
async function testAutofixDocumentation() {
|
||||
logger.info('Testing n8n_autofix_workflow documentation...\n');
|
||||
|
||||
// Test 1: Check if documentation exists in the registry
|
||||
logger.info('Test 1: Checking documentation registry');
|
||||
const hasDoc = 'n8n_autofix_workflow' in toolsDocumentation;
|
||||
if (hasDoc) {
|
||||
logger.info('✅ Documentation found in registry');
|
||||
} else {
|
||||
logger.error('❌ Documentation NOT found in registry');
|
||||
logger.info('Available tools:', Object.keys(toolsDocumentation).filter(k => k.includes('autofix')));
|
||||
}
|
||||
|
||||
// Test 2: Check documentation structure
|
||||
if (hasDoc) {
|
||||
logger.info('\nTest 2: Checking documentation structure');
|
||||
const doc = toolsDocumentation['n8n_autofix_workflow'];
|
||||
|
||||
const hasEssentials = doc.essentials &&
|
||||
doc.essentials.description &&
|
||||
doc.essentials.keyParameters &&
|
||||
doc.essentials.example;
|
||||
|
||||
const hasFull = doc.full &&
|
||||
doc.full.description &&
|
||||
doc.full.parameters &&
|
||||
doc.full.examples;
|
||||
|
||||
if (hasEssentials) {
|
||||
logger.info('✅ Essentials documentation complete');
|
||||
logger.info(` Description: ${doc.essentials.description.substring(0, 80)}...`);
|
||||
logger.info(` Key params: ${doc.essentials.keyParameters.join(', ')}`);
|
||||
} else {
|
||||
logger.error('❌ Essentials documentation incomplete');
|
||||
}
|
||||
|
||||
if (hasFull) {
|
||||
logger.info('✅ Full documentation complete');
|
||||
logger.info(` Parameters: ${Object.keys(doc.full.parameters).join(', ')}`);
|
||||
logger.info(` Examples: ${doc.full.examples.length} provided`);
|
||||
} else {
|
||||
logger.error('❌ Full documentation incomplete');
|
||||
}
|
||||
}
|
||||
|
||||
// Test 3: Test getToolDocumentation function
|
||||
logger.info('\nTest 3: Testing getToolDocumentation function');
|
||||
|
||||
try {
|
||||
const essentialsDoc = getToolDocumentation('n8n_autofix_workflow', 'essentials');
|
||||
if (essentialsDoc.includes("Tool 'n8n_autofix_workflow' not found")) {
|
||||
logger.error('❌ Essentials documentation retrieval failed');
|
||||
} else {
|
||||
logger.info('✅ Essentials documentation retrieved');
|
||||
const lines = essentialsDoc.split('\n').slice(0, 3);
|
||||
lines.forEach(line => logger.info(` ${line}`));
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('❌ Error retrieving essentials documentation:', error);
|
||||
}
|
||||
|
||||
try {
|
||||
const fullDoc = getToolDocumentation('n8n_autofix_workflow', 'full');
|
||||
if (fullDoc.includes("Tool 'n8n_autofix_workflow' not found")) {
|
||||
logger.error('❌ Full documentation retrieval failed');
|
||||
} else {
|
||||
logger.info('✅ Full documentation retrieved');
|
||||
const lines = fullDoc.split('\n').slice(0, 3);
|
||||
lines.forEach(line => logger.info(` ${line}`));
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('❌ Error retrieving full documentation:', error);
|
||||
}
|
||||
|
||||
// Test 4: Check if tool is listed in workflow management tools
|
||||
logger.info('\nTest 4: Checking workflow management tools listing');
|
||||
const workflowTools = Object.keys(toolsDocumentation).filter(k => k.startsWith('n8n_'));
|
||||
const hasAutofix = workflowTools.includes('n8n_autofix_workflow');
|
||||
|
||||
if (hasAutofix) {
|
||||
logger.info('✅ n8n_autofix_workflow is listed in workflow management tools');
|
||||
logger.info(` Total workflow tools: ${workflowTools.length}`);
|
||||
|
||||
// Show related tools
|
||||
const relatedTools = workflowTools.filter(t =>
|
||||
t.includes('validate') || t.includes('update') || t.includes('fix')
|
||||
);
|
||||
logger.info(` Related tools: ${relatedTools.join(', ')}`);
|
||||
} else {
|
||||
logger.error('❌ n8n_autofix_workflow NOT listed in workflow management tools');
|
||||
}
|
||||
|
||||
// Summary
|
||||
logger.info('\n' + '='.repeat(60));
|
||||
logger.info('Summary:');
|
||||
|
||||
if (hasDoc && hasAutofix) {
|
||||
logger.info('✨ Documentation integration successful!');
|
||||
logger.info('The n8n_autofix_workflow tool documentation is properly integrated.');
|
||||
logger.info('\nTo use in MCP:');
|
||||
logger.info(' - Essentials: tools_documentation({topic: "n8n_autofix_workflow"})');
|
||||
logger.info(' - Full: tools_documentation({topic: "n8n_autofix_workflow", depth: "full"})');
|
||||
} else {
|
||||
logger.error('⚠️ Documentation integration incomplete');
|
||||
logger.info('Please check the implementation and rebuild the project.');
|
||||
}
|
||||
}
|
||||
|
||||
testAutofixDocumentation().catch(console.error);
|
||||
149
src/scripts/test-webhook-autofix.ts
Normal file
149
src/scripts/test-webhook-autofix.ts
Normal file
@@ -0,0 +1,149 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Test script for webhook path autofixer functionality
|
||||
*/
|
||||
|
||||
import { NodeRepository } from '../database/node-repository';
|
||||
import { createDatabaseAdapter } from '../database/database-adapter';
|
||||
import { WorkflowAutoFixer } from '../services/workflow-auto-fixer';
|
||||
import { WorkflowValidator } from '../services/workflow-validator';
|
||||
import { EnhancedConfigValidator } from '../services/enhanced-config-validator';
|
||||
import { Workflow } from '../types/n8n-api';
|
||||
import { Logger } from '../utils/logger';
|
||||
import { join } from 'path';
|
||||
|
||||
const logger = new Logger({ prefix: '[TestWebhookAutofix]' });
|
||||
|
||||
// Test workflow with webhook missing path
|
||||
const testWorkflow: Workflow = {
|
||||
id: 'test_webhook_fix',
|
||||
name: 'Test Webhook Autofix',
|
||||
active: false,
|
||||
nodes: [
|
||||
{
|
||||
id: '1',
|
||||
name: 'Webhook',
|
||||
type: 'n8n-nodes-base.webhook',
|
||||
typeVersion: 2.1,
|
||||
position: [250, 300],
|
||||
parameters: {}, // Empty parameters - missing path
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
name: 'HTTP Request',
|
||||
type: 'n8n-nodes-base.httpRequest',
|
||||
typeVersion: 4.2,
|
||||
position: [450, 300],
|
||||
parameters: {
|
||||
url: 'https://api.example.com/data',
|
||||
method: 'GET'
|
||||
}
|
||||
}
|
||||
],
|
||||
connections: {
|
||||
'Webhook': {
|
||||
main: [[{
|
||||
node: 'HTTP Request',
|
||||
type: 'main',
|
||||
index: 0
|
||||
}]]
|
||||
}
|
||||
},
|
||||
settings: {
|
||||
executionOrder: 'v1'
|
||||
},
|
||||
staticData: undefined
|
||||
};
|
||||
|
||||
async function testWebhookAutofix() {
|
||||
logger.info('Testing webhook path autofixer...');
|
||||
|
||||
// Initialize database and repository
|
||||
const dbPath = join(process.cwd(), 'data', 'nodes.db');
|
||||
const adapter = await createDatabaseAdapter(dbPath);
|
||||
const repository = new NodeRepository(adapter);
|
||||
|
||||
// Create validators
|
||||
const validator = new WorkflowValidator(repository, EnhancedConfigValidator);
|
||||
const autoFixer = new WorkflowAutoFixer(repository);
|
||||
|
||||
// Step 1: Validate workflow to identify issues
|
||||
logger.info('Step 1: Validating workflow to identify issues...');
|
||||
const validationResult = await validator.validateWorkflow(testWorkflow);
|
||||
|
||||
console.log('\n📋 Validation Summary:');
|
||||
console.log(`- Valid: ${validationResult.valid}`);
|
||||
console.log(`- Errors: ${validationResult.errors.length}`);
|
||||
console.log(`- Warnings: ${validationResult.warnings.length}`);
|
||||
|
||||
if (validationResult.errors.length > 0) {
|
||||
console.log('\n❌ Errors found:');
|
||||
validationResult.errors.forEach(error => {
|
||||
console.log(` - [${error.nodeName || error.nodeId}] ${error.message}`);
|
||||
});
|
||||
}
|
||||
|
||||
// Step 2: Generate fixes (preview mode)
|
||||
logger.info('\nStep 2: Generating fixes in preview mode...');
|
||||
|
||||
const fixResult = autoFixer.generateFixes(
|
||||
testWorkflow,
|
||||
validationResult,
|
||||
[], // No expression format issues to pass
|
||||
{
|
||||
applyFixes: false, // Preview mode
|
||||
fixTypes: ['webhook-missing-path'] // Only test webhook fixes
|
||||
}
|
||||
);
|
||||
|
||||
console.log('\n🔧 Fix Results:');
|
||||
console.log(`- Summary: ${fixResult.summary}`);
|
||||
console.log(`- Total fixes: ${fixResult.stats.total}`);
|
||||
console.log(`- Webhook path fixes: ${fixResult.stats.byType['webhook-missing-path']}`);
|
||||
|
||||
if (fixResult.fixes.length > 0) {
|
||||
console.log('\n📝 Detailed Fixes:');
|
||||
fixResult.fixes.forEach(fix => {
|
||||
console.log(` - Node: ${fix.node}`);
|
||||
console.log(` Field: ${fix.field}`);
|
||||
console.log(` Type: ${fix.type}`);
|
||||
console.log(` Before: ${fix.before || 'undefined'}`);
|
||||
console.log(` After: ${fix.after}`);
|
||||
console.log(` Confidence: ${fix.confidence}`);
|
||||
console.log(` Description: ${fix.description}`);
|
||||
});
|
||||
}
|
||||
|
||||
if (fixResult.operations.length > 0) {
|
||||
console.log('\n🔄 Operations to Apply:');
|
||||
fixResult.operations.forEach(op => {
|
||||
if (op.type === 'updateNode') {
|
||||
console.log(` - Update Node: ${op.nodeId}`);
|
||||
console.log(` Updates: ${JSON.stringify(op.updates, null, 2)}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Step 3: Verify UUID format
|
||||
if (fixResult.fixes.length > 0) {
|
||||
const webhookFix = fixResult.fixes.find(f => f.type === 'webhook-missing-path');
|
||||
if (webhookFix) {
|
||||
const uuid = webhookFix.after as string;
|
||||
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
||||
const isValidUUID = uuidRegex.test(uuid);
|
||||
|
||||
console.log('\n✅ UUID Validation:');
|
||||
console.log(` - Generated UUID: ${uuid}`);
|
||||
console.log(` - Valid format: ${isValidUUID ? 'Yes' : 'No'}`);
|
||||
}
|
||||
}
|
||||
|
||||
logger.info('\n✨ Webhook autofix test completed successfully!');
|
||||
}
|
||||
|
||||
// Run test
|
||||
testWebhookAutofix().catch(error => {
|
||||
logger.error('Test failed:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user