feat: add n8n_validate_workflow tool (v2.6.3)

- NEW: n8n_validate_workflow tool to validate workflows from n8n instance by ID
- Fetches workflow via API and runs comprehensive validation
- Uses existing WorkflowValidator for consistency
- Supports all validation profiles and options
- Updated start_here tool with new workflow lifecycle info
- Updated README with new tool documentation
- Updated Claude Project Setup instructions

This completes the workflow lifecycle management:
discover → build → validate → deploy → execute → monitor

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
czlonkowski
2025-06-26 16:07:46 +02:00
parent bcfdc9627d
commit 34b5ff5d35
7 changed files with 295 additions and 7 deletions

View File

@@ -20,6 +20,9 @@ import {
} from '../utils/n8n-errors';
import { logger } from '../utils/logger';
import { z } from 'zod';
import { WorkflowValidator } from '../services/workflow-validator';
import { EnhancedConfigValidator } from '../services/enhanced-config-validator';
import { NodeRepository } from '../database/node-repository';
// Singleton n8n API client instance
let apiClient: N8nApiClient | null = null;
@@ -80,6 +83,16 @@ const listWorkflowsSchema = z.object({
excludePinnedData: z.boolean().optional(),
});
const validateWorkflowSchema = z.object({
id: z.string(),
options: z.object({
validateNodes: z.boolean().optional(),
validateConnections: z.boolean().optional(),
validateExpressions: z.boolean().optional(),
profile: z.enum(['minimal', 'runtime', 'ai-friendly', 'strict']).optional(),
}).optional(),
});
const triggerWebhookSchema = z.object({
webhookUrl: z.string().url(),
httpMethod: z.enum(['GET', 'POST', 'PUT', 'DELETE']).optional(),
@@ -473,6 +486,94 @@ export async function handleListWorkflows(args: unknown): Promise<McpToolRespons
}
}
export async function handleValidateWorkflow(
args: unknown,
repository: NodeRepository
): Promise<McpToolResponse> {
try {
const client = ensureApiConfigured();
const input = validateWorkflowSchema.parse(args);
// First, fetch the workflow from n8n
const workflowResponse = await handleGetWorkflow({ id: input.id });
if (!workflowResponse.success) {
return workflowResponse; // Return the error from fetching
}
const workflow = workflowResponse.data as Workflow;
// Create validator instance using the provided repository
const validator = new WorkflowValidator(repository, EnhancedConfigValidator);
// Run validation
const validationResult = await validator.validateWorkflow(workflow, input.options);
// Format the response (same format as the regular validate_workflow tool)
const response: any = {
valid: validationResult.valid,
workflowId: workflow.id,
workflowName: workflow.name,
summary: {
totalNodes: validationResult.statistics.totalNodes,
enabledNodes: validationResult.statistics.enabledNodes,
triggerNodes: validationResult.statistics.triggerNodes,
validConnections: validationResult.statistics.validConnections,
invalidConnections: validationResult.statistics.invalidConnections,
expressionsValidated: validationResult.statistics.expressionsValidated,
errorCount: validationResult.errors.length,
warningCount: validationResult.warnings.length
}
};
if (validationResult.errors.length > 0) {
response.errors = validationResult.errors.map(e => ({
node: e.nodeName || 'workflow',
message: e.message,
details: e.details
}));
}
if (validationResult.warnings.length > 0) {
response.warnings = validationResult.warnings.map(w => ({
node: w.nodeName || 'workflow',
message: w.message,
details: w.details
}));
}
if (validationResult.suggestions.length > 0) {
response.suggestions = validationResult.suggestions;
}
return {
success: true,
data: response
};
} catch (error) {
if (error instanceof z.ZodError) {
return {
success: false,
error: 'Invalid input',
details: { errors: error.errors }
};
}
if (error instanceof N8nApiError) {
return {
success: false,
error: getUserFriendlyErrorMessage(error),
code: error.code
};
}
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error occurred'
};
}
}
// Execution Management Handlers
export async function handleTriggerWebhookWorkflow(args: unknown): Promise<McpToolResponse> {
@@ -688,7 +789,8 @@ export async function handleListAvailableTools(): Promise<McpToolResponse> {
{ name: 'n8n_get_workflow_minimal', description: 'Get minimal workflow info' },
{ name: 'n8n_update_workflow', description: 'Update existing workflows' },
{ name: 'n8n_delete_workflow', description: 'Delete workflows' },
{ name: 'n8n_list_workflows', description: 'List workflows with filters' }
{ name: 'n8n_list_workflows', description: 'List workflows with filters' },
{ name: 'n8n_validate_workflow', description: 'Validate workflow from n8n instance' }
]
},
{

View File

@@ -242,6 +242,10 @@ export class N8NDocumentationMCPServer {
return n8nHandlers.handleDeleteWorkflow(args);
case 'n8n_list_workflows':
return n8nHandlers.handleListWorkflows(args);
case 'n8n_validate_workflow':
await this.ensureInitialized();
if (!this.repository) throw new Error('Repository not initialized');
return n8nHandlers.handleValidateWorkflow(args, this.repository);
case 'n8n_trigger_webhook_workflow':
return n8nHandlers.handleTriggerWebhookWorkflow(args);
case 'n8n_get_execution':
@@ -1215,8 +1219,9 @@ Full documentation is being prepared. For now, use get_node_essentials for confi
purpose: "Configure nodes with the right settings"
},
"3. Validate": {
tools: ["validate_node_minimal", "validate_node_operation", "validate_workflow"],
purpose: "Ensure your workflow is correct before deployment"
tools: ["validate_node_minimal", "validate_node_operation", "validate_workflow", "n8n_validate_workflow"],
purpose: "Ensure your workflow is correct before deployment",
new: "n8n_validate_workflow - Validate workflows already in n8n by ID"
},
"4. Deploy": {
tools: ["n8n_create_workflow", "n8n_update_workflow", "n8n_list_workflows"],
@@ -1234,7 +1239,8 @@ Full documentation is being prepared. For now, use get_node_essentials for confi
"2. get_node_essentials('nodes-base.slack') - Get configuration",
"3. validate_node_operation() - Validate settings",
"4. n8n_create_workflow() - Deploy to n8n",
"5. n8n_trigger_webhook_workflow() - Execute via webhook"
"5. n8n_validate_workflow({id: 'workflow-id'}) - Validate deployed workflow",
"6. n8n_trigger_webhook_workflow() - Execute via webhook"
]
}
}

View File

@@ -202,6 +202,43 @@ export const n8nManagementTools: ToolDefinition[] = [
}
}
},
{
name: 'n8n_validate_workflow',
description: `Validate a workflow from n8n instance by ID. Fetches the workflow and runs comprehensive validation including node configurations, connections, and expressions. Returns detailed validation report with errors, warnings, and suggestions.`,
inputSchema: {
type: 'object',
properties: {
id: {
type: 'string',
description: 'Workflow ID to validate'
},
options: {
type: 'object',
description: 'Validation options',
properties: {
validateNodes: {
type: 'boolean',
description: 'Validate node configurations (default: true)'
},
validateConnections: {
type: 'boolean',
description: 'Validate workflow connections (default: true)'
},
validateExpressions: {
type: 'boolean',
description: 'Validate n8n expressions (default: true)'
},
profile: {
type: 'string',
enum: ['minimal', 'runtime', 'ai-friendly', 'strict'],
description: 'Validation profile to use (default: runtime)'
}
}
}
},
required: ['id']
}
},
// Execution Management Tools
{