mirror of
https://github.com/czlonkowski/n8n-mcp.git
synced 2026-02-06 13:33:11 +00:00
feat: Tool consolidation v2.26.0 - reduce tools by 38% (31 → 19)
Major consolidation of MCP tools using mode-based parameters for better AI agent ergonomics: Node Tools: - get_node_documentation → get_node with mode='documentation' - search_node_properties → get_node with mode='search_properties' - get_property_dependencies → removed Validation Tools: - validate_node_operation + validate_node_minimal → validate_node with mode param Template Tools: - list_node_templates → search_templates with searchMode='nodes' - search_templates_by_metadata → search_templates with searchMode='metadata' - get_templates_for_task → search_templates with searchMode='task' Workflow Getters: - n8n_get_workflow_details/structure/minimal → n8n_get_workflow with mode param Execution Tools: - n8n_list/get/delete_execution → n8n_executions with action param Test updates for all consolidated tools. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en
This commit is contained in:
@@ -830,36 +830,32 @@ export class N8NDocumentationMCPServer {
|
||||
let validationResult;
|
||||
|
||||
switch (toolName) {
|
||||
case 'validate_node_operation':
|
||||
case 'validate_node':
|
||||
// Consolidated tool handles both modes - validate as operation for now
|
||||
validationResult = ToolValidation.validateNodeOperation(args);
|
||||
break;
|
||||
case 'validate_node_minimal':
|
||||
validationResult = ToolValidation.validateNodeMinimal(args);
|
||||
break;
|
||||
case 'validate_workflow':
|
||||
validationResult = ToolValidation.validateWorkflow(args);
|
||||
break;
|
||||
case 'search_nodes':
|
||||
validationResult = ToolValidation.validateSearchNodes(args);
|
||||
break;
|
||||
case 'list_node_templates':
|
||||
validationResult = ToolValidation.validateListNodeTemplates(args);
|
||||
break;
|
||||
case 'n8n_create_workflow':
|
||||
validationResult = ToolValidation.validateCreateWorkflow(args);
|
||||
break;
|
||||
case 'n8n_get_workflow':
|
||||
case 'n8n_get_workflow_details':
|
||||
case 'n8n_get_workflow_structure':
|
||||
case 'n8n_get_workflow_minimal':
|
||||
case 'n8n_update_full_workflow':
|
||||
case 'n8n_delete_workflow':
|
||||
case 'n8n_validate_workflow':
|
||||
case 'n8n_autofix_workflow':
|
||||
case 'n8n_get_execution':
|
||||
case 'n8n_delete_execution':
|
||||
validationResult = ToolValidation.validateWorkflowId(args);
|
||||
break;
|
||||
case 'n8n_executions':
|
||||
// Requires action parameter, id validation done in handler based on action
|
||||
validationResult = args.action
|
||||
? { valid: true, errors: [] }
|
||||
: { valid: false, errors: [{ field: 'action', message: 'action is required' }] };
|
||||
break;
|
||||
default:
|
||||
// For tools not yet migrated to schema validation, use basic validation
|
||||
return this.validateToolParamsBasic(toolName, args, legacyRequiredParams || []);
|
||||
@@ -1018,11 +1014,19 @@ export class N8NDocumentationMCPServer {
|
||||
// Convert limit to number if provided, otherwise use default
|
||||
const limit = args.limit !== undefined ? Number(args.limit) || 20 : 20;
|
||||
return this.searchNodes(args.query, limit, { mode: args.mode, includeExamples: args.includeExamples });
|
||||
case 'get_node_documentation':
|
||||
this.validateToolParams(name, args, ['nodeType']);
|
||||
return this.getNodeDocumentation(args.nodeType);
|
||||
case 'get_node':
|
||||
this.validateToolParams(name, args, ['nodeType']);
|
||||
// Handle consolidated modes: docs, search_properties
|
||||
if (args.mode === 'docs') {
|
||||
return this.getNodeDocumentation(args.nodeType);
|
||||
}
|
||||
if (args.mode === 'search_properties') {
|
||||
if (!args.propertyQuery) {
|
||||
throw new Error('propertyQuery is required for mode=search_properties');
|
||||
}
|
||||
const maxResults = args.maxPropertyResults !== undefined ? Number(args.maxPropertyResults) || 20 : 20;
|
||||
return this.searchNodeProperties(args.nodeType, args.propertyQuery, maxResults);
|
||||
}
|
||||
return this.getNode(
|
||||
args.nodeType,
|
||||
args.detail,
|
||||
@@ -1032,15 +1036,23 @@ export class N8NDocumentationMCPServer {
|
||||
args.fromVersion,
|
||||
args.toVersion
|
||||
);
|
||||
case 'search_node_properties':
|
||||
this.validateToolParams(name, args, ['nodeType', 'query']);
|
||||
const maxResults = args.maxResults !== undefined ? Number(args.maxResults) || 20 : 20;
|
||||
return this.searchNodeProperties(args.nodeType, args.query, maxResults);
|
||||
case 'validate_node_operation':
|
||||
case 'validate_node':
|
||||
this.validateToolParams(name, args, ['nodeType', 'config']);
|
||||
// Ensure config is an object
|
||||
if (typeof args.config !== 'object' || args.config === null) {
|
||||
logger.warn(`validate_node_operation called with invalid config type: ${typeof args.config}`);
|
||||
logger.warn(`validate_node called with invalid config type: ${typeof args.config}`);
|
||||
const validationMode = args.mode || 'full';
|
||||
if (validationMode === 'minimal') {
|
||||
return {
|
||||
nodeType: args.nodeType || 'unknown',
|
||||
displayName: 'Unknown Node',
|
||||
valid: false,
|
||||
missingRequiredFields: [
|
||||
'Invalid config format - expected object',
|
||||
'🔧 RECOVERY: Use format { "resource": "...", "operation": "..." } or {} for empty config'
|
||||
]
|
||||
};
|
||||
}
|
||||
return {
|
||||
nodeType: args.nodeType || 'unknown',
|
||||
workflowNodeType: args.nodeType || 'unknown',
|
||||
@@ -1056,7 +1068,7 @@ export class N8NDocumentationMCPServer {
|
||||
suggestions: [
|
||||
'🔧 RECOVERY: Invalid config detected. Fix with:',
|
||||
' • Ensure config is an object: { "resource": "...", "operation": "..." }',
|
||||
' • Use get_node_essentials to see required fields for this node type',
|
||||
' • Use get_node to see required fields for this node type',
|
||||
' • Check if the node type is correct before configuring it'
|
||||
],
|
||||
summary: {
|
||||
@@ -1067,59 +1079,52 @@ export class N8NDocumentationMCPServer {
|
||||
}
|
||||
};
|
||||
}
|
||||
return this.validateNodeConfig(args.nodeType, args.config, 'operation', args.profile);
|
||||
case 'validate_node_minimal':
|
||||
this.validateToolParams(name, args, ['nodeType', 'config']);
|
||||
// Ensure config is an object
|
||||
if (typeof args.config !== 'object' || args.config === null) {
|
||||
logger.warn(`validate_node_minimal called with invalid config type: ${typeof args.config}`);
|
||||
return {
|
||||
nodeType: args.nodeType || 'unknown',
|
||||
displayName: 'Unknown Node',
|
||||
valid: false,
|
||||
missingRequiredFields: [
|
||||
'Invalid config format - expected object',
|
||||
'🔧 RECOVERY: Use format { "resource": "...", "operation": "..." } or {} for empty config'
|
||||
]
|
||||
};
|
||||
// Handle mode parameter
|
||||
const validationMode = args.mode || 'full';
|
||||
if (validationMode === 'minimal') {
|
||||
return this.validateNodeMinimal(args.nodeType, args.config);
|
||||
}
|
||||
return this.validateNodeMinimal(args.nodeType, args.config);
|
||||
case 'get_property_dependencies':
|
||||
this.validateToolParams(name, args, ['nodeType']);
|
||||
return this.getPropertyDependencies(args.nodeType, args.config);
|
||||
case 'list_node_templates':
|
||||
this.validateToolParams(name, args, ['nodeTypes']);
|
||||
const templateLimit = Math.min(Math.max(Number(args.limit) || 10, 1), 100);
|
||||
const templateOffset = Math.max(Number(args.offset) || 0, 0);
|
||||
return this.listNodeTemplates(args.nodeTypes, templateLimit, templateOffset);
|
||||
return this.validateNodeConfig(args.nodeType, args.config, 'operation', args.profile);
|
||||
case 'get_template':
|
||||
this.validateToolParams(name, args, ['templateId']);
|
||||
const templateId = Number(args.templateId);
|
||||
const mode = args.mode || 'full';
|
||||
return this.getTemplate(templateId, mode);
|
||||
case 'search_templates':
|
||||
this.validateToolParams(name, args, ['query']);
|
||||
const templateMode = args.mode || 'full';
|
||||
return this.getTemplate(templateId, templateMode);
|
||||
case 'search_templates': {
|
||||
// Consolidated tool with searchMode parameter
|
||||
const searchMode = args.searchMode || 'keyword';
|
||||
const searchLimit = Math.min(Math.max(Number(args.limit) || 20, 1), 100);
|
||||
const searchOffset = Math.max(Number(args.offset) || 0, 0);
|
||||
const searchFields = args.fields as string[] | undefined;
|
||||
return this.searchTemplates(args.query, searchLimit, searchOffset, searchFields);
|
||||
case 'get_templates_for_task':
|
||||
this.validateToolParams(name, args, ['task']);
|
||||
const taskLimit = Math.min(Math.max(Number(args.limit) || 10, 1), 100);
|
||||
const taskOffset = Math.max(Number(args.offset) || 0, 0);
|
||||
return this.getTemplatesForTask(args.task, taskLimit, taskOffset);
|
||||
case 'search_templates_by_metadata':
|
||||
// No required params - all filters are optional
|
||||
const metadataLimit = Math.min(Math.max(Number(args.limit) || 20, 1), 100);
|
||||
const metadataOffset = Math.max(Number(args.offset) || 0, 0);
|
||||
return this.searchTemplatesByMetadata({
|
||||
category: args.category,
|
||||
complexity: args.complexity,
|
||||
maxSetupMinutes: args.maxSetupMinutes ? Number(args.maxSetupMinutes) : undefined,
|
||||
minSetupMinutes: args.minSetupMinutes ? Number(args.minSetupMinutes) : undefined,
|
||||
requiredService: args.requiredService,
|
||||
targetAudience: args.targetAudience
|
||||
}, metadataLimit, metadataOffset);
|
||||
|
||||
switch (searchMode) {
|
||||
case 'by_nodes':
|
||||
if (!args.nodeTypes || !Array.isArray(args.nodeTypes) || args.nodeTypes.length === 0) {
|
||||
throw new Error('nodeTypes array is required for searchMode=by_nodes');
|
||||
}
|
||||
return this.listNodeTemplates(args.nodeTypes, searchLimit, searchOffset);
|
||||
case 'by_task':
|
||||
if (!args.task) {
|
||||
throw new Error('task is required for searchMode=by_task');
|
||||
}
|
||||
return this.getTemplatesForTask(args.task, searchLimit, searchOffset);
|
||||
case 'by_metadata':
|
||||
return this.searchTemplatesByMetadata({
|
||||
category: args.category,
|
||||
complexity: args.complexity,
|
||||
maxSetupMinutes: args.maxSetupMinutes ? Number(args.maxSetupMinutes) : undefined,
|
||||
minSetupMinutes: args.minSetupMinutes ? Number(args.minSetupMinutes) : undefined,
|
||||
requiredService: args.requiredService,
|
||||
targetAudience: args.targetAudience
|
||||
}, searchLimit, searchOffset);
|
||||
case 'keyword':
|
||||
default:
|
||||
if (!args.query) {
|
||||
throw new Error('query is required for searchMode=keyword');
|
||||
}
|
||||
const searchFields = args.fields as string[] | undefined;
|
||||
return this.searchTemplates(args.query, searchLimit, searchOffset, searchFields);
|
||||
}
|
||||
}
|
||||
case 'validate_workflow':
|
||||
this.validateToolParams(name, args, ['workflow']);
|
||||
return this.validateWorkflow(args.workflow, args.options);
|
||||
@@ -1128,18 +1133,21 @@ export class N8NDocumentationMCPServer {
|
||||
case 'n8n_create_workflow':
|
||||
this.validateToolParams(name, args, ['name', 'nodes', 'connections']);
|
||||
return n8nHandlers.handleCreateWorkflow(args, this.instanceContext);
|
||||
case 'n8n_get_workflow':
|
||||
case 'n8n_get_workflow': {
|
||||
this.validateToolParams(name, args, ['id']);
|
||||
return n8nHandlers.handleGetWorkflow(args, this.instanceContext);
|
||||
case 'n8n_get_workflow_details':
|
||||
this.validateToolParams(name, args, ['id']);
|
||||
return n8nHandlers.handleGetWorkflowDetails(args, this.instanceContext);
|
||||
case 'n8n_get_workflow_structure':
|
||||
this.validateToolParams(name, args, ['id']);
|
||||
return n8nHandlers.handleGetWorkflowStructure(args, this.instanceContext);
|
||||
case 'n8n_get_workflow_minimal':
|
||||
this.validateToolParams(name, args, ['id']);
|
||||
return n8nHandlers.handleGetWorkflowMinimal(args, this.instanceContext);
|
||||
const workflowMode = args.mode || 'full';
|
||||
switch (workflowMode) {
|
||||
case 'details':
|
||||
return n8nHandlers.handleGetWorkflowDetails(args, this.instanceContext);
|
||||
case 'structure':
|
||||
return n8nHandlers.handleGetWorkflowStructure(args, this.instanceContext);
|
||||
case 'minimal':
|
||||
return n8nHandlers.handleGetWorkflowMinimal(args, this.instanceContext);
|
||||
case 'full':
|
||||
default:
|
||||
return n8nHandlers.handleGetWorkflow(args, this.instanceContext);
|
||||
}
|
||||
}
|
||||
case 'n8n_update_full_workflow':
|
||||
this.validateToolParams(name, args, ['id']);
|
||||
return n8nHandlers.handleUpdateWorkflow(args, this.repository!, this.instanceContext);
|
||||
@@ -1165,15 +1173,26 @@ export class N8NDocumentationMCPServer {
|
||||
case 'n8n_trigger_webhook_workflow':
|
||||
this.validateToolParams(name, args, ['webhookUrl']);
|
||||
return n8nHandlers.handleTriggerWebhookWorkflow(args, this.instanceContext);
|
||||
case 'n8n_get_execution':
|
||||
this.validateToolParams(name, args, ['id']);
|
||||
return n8nHandlers.handleGetExecution(args, this.instanceContext);
|
||||
case 'n8n_list_executions':
|
||||
// No required parameters
|
||||
return n8nHandlers.handleListExecutions(args, this.instanceContext);
|
||||
case 'n8n_delete_execution':
|
||||
this.validateToolParams(name, args, ['id']);
|
||||
return n8nHandlers.handleDeleteExecution(args, this.instanceContext);
|
||||
case 'n8n_executions': {
|
||||
this.validateToolParams(name, args, ['action']);
|
||||
const execAction = args.action;
|
||||
switch (execAction) {
|
||||
case 'get':
|
||||
if (!args.id) {
|
||||
throw new Error('id is required for action=get');
|
||||
}
|
||||
return n8nHandlers.handleGetExecution(args, this.instanceContext);
|
||||
case 'list':
|
||||
return n8nHandlers.handleListExecutions(args, this.instanceContext);
|
||||
case 'delete':
|
||||
if (!args.id) {
|
||||
throw new Error('id is required for action=delete');
|
||||
}
|
||||
return n8nHandlers.handleDeleteExecution(args, this.instanceContext);
|
||||
default:
|
||||
throw new Error(`Unknown action: ${execAction}. Valid actions: get, list, delete`);
|
||||
}
|
||||
}
|
||||
case 'n8n_health_check':
|
||||
// No required parameters - supports mode='status' (default) or mode='diagnostic'
|
||||
if (args.mode === 'diagnostic') {
|
||||
|
||||
Reference in New Issue
Block a user