mirror of
https://github.com/czlonkowski/n8n-mcp.git
synced 2026-02-06 05:23:08 +00:00
feat: Remove 9 low-value tools and consolidate n8n_health_check (v2.25.0)
Telemetry-driven tool cleanup to improve API clarity: **Removed Tools (9):** - list_nodes - Use search_nodes instead - list_ai_tools - Use search_nodes with isAITool filter - list_tasks - Low usage (0.02%) - get_database_statistics - Use n8n_health_check - list_templates - Use search_templates or get_templates_for_task - get_node_as_tool_info - Documented in get_node - validate_workflow_connections - Use validate_workflow - validate_workflow_expressions - Use validate_workflow - n8n_list_available_tools - Use n8n_health_check - n8n_diagnostic - Merged into n8n_health_check **Consolidated Tool:** - n8n_health_check now supports mode='diagnostic' for detailed troubleshooting **Tool Count:** - Before: 38 tools - After: 31 tools (18% reduction) Concieved by Romuald Członkowski - www.aiadvisors.pl/en 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1844,8 +1844,8 @@ export async function handleDiagnostic(request: any, context?: InstanceContext):
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check which tools are available
|
// Check which tools are available
|
||||||
const documentationTools = 22; // Base documentation tools
|
const documentationTools = 14; // Base documentation tools (after v2.25.0 cleanup)
|
||||||
const managementTools = apiConfigured ? 16 : 0;
|
const managementTools = apiConfigured ? 17 : 0; // Management tools requiring API (includes n8n_health_check)
|
||||||
const totalTools = documentationTools + managementTools;
|
const totalTools = documentationTools + managementTools;
|
||||||
|
|
||||||
// Check npm version
|
// Check npm version
|
||||||
|
|||||||
@@ -837,8 +837,6 @@ export class N8NDocumentationMCPServer {
|
|||||||
validationResult = ToolValidation.validateNodeMinimal(args);
|
validationResult = ToolValidation.validateNodeMinimal(args);
|
||||||
break;
|
break;
|
||||||
case 'validate_workflow':
|
case 'validate_workflow':
|
||||||
case 'validate_workflow_connections':
|
|
||||||
case 'validate_workflow_expressions':
|
|
||||||
validationResult = ToolValidation.validateWorkflow(args);
|
validationResult = ToolValidation.validateWorkflow(args);
|
||||||
break;
|
break;
|
||||||
case 'search_nodes':
|
case 'search_nodes':
|
||||||
@@ -1015,23 +1013,14 @@ export class N8NDocumentationMCPServer {
|
|||||||
case 'tools_documentation':
|
case 'tools_documentation':
|
||||||
// No required parameters
|
// No required parameters
|
||||||
return this.getToolsDocumentation(args.topic, args.depth);
|
return this.getToolsDocumentation(args.topic, args.depth);
|
||||||
case 'list_nodes':
|
|
||||||
// No required parameters
|
|
||||||
return this.listNodes(args);
|
|
||||||
case 'search_nodes':
|
case 'search_nodes':
|
||||||
this.validateToolParams(name, args, ['query']);
|
this.validateToolParams(name, args, ['query']);
|
||||||
// Convert limit to number if provided, otherwise use default
|
// Convert limit to number if provided, otherwise use default
|
||||||
const limit = args.limit !== undefined ? Number(args.limit) || 20 : 20;
|
const limit = args.limit !== undefined ? Number(args.limit) || 20 : 20;
|
||||||
return this.searchNodes(args.query, limit, { mode: args.mode, includeExamples: args.includeExamples });
|
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':
|
case 'get_node_documentation':
|
||||||
this.validateToolParams(name, args, ['nodeType']);
|
this.validateToolParams(name, args, ['nodeType']);
|
||||||
return this.getNodeDocumentation(args.nodeType);
|
return this.getNodeDocumentation(args.nodeType);
|
||||||
case 'get_database_statistics':
|
|
||||||
// No required parameters
|
|
||||||
return this.getDatabaseStatistics();
|
|
||||||
case 'get_node':
|
case 'get_node':
|
||||||
this.validateToolParams(name, args, ['nodeType']);
|
this.validateToolParams(name, args, ['nodeType']);
|
||||||
return this.getNode(
|
return this.getNode(
|
||||||
@@ -1047,9 +1036,6 @@ export class N8NDocumentationMCPServer {
|
|||||||
this.validateToolParams(name, args, ['nodeType', 'query']);
|
this.validateToolParams(name, args, ['nodeType', 'query']);
|
||||||
const maxResults = args.maxResults !== undefined ? Number(args.maxResults) || 20 : 20;
|
const maxResults = args.maxResults !== undefined ? Number(args.maxResults) || 20 : 20;
|
||||||
return this.searchNodeProperties(args.nodeType, args.query, maxResults);
|
return this.searchNodeProperties(args.nodeType, args.query, maxResults);
|
||||||
case 'list_tasks':
|
|
||||||
// No required parameters
|
|
||||||
return this.listTasks(args.category);
|
|
||||||
case 'validate_node_operation':
|
case 'validate_node_operation':
|
||||||
this.validateToolParams(name, args, ['nodeType', 'config']);
|
this.validateToolParams(name, args, ['nodeType', 'config']);
|
||||||
// Ensure config is an object
|
// Ensure config is an object
|
||||||
@@ -1101,16 +1087,6 @@ export class N8NDocumentationMCPServer {
|
|||||||
case 'get_property_dependencies':
|
case 'get_property_dependencies':
|
||||||
this.validateToolParams(name, args, ['nodeType']);
|
this.validateToolParams(name, args, ['nodeType']);
|
||||||
return this.getPropertyDependencies(args.nodeType, args.config);
|
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':
|
case 'list_node_templates':
|
||||||
this.validateToolParams(name, args, ['nodeTypes']);
|
this.validateToolParams(name, args, ['nodeTypes']);
|
||||||
const templateLimit = Math.min(Math.max(Number(args.limit) || 10, 1), 100);
|
const templateLimit = Math.min(Math.max(Number(args.limit) || 10, 1), 100);
|
||||||
@@ -1147,12 +1123,6 @@ export class N8NDocumentationMCPServer {
|
|||||||
case 'validate_workflow':
|
case 'validate_workflow':
|
||||||
this.validateToolParams(name, args, ['workflow']);
|
this.validateToolParams(name, args, ['workflow']);
|
||||||
return this.validateWorkflow(args.workflow, args.options);
|
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)
|
// n8n Management Tools (if API is configured)
|
||||||
case 'n8n_create_workflow':
|
case 'n8n_create_workflow':
|
||||||
@@ -1205,14 +1175,11 @@ export class N8NDocumentationMCPServer {
|
|||||||
this.validateToolParams(name, args, ['id']);
|
this.validateToolParams(name, args, ['id']);
|
||||||
return n8nHandlers.handleDeleteExecution(args, this.instanceContext);
|
return n8nHandlers.handleDeleteExecution(args, this.instanceContext);
|
||||||
case 'n8n_health_check':
|
case 'n8n_health_check':
|
||||||
// No required parameters
|
// No required parameters - supports mode='status' (default) or mode='diagnostic'
|
||||||
return n8nHandlers.handleHealthCheck(this.instanceContext);
|
if (args.mode === 'diagnostic') {
|
||||||
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);
|
return n8nHandlers.handleDiagnostic({ params: { arguments: args } }, this.instanceContext);
|
||||||
|
}
|
||||||
|
return n8nHandlers.handleHealthCheck(this.instanceContext);
|
||||||
case 'n8n_workflow_versions':
|
case 'n8n_workflow_versions':
|
||||||
this.validateToolParams(name, args, ['mode']);
|
this.validateToolParams(name, args, ['mode']);
|
||||||
return n8nHandlers.handleWorkflowVersions(args, this.repository!, this.instanceContext);
|
return n8nHandlers.handleWorkflowVersions(args, this.repository!, this.instanceContext);
|
||||||
|
|||||||
@@ -436,29 +436,19 @@ Examples:
|
|||||||
// System Tools
|
// System Tools
|
||||||
{
|
{
|
||||||
name: 'n8n_health_check',
|
name: 'n8n_health_check',
|
||||||
description: `Check n8n instance health and API connectivity. Returns status and available features.`,
|
description: `Check n8n instance health and API connectivity. Use mode='diagnostic' for detailed troubleshooting with env vars and tool status.`,
|
||||||
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.`,
|
|
||||||
inputSchema: {
|
inputSchema: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
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: {
|
verbose: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
description: 'Include detailed debug information (default: false)'
|
description: 'Include extra details in diagnostic mode (default: false)'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
208
src/mcp/tools.ts
208
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',
|
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.`,
|
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'],
|
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',
|
name: 'get_node_documentation',
|
||||||
description: `Get readable docs with examples/auth/patterns. Better than raw schema! 87% coverage. Format: "nodes-base.slack"`,
|
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'],
|
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',
|
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.`,
|
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'],
|
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',
|
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"}`,
|
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'],
|
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',
|
name: 'list_node_templates',
|
||||||
description: `Find templates using specific nodes. Returns paginated results. Use FULL types: "n8n-nodes-base.httpRequest".`,
|
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']
|
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']
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ describe('MCP Protocol Compliance', () => {
|
|||||||
for (let i = 0; i < 5; i++) {
|
for (let i = 0; i < 5; i++) {
|
||||||
expectedOrder.push(i);
|
expectedOrder.push(i);
|
||||||
requests.push(
|
requests.push(
|
||||||
client.callTool({ name: 'get_database_statistics', arguments: {} })
|
client.callTool({ name: 'tools_documentation', arguments: {} })
|
||||||
.then(() => i)
|
.then(() => i)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -125,7 +125,7 @@ describe('MCP Protocol Compliance', () => {
|
|||||||
|
|
||||||
it('should handle missing params gracefully', async () => {
|
it('should handle missing params gracefully', async () => {
|
||||||
// Most tools should work without params
|
// 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();
|
expect(response).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -147,7 +147,7 @@ describe('MCP Protocol Compliance', () => {
|
|||||||
|
|
||||||
describe('Content Types', () => {
|
describe('Content Types', () => {
|
||||||
it('should handle text content in tool responses', async () => {
|
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).toHaveLength(1);
|
||||||
expect((response as any).content[0]).toHaveProperty('type', 'text');
|
expect((response as any).content[0]).toHaveProperty('type', 'text');
|
||||||
@@ -167,14 +167,15 @@ describe('MCP Protocol Compliance', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should handle JSON content properly', async () => {
|
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
|
limit: 5
|
||||||
} });
|
} });
|
||||||
|
|
||||||
expect((response as any).content).toHaveLength(1);
|
expect((response as any).content).toHaveLength(1);
|
||||||
const content = JSON.parse((response as any).content[0].text);
|
const content = JSON.parse((response as any).content[0].text);
|
||||||
expect(content).toHaveProperty('nodes');
|
expect(content).toHaveProperty('results');
|
||||||
expect(Array.isArray(content.nodes)).toBe(true);
|
expect(Array.isArray(content.results)).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -197,10 +198,10 @@ describe('MCP Protocol Compliance', () => {
|
|||||||
const results: string[] = [];
|
const results: string[] = [];
|
||||||
|
|
||||||
// Start multiple requests with different delays
|
// Start multiple requests with different delays
|
||||||
const p1 = client.callTool({ name: 'get_database_statistics', arguments: {} })
|
const p1 = client.callTool({ name: 'tools_documentation', arguments: {} })
|
||||||
.then(() => { results.push('stats'); return 'stats'; });
|
.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'; });
|
.then(() => { results.push('nodes'); return 'nodes'; });
|
||||||
|
|
||||||
const p3 = client.callTool({ name: 'search_nodes', arguments: { query: 'http' } })
|
const p3 = client.callTool({ name: 'search_nodes', arguments: { query: 'http' } })
|
||||||
@@ -232,13 +233,13 @@ describe('MCP Protocol Compliance', () => {
|
|||||||
|
|
||||||
it('should support optional parameters', async () => {
|
it('should support optional parameters', async () => {
|
||||||
// Call with minimal params
|
// 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
|
// 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,
|
limit: 10,
|
||||||
category: 'trigger',
|
mode: 'OR'
|
||||||
package: 'n8n-nodes-base'
|
|
||||||
} });
|
} });
|
||||||
|
|
||||||
expect(response1).toBeDefined();
|
expect(response1).toBeDefined();
|
||||||
@@ -255,7 +256,7 @@ describe('MCP Protocol Compliance', () => {
|
|||||||
await testClient.connect(clientTransport);
|
await testClient.connect(clientTransport);
|
||||||
|
|
||||||
// Make a request
|
// 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();
|
expect(response).toBeDefined();
|
||||||
|
|
||||||
// Close client
|
// Close client
|
||||||
@@ -263,7 +264,7 @@ describe('MCP Protocol Compliance', () => {
|
|||||||
|
|
||||||
// Further requests should fail
|
// Further requests should fail
|
||||||
try {
|
try {
|
||||||
await testClient.callTool({ name: 'get_database_statistics', arguments: {} });
|
await testClient.callTool({ name: 'tools_documentation', arguments: {} });
|
||||||
expect.fail('Should have thrown an error');
|
expect.fail('Should have thrown an error');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
expect(error).toBeDefined();
|
expect(error).toBeDefined();
|
||||||
@@ -286,7 +287,7 @@ describe('MCP Protocol Compliance', () => {
|
|||||||
const testClient = new Client({ name: 'test', version: '1.0.0' }, {});
|
const testClient = new Client({ name: 'test', version: '1.0.0' }, {});
|
||||||
await testClient.connect(clientTransport);
|
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();
|
expect(response).toBeDefined();
|
||||||
|
|
||||||
await testClient.close();
|
await testClient.close();
|
||||||
|
|||||||
@@ -1068,14 +1068,14 @@ describe('handlers-n8n-manager', () => {
|
|||||||
},
|
},
|
||||||
toolsAvailability: {
|
toolsAvailability: {
|
||||||
documentationTools: {
|
documentationTools: {
|
||||||
count: 22,
|
count: 14,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
},
|
},
|
||||||
managementTools: {
|
managementTools: {
|
||||||
count: 16,
|
count: 17,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
},
|
},
|
||||||
totalAvailable: 38,
|
totalAvailable: 31,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user