diff --git a/src/mcp/handlers-n8n-manager.ts b/src/mcp/handlers-n8n-manager.ts index 1558894..c9ced18 100644 --- a/src/mcp/handlers-n8n-manager.ts +++ b/src/mcp/handlers-n8n-manager.ts @@ -1844,8 +1844,8 @@ export async function handleDiagnostic(request: any, context?: InstanceContext): } // Check which tools are available - const documentationTools = 22; // Base documentation tools - const managementTools = apiConfigured ? 16 : 0; + const documentationTools = 14; // Base documentation tools (after v2.25.0 cleanup) + const managementTools = apiConfigured ? 17 : 0; // Management tools requiring API (includes n8n_health_check) const totalTools = documentationTools + managementTools; // Check npm version diff --git a/src/mcp/server.ts b/src/mcp/server.ts index 28dd769..11932f9 100644 --- a/src/mcp/server.ts +++ b/src/mcp/server.ts @@ -837,8 +837,6 @@ export class N8NDocumentationMCPServer { validationResult = ToolValidation.validateNodeMinimal(args); break; case 'validate_workflow': - case 'validate_workflow_connections': - case 'validate_workflow_expressions': validationResult = ToolValidation.validateWorkflow(args); break; case 'search_nodes': @@ -1015,23 +1013,14 @@ export class N8NDocumentationMCPServer { case 'tools_documentation': // No required parameters return this.getToolsDocumentation(args.topic, args.depth); - case 'list_nodes': - // No required parameters - return this.listNodes(args); case 'search_nodes': this.validateToolParams(name, args, ['query']); // 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 'list_ai_tools': - // No required parameters - return this.listAITools(); case 'get_node_documentation': this.validateToolParams(name, args, ['nodeType']); return this.getNodeDocumentation(args.nodeType); - case 'get_database_statistics': - // No required parameters - return this.getDatabaseStatistics(); case 'get_node': this.validateToolParams(name, args, ['nodeType']); return this.getNode( @@ -1047,9 +1036,6 @@ export class N8NDocumentationMCPServer { 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 'list_tasks': - // No required parameters - return this.listTasks(args.category); case 'validate_node_operation': this.validateToolParams(name, args, ['nodeType', 'config']); // Ensure config is an object @@ -1101,16 +1087,6 @@ export class N8NDocumentationMCPServer { case 'get_property_dependencies': this.validateToolParams(name, args, ['nodeType']); return this.getPropertyDependencies(args.nodeType, args.config); - case 'get_node_as_tool_info': - this.validateToolParams(name, args, ['nodeType']); - return this.getNodeAsToolInfo(args.nodeType); - case 'list_templates': - // No required params - const listLimit = Math.min(Math.max(Number(args.limit) || 10, 1), 100); - const listOffset = Math.max(Number(args.offset) || 0, 0); - const sortBy = args.sortBy || 'views'; - const includeMetadata = Boolean(args.includeMetadata); - return this.listTemplates(listLimit, listOffset, sortBy, includeMetadata); case 'list_node_templates': this.validateToolParams(name, args, ['nodeTypes']); const templateLimit = Math.min(Math.max(Number(args.limit) || 10, 1), 100); @@ -1147,13 +1123,7 @@ export class N8NDocumentationMCPServer { case 'validate_workflow': this.validateToolParams(name, args, ['workflow']); return this.validateWorkflow(args.workflow, args.options); - case 'validate_workflow_connections': - this.validateToolParams(name, args, ['workflow']); - return this.validateWorkflowConnections(args.workflow); - case 'validate_workflow_expressions': - this.validateToolParams(name, args, ['workflow']); - return this.validateWorkflowExpressions(args.workflow); - + // n8n Management Tools (if API is configured) case 'n8n_create_workflow': this.validateToolParams(name, args, ['name', 'nodes', 'connections']); @@ -1205,14 +1175,11 @@ export class N8NDocumentationMCPServer { this.validateToolParams(name, args, ['id']); return n8nHandlers.handleDeleteExecution(args, this.instanceContext); case 'n8n_health_check': - // No required parameters + // No required parameters - supports mode='status' (default) or mode='diagnostic' + if (args.mode === 'diagnostic') { + return n8nHandlers.handleDiagnostic({ params: { arguments: args } }, this.instanceContext); + } return n8nHandlers.handleHealthCheck(this.instanceContext); - case 'n8n_list_available_tools': - // No required parameters - return n8nHandlers.handleListAvailableTools(this.instanceContext); - case 'n8n_diagnostic': - // No required parameters - return n8nHandlers.handleDiagnostic({ params: { arguments: args } }, this.instanceContext); case 'n8n_workflow_versions': this.validateToolParams(name, args, ['mode']); return n8nHandlers.handleWorkflowVersions(args, this.repository!, this.instanceContext); diff --git a/src/mcp/tools-n8n-manager.ts b/src/mcp/tools-n8n-manager.ts index 4ee4c81..e7db76d 100644 --- a/src/mcp/tools-n8n-manager.ts +++ b/src/mcp/tools-n8n-manager.ts @@ -436,29 +436,19 @@ Examples: // System Tools { name: 'n8n_health_check', - description: `Check n8n instance health and API connectivity. Returns status and available features.`, - inputSchema: { - type: 'object', - properties: {} - } - }, - { - name: 'n8n_list_available_tools', - description: `List available n8n tools and capabilities.`, - inputSchema: { - type: 'object', - properties: {} - } - }, - { - name: 'n8n_diagnostic', - description: `Diagnose n8n API config. Shows tool status, API connectivity, env vars. Helps troubleshoot missing tools.`, + description: `Check n8n instance health and API connectivity. Use mode='diagnostic' for detailed troubleshooting with env vars and tool status.`, inputSchema: { type: 'object', properties: { + mode: { + type: 'string', + enum: ['status', 'diagnostic'], + description: 'Mode: "status" (default) for quick health check, "diagnostic" for detailed debug info including env vars and tool status', + default: 'status' + }, verbose: { type: 'boolean', - description: 'Include detailed debug information (default: false)' + description: 'Include extra details in diagnostic mode (default: false)' } } } diff --git a/src/mcp/tools.ts b/src/mcp/tools.ts index fee8d03..c61e548 100644 --- a/src/mcp/tools.ts +++ b/src/mcp/tools.ts @@ -26,37 +26,6 @@ export const n8nDocumentationToolsFinal: ToolDefinition[] = [ }, }, }, - { - name: 'list_nodes', - description: `List n8n nodes. Common: list_nodes({limit:200}) for all, list_nodes({category:'trigger'}) for triggers. Package: "n8n-nodes-base" or "@n8n/n8n-nodes-langchain". Categories: trigger/transform/output/input.`, - inputSchema: { - type: 'object', - properties: { - package: { - type: 'string', - description: '"n8n-nodes-base" (core) or "@n8n/n8n-nodes-langchain" (AI)', - }, - category: { - type: 'string', - description: 'trigger|transform|output|input|AI', - }, - developmentStyle: { - type: 'string', - enum: ['declarative', 'programmatic'], - description: 'Usually "programmatic"', - }, - isAITool: { - type: 'boolean', - description: 'Filter AI-capable nodes', - }, - limit: { - type: 'number', - description: 'Max results (default 50, use 200+ for all)', - default: 50, - }, - }, - }, - }, { name: 'search_nodes', description: `Search n8n nodes by keyword with optional real-world examples. Pass query as string. Example: query="webhook" or query="database". Returns max 20 results. Use includeExamples=true to get top 2 template configs per node.`, @@ -87,14 +56,6 @@ export const n8nDocumentationToolsFinal: ToolDefinition[] = [ required: ['query'], }, }, - { - name: 'list_ai_tools', - description: `List 263 AI-optimized nodes. Note: ANY node can be AI tool! Connect any node to AI Agent's tool port. Community nodes need N8N_COMMUNITY_PACKAGES_ALLOW_TOOL_USAGE=true.`, - inputSchema: { - type: 'object', - properties: {}, - }, - }, { name: 'get_node_documentation', description: `Get readable docs with examples/auth/patterns. Better than raw schema! 87% coverage. Format: "nodes-base.slack"`, @@ -109,14 +70,6 @@ export const n8nDocumentationToolsFinal: ToolDefinition[] = [ required: ['nodeType'], }, }, - { - name: 'get_database_statistics', - description: `Node stats: 525 total, 263 AI tools, 104 triggers, 87% docs coverage. Verifies MCP working.`, - inputSchema: { - type: 'object', - properties: {}, - }, - }, { name: 'get_node', description: `Get node info with progressive detail levels. Detail: minimal (~200 tokens), standard (~1-2K, default), full (~3-8K). Version modes: versions (history), compare (diff), breaking (changes), migrations (auto-migrate). Supports includeTypeInfo and includeExamples. Use standard for most tasks.`, @@ -184,19 +137,6 @@ export const n8nDocumentationToolsFinal: ToolDefinition[] = [ required: ['nodeType', 'query'], }, }, - { - name: 'list_tasks', - description: `List task templates by category: HTTP/API, Webhooks, Database, AI, Data Processing, Communication.`, - inputSchema: { - type: 'object', - properties: { - category: { - type: 'string', - description: 'Filter by category (optional)', - }, - }, - }, - }, { name: 'validate_node_operation', description: `Validate n8n node configuration. Pass nodeType as string and config as object. Example: nodeType="nodes-base.slack", config={resource:"channel",operation:"create"}`, @@ -316,53 +256,6 @@ export const n8nDocumentationToolsFinal: ToolDefinition[] = [ required: ['nodeType'], }, }, - { - name: 'get_node_as_tool_info', - description: `How to use ANY node as AI tool. Shows requirements, use cases, examples. Works for all nodes, not just AI-marked ones.`, - inputSchema: { - type: 'object', - properties: { - nodeType: { - type: 'string', - description: 'Full node type WITH prefix: "nodes-base.slack", "nodes-base.googleSheets", etc.', - }, - }, - required: ['nodeType'], - }, - }, - { - name: 'list_templates', - description: `List all templates with minimal data (id, name, description, views, node count). Optionally include AI-generated metadata for smart filtering.`, - inputSchema: { - type: 'object', - properties: { - limit: { - type: 'number', - description: 'Number of results (1-100). Default 10.', - default: 10, - minimum: 1, - maximum: 100, - }, - offset: { - type: 'number', - description: 'Pagination offset. Default 0.', - default: 0, - minimum: 0, - }, - sortBy: { - type: 'string', - enum: ['views', 'created_at', 'name'], - description: 'Sort field. Default: views (popularity).', - default: 'views', - }, - includeMetadata: { - type: 'boolean', - description: 'Include AI-generated metadata (categories, complexity, setup time, etc.). Default false.', - default: false, - }, - }, - }, - }, { name: 'list_node_templates', description: `Find templates using specific nodes. Returns paginated results. Use FULL types: "n8n-nodes-base.httpRequest".`, @@ -622,107 +515,6 @@ export const n8nDocumentationToolsFinal: ToolDefinition[] = [ required: ['valid', 'summary'] }, }, - { - name: 'validate_workflow_connections', - description: `Check workflow connections only: valid nodes, no cycles, proper triggers, AI tool links. Fast structure validation.`, - inputSchema: { - type: 'object', - properties: { - workflow: { - type: 'object', - description: 'The workflow JSON with nodes array and connections object.', - }, - }, - required: ['workflow'], - additionalProperties: false, - }, - outputSchema: { - type: 'object', - properties: { - valid: { type: 'boolean' }, - statistics: { - type: 'object', - properties: { - totalNodes: { type: 'number' }, - triggerNodes: { type: 'number' }, - validConnections: { type: 'number' }, - invalidConnections: { type: 'number' } - } - }, - errors: { - type: 'array', - items: { - type: 'object', - properties: { - node: { type: 'string' }, - message: { type: 'string' } - } - } - }, - warnings: { - type: 'array', - items: { - type: 'object', - properties: { - node: { type: 'string' }, - message: { type: 'string' } - } - } - } - }, - required: ['valid', 'statistics'] - }, - }, - { - name: 'validate_workflow_expressions', - description: `Validate n8n expressions: syntax {{}}, variables ($json/$node), references. Returns errors with locations.`, - inputSchema: { - type: 'object', - properties: { - workflow: { - type: 'object', - description: 'The workflow JSON to check for expression errors.', - }, - }, - required: ['workflow'], - additionalProperties: false, - }, - outputSchema: { - type: 'object', - properties: { - valid: { type: 'boolean' }, - statistics: { - type: 'object', - properties: { - totalNodes: { type: 'number' }, - expressionsValidated: { type: 'number' } - } - }, - errors: { - type: 'array', - items: { - type: 'object', - properties: { - node: { type: 'string' }, - message: { type: 'string' } - } - } - }, - warnings: { - type: 'array', - items: { - type: 'object', - properties: { - node: { type: 'string' }, - message: { type: 'string' } - } - } - }, - tips: { type: 'array', items: { type: 'string' } } - }, - required: ['valid', 'statistics'] - }, - }, ]; /** diff --git a/tests/integration/mcp-protocol/protocol-compliance.test.ts b/tests/integration/mcp-protocol/protocol-compliance.test.ts index d802aef..9f8eac2 100644 --- a/tests/integration/mcp-protocol/protocol-compliance.test.ts +++ b/tests/integration/mcp-protocol/protocol-compliance.test.ts @@ -74,7 +74,7 @@ describe('MCP Protocol Compliance', () => { for (let i = 0; i < 5; i++) { expectedOrder.push(i); requests.push( - client.callTool({ name: 'get_database_statistics', arguments: {} }) + client.callTool({ name: 'tools_documentation', arguments: {} }) .then(() => i) ); } @@ -125,7 +125,7 @@ describe('MCP Protocol Compliance', () => { it('should handle missing params gracefully', async () => { // Most tools should work without params - const response = await client.callTool({ name: 'list_nodes', arguments: {} }); + const response = await client.callTool({ name: 'search_nodes', arguments: { query: 'webhook' } }); expect(response).toBeDefined(); }); @@ -147,8 +147,8 @@ describe('MCP Protocol Compliance', () => { describe('Content Types', () => { it('should handle text content in tool responses', async () => { - const response = await client.callTool({ name: 'get_database_statistics', arguments: {} }); - + const response = await client.callTool({ name: 'tools_documentation', arguments: {} }); + expect((response as any).content).toHaveLength(1); expect((response as any).content[0]).toHaveProperty('type', 'text'); expect((response as any).content[0]).toHaveProperty('text'); @@ -167,14 +167,15 @@ describe('MCP Protocol Compliance', () => { }); it('should handle JSON content properly', async () => { - const response = await client.callTool({ name: 'list_nodes', arguments: { + const response = await client.callTool({ name: 'search_nodes', arguments: { + query: 'webhook', limit: 5 } }); expect((response as any).content).toHaveLength(1); const content = JSON.parse((response as any).content[0].text); - expect(content).toHaveProperty('nodes'); - expect(Array.isArray(content.nodes)).toBe(true); + expect(content).toHaveProperty('results'); + expect(Array.isArray(content.results)).toBe(true); }); }); @@ -197,10 +198,10 @@ describe('MCP Protocol Compliance', () => { const results: string[] = []; // Start multiple requests with different delays - const p1 = client.callTool({ name: 'get_database_statistics', arguments: {} }) - .then(() => { results.push('stats'); return 'stats'; }); + const p1 = client.callTool({ name: 'tools_documentation', arguments: {} }) + .then(() => { results.push('docs'); return 'docs'; }); - const p2 = client.callTool({ name: 'list_nodes', arguments: { limit: 1 } }) + const p2 = client.callTool({ name: 'search_nodes', arguments: { query: 'webhook', limit: 1 } }) .then(() => { results.push('nodes'); return 'nodes'; }); const p3 = client.callTool({ name: 'search_nodes', arguments: { query: 'http' } }) @@ -232,13 +233,13 @@ describe('MCP Protocol Compliance', () => { it('should support optional parameters', async () => { // Call with minimal params - const response1 = await client.callTool({ name: 'list_nodes', arguments: {} }); - + const response1 = await client.callTool({ name: 'search_nodes', arguments: { query: 'webhook' } }); + // Call with all params - const response2 = await client.callTool({ name: 'list_nodes', arguments: { + const response2 = await client.callTool({ name: 'search_nodes', arguments: { + query: 'webhook', limit: 10, - category: 'trigger', - package: 'n8n-nodes-base' + mode: 'OR' } }); expect(response1).toBeDefined(); @@ -255,7 +256,7 @@ describe('MCP Protocol Compliance', () => { await testClient.connect(clientTransport); // Make a request - const response = await testClient.callTool({ name: 'get_database_statistics', arguments: {} }); + const response = await testClient.callTool({ name: 'tools_documentation', arguments: {} }); expect(response).toBeDefined(); // Close client @@ -263,7 +264,7 @@ describe('MCP Protocol Compliance', () => { // Further requests should fail try { - await testClient.callTool({ name: 'get_database_statistics', arguments: {} }); + await testClient.callTool({ name: 'tools_documentation', arguments: {} }); expect.fail('Should have thrown an error'); } catch (error) { expect(error).toBeDefined(); @@ -286,7 +287,7 @@ describe('MCP Protocol Compliance', () => { const testClient = new Client({ name: 'test', version: '1.0.0' }, {}); await testClient.connect(clientTransport); - const response = await testClient.callTool({ name: 'get_database_statistics', arguments: {} }); + const response = await testClient.callTool({ name: 'tools_documentation', arguments: {} }); expect(response).toBeDefined(); await testClient.close(); diff --git a/tests/unit/mcp/handlers-n8n-manager.test.ts b/tests/unit/mcp/handlers-n8n-manager.test.ts index 59fd23b..e3e583d 100644 --- a/tests/unit/mcp/handlers-n8n-manager.test.ts +++ b/tests/unit/mcp/handlers-n8n-manager.test.ts @@ -1068,14 +1068,14 @@ describe('handlers-n8n-manager', () => { }, toolsAvailability: { documentationTools: { - count: 22, + count: 14, enabled: true, }, managementTools: { - count: 16, + count: 17, enabled: true, }, - totalAvailable: 38, + totalAvailable: 31, }, });