feat: rename n8n_trigger_webhook_workflow to n8n_test_workflow with multi-trigger support (#460)

* feat: rename n8n_trigger_webhook_workflow to n8n_test_workflow with multi-trigger support

- Rename tool from n8n_trigger_webhook_workflow to n8n_test_workflow
- Add support for webhook, form, and chat triggers (auto-detection)
- Implement modular trigger system with registry pattern
- Add trigger detector for automatic trigger type inference
- Remove execute trigger type (n8n public API limitation)
- Add comprehensive tests for trigger detection and handlers

The tool now auto-detects trigger type from workflow structure and
supports all externally-triggerable workflows via n8n's public API.

Note: Direct workflow execution (Schedule/Manual triggers) requires
n8n's instance-level MCP access, not available via REST API.

Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en

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

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: add SSRF protection to webhook handler and update tests

- Add SSRF URL validation to webhook-handler.ts (critical security fix)
  Aligns with existing SSRF protection in form-handler.ts and chat-handler.ts
- Update parameter-validation.test.ts to use new n8n_test_workflow tool name

Conceived by Romuald Członkowski - www.aiadvisors.pl/en

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

Co-Authored-By: Claude <noreply@anthropic.com>

* feat: n8n_test_workflow unified trigger tool (v2.28.0)

Added new `n8n_test_workflow` tool replacing `n8n_trigger_webhook_workflow`:

Features:
- Auto-detects trigger type (webhook/form/chat) from workflow
- Supports multiple trigger types with type-specific parameters
- SSRF protection for all trigger handlers
- Extensible handler architecture with registry pattern

Changes:
- Fixed Zod schema to remove invalid 'execute' trigger type
- Updated README.md tool documentation
- Added CHANGELOG entry for v2.28.0
- Bumped version to 2.28.0

Conceived by Romuald Członkowski - www.aiadvisors.pl/en

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

Co-Authored-By: Claude <noreply@anthropic.com>

* test: add comprehensive unit tests for trigger handlers

Added 87 unit tests across 4 test files to improve code coverage:

- base-handler.test.ts (19 tests) - 100% coverage
- webhook-handler.test.ts (22 tests) - 100% coverage
- chat-handler.test.ts (23 tests) - 100% coverage
- form-handler.test.ts (23 tests) - 100% coverage

Tests cover:
- Input validation and parameter handling
- SSRF protection integration
- HTTP method handling and URL building
- Error response formatting
- Execution paths for all trigger types

Conceived by Romuald Członkowski - www.aiadvisors.pl/en

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

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Romuald Członkowski
2025-12-01 15:55:14 +01:00
committed by GitHub
parent ddf9556759
commit 33690c5650
30 changed files with 4131 additions and 197 deletions

View File

@@ -10,6 +10,7 @@ import {
ExecutionFilterOptions,
ExecutionMode
} from '../types/n8n-api';
import type { TriggerType, TestWorkflowInput } from '../triggers/types';
import {
validateWorkflowStructure,
hasWebhookTrigger,
@@ -429,11 +430,17 @@ const autofixWorkflowSchema = z.object({
maxFixes: z.number().optional().default(50)
});
const triggerWebhookSchema = z.object({
webhookUrl: z.string().url(),
// Schema for n8n_test_workflow tool
const testWorkflowSchema = z.object({
workflowId: z.string(),
triggerType: z.enum(['webhook', 'form', 'chat']).optional(),
httpMethod: z.enum(['GET', 'POST', 'PUT', 'DELETE']).optional(),
webhookPath: z.string().optional(),
message: z.string().optional(),
sessionId: z.string().optional(),
data: z.record(z.unknown()).optional(),
headers: z.record(z.string()).optional(),
timeout: z.number().optional(),
waitForResponse: z.boolean().optional(),
});
@@ -1235,74 +1242,160 @@ export async function handleAutofixWorkflow(
// Execution Management Handlers
export async function handleTriggerWebhookWorkflow(args: unknown, context?: InstanceContext): Promise<McpToolResponse> {
/**
* Handler for n8n_test_workflow tool
* Triggers workflow execution via auto-detected or specified trigger type
*/
export async function handleTestWorkflow(args: unknown, context?: InstanceContext): Promise<McpToolResponse> {
try {
const client = ensureApiConfigured(context);
const input = triggerWebhookSchema.parse(args);
const input = testWorkflowSchema.parse(args);
const webhookRequest: WebhookRequest = {
webhookUrl: input.webhookUrl,
httpMethod: input.httpMethod || 'POST',
// Import trigger system (lazy to avoid circular deps)
const {
detectTriggerFromWorkflow,
ensureRegistryInitialized,
TriggerRegistry,
} = await import('../triggers');
// Ensure registry is initialized
await ensureRegistryInitialized();
// Fetch the workflow to analyze its trigger
const workflow = await client.getWorkflow(input.workflowId);
// Determine trigger type
let triggerType: TriggerType | undefined = input.triggerType as TriggerType | undefined;
let triggerInfo;
// Auto-detect from workflow
const detection = detectTriggerFromWorkflow(workflow);
if (!triggerType) {
if (detection.detected && detection.trigger) {
triggerType = detection.trigger.type;
triggerInfo = detection.trigger;
} else {
// No externally-triggerable trigger found
return {
success: false,
error: 'Workflow cannot be triggered externally',
details: {
workflowId: input.workflowId,
reason: detection.reason,
hint: 'Only workflows with webhook, form, or chat triggers can be executed via the API. Add one of these trigger nodes to your workflow.',
},
};
}
} else {
// User specified a trigger type, verify it matches workflow
if (detection.detected && detection.trigger?.type === triggerType) {
triggerInfo = detection.trigger;
} else if (!detection.detected || detection.trigger?.type !== triggerType) {
return {
success: false,
error: `Workflow does not have a ${triggerType} trigger`,
details: {
workflowId: input.workflowId,
requestedTrigger: triggerType,
detectedTrigger: detection.trigger?.type || 'none',
hint: detection.detected
? `Workflow has a ${detection.trigger?.type} trigger. Either use that type or omit triggerType for auto-detection.`
: 'Workflow has no externally-triggerable triggers (webhook, form, or chat).',
},
};
}
}
// Get handler for trigger type
const handler = TriggerRegistry.getHandler(triggerType, client, context);
if (!handler) {
return {
success: false,
error: `No handler registered for trigger type: ${triggerType}`,
details: {
supportedTypes: TriggerRegistry.getRegisteredTypes(),
},
};
}
// Check if workflow is active (if required by handler)
if (handler.capabilities.requiresActiveWorkflow && !workflow.active) {
return {
success: false,
error: 'Workflow must be active to trigger via this method',
details: {
workflowId: input.workflowId,
triggerType,
hint: 'Activate the workflow in n8n using n8n_update_partial_workflow with [{type: "activateWorkflow"}]',
},
};
}
// Validate chat trigger has message
if (triggerType === 'chat' && !input.message) {
return {
success: false,
error: 'Chat trigger requires a message parameter',
details: {
hint: 'Provide message="your message" for chat triggers',
},
};
}
// Build trigger-specific input
const triggerInput = {
workflowId: input.workflowId,
triggerType,
httpMethod: input.httpMethod,
webhookPath: input.webhookPath,
message: input.message || '',
sessionId: input.sessionId,
data: input.data,
formData: input.data, // For form triggers
headers: input.headers,
waitForResponse: input.waitForResponse ?? true
timeout: input.timeout,
waitForResponse: input.waitForResponse,
};
const response = await client.triggerWebhook(webhookRequest);
// Execute the trigger
const response = await handler.execute(triggerInput as any, workflow, triggerInfo);
return {
success: true,
data: response,
message: 'Webhook triggered successfully'
success: response.success,
data: response.data,
message: response.success
? `Workflow triggered successfully via ${triggerType}`
: response.error,
executionId: response.executionId,
workflowId: input.workflowId,
details: {
triggerType,
metadata: response.metadata,
...(response.details || {}),
},
};
} catch (error) {
if (error instanceof z.ZodError) {
return {
success: false,
error: 'Invalid input',
details: { errors: error.errors }
details: { errors: error.errors },
};
}
if (error instanceof N8nApiError) {
// Try to extract execution context from error response
const errorData = error.details as any;
const executionId = errorData?.executionId || errorData?.id || errorData?.execution?.id;
const workflowId = errorData?.workflowId || errorData?.workflow?.id;
// If we have execution ID, provide specific guidance with n8n_get_execution
if (executionId) {
return {
success: false,
error: formatExecutionError(executionId, workflowId),
code: error.code,
executionId,
workflowId: workflowId || undefined
};
}
// No execution ID available - workflow likely didn't start
// Provide guidance to check recent executions
if (error.code === 'SERVER_ERROR' || error.statusCode && error.statusCode >= 500) {
return {
success: false,
error: formatNoExecutionError(),
code: error.code
};
}
// For other errors (auth, validation, etc), use standard message
return {
success: false,
error: getUserFriendlyErrorMessage(error),
code: error.code,
details: error.details as Record<string, unknown> | undefined
details: error.details as Record<string, unknown> | undefined,
};
}
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error occurred'
error: error instanceof Error ? error.message : 'Unknown error occurred',
};
}
}
@@ -2449,3 +2542,84 @@ export async function handleDeployTemplate(
};
}
}
/**
* Backward-compatible webhook trigger handler
*
* @deprecated Use handleTestWorkflow instead. This function is kept for
* backward compatibility with existing integration tests.
*/
export async function handleTriggerWebhookWorkflow(args: unknown, context?: InstanceContext): Promise<McpToolResponse> {
const triggerWebhookSchema = z.object({
webhookUrl: z.string().url(),
httpMethod: z.enum(['GET', 'POST', 'PUT', 'DELETE']).optional(),
data: z.record(z.unknown()).optional(),
headers: z.record(z.string()).optional(),
waitForResponse: z.boolean().optional(),
});
try {
const client = ensureApiConfigured(context);
const input = triggerWebhookSchema.parse(args);
const webhookRequest: WebhookRequest = {
webhookUrl: input.webhookUrl,
httpMethod: input.httpMethod || 'POST',
data: input.data,
headers: input.headers,
waitForResponse: input.waitForResponse ?? true
};
const response = await client.triggerWebhook(webhookRequest);
return {
success: true,
data: response,
message: 'Webhook triggered successfully'
};
} catch (error) {
if (error instanceof z.ZodError) {
return {
success: false,
error: 'Invalid input',
details: { errors: error.errors }
};
}
if (error instanceof N8nApiError) {
const errorData = error.details as any;
const executionId = errorData?.executionId || errorData?.id || errorData?.execution?.id;
const workflowId = errorData?.workflowId || errorData?.workflow?.id;
if (executionId) {
return {
success: false,
error: formatExecutionError(executionId, workflowId),
code: error.code,
executionId,
workflowId: workflowId || undefined
};
}
if (error.code === 'SERVER_ERROR' || error.statusCode && error.statusCode >= 500) {
return {
success: false,
error: formatNoExecutionError(),
code: error.code
};
}
return {
success: false,
error: getUserFriendlyErrorMessage(error),
code: error.code,
details: error.details as Record<string, unknown> | undefined
};
}
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error occurred'
};
}
}

View File

@@ -1176,9 +1176,9 @@ export class N8NDocumentationMCPServer {
await this.ensureInitialized();
if (!this.repository) throw new Error('Repository not initialized');
return n8nHandlers.handleAutofixWorkflow(args, this.repository, this.instanceContext);
case 'n8n_trigger_webhook_workflow':
this.validateToolParams(name, args, ['webhookUrl']);
return n8nHandlers.handleTriggerWebhookWorkflow(args, this.instanceContext);
case 'n8n_test_workflow':
this.validateToolParams(name, args, ['workflowId']);
return n8nHandlers.handleTestWorkflow(args, this.instanceContext);
case 'n8n_executions': {
this.validateToolParams(name, args, ['action']);
const execAction = args.action;

View File

@@ -19,7 +19,7 @@ import {
n8nListWorkflowsDoc,
n8nValidateWorkflowDoc,
n8nAutofixWorkflowDoc,
n8nTriggerWebhookWorkflowDoc,
n8nTestWorkflowDoc,
n8nExecutionsDoc,
n8nWorkflowVersionsDoc,
n8nDeployTemplateDoc
@@ -57,7 +57,7 @@ export const toolsDocumentation: Record<string, ToolDocumentation> = {
n8n_list_workflows: n8nListWorkflowsDoc,
n8n_validate_workflow: n8nValidateWorkflowDoc,
n8n_autofix_workflow: n8nAutofixWorkflowDoc,
n8n_trigger_webhook_workflow: n8nTriggerWebhookWorkflowDoc,
n8n_test_workflow: n8nTestWorkflowDoc,
n8n_executions: n8nExecutionsDoc,
n8n_workflow_versions: n8nWorkflowVersionsDoc,
n8n_deploy_template: n8nDeployTemplateDoc

View File

@@ -6,7 +6,7 @@ export { n8nDeleteWorkflowDoc } from './n8n-delete-workflow';
export { n8nListWorkflowsDoc } from './n8n-list-workflows';
export { n8nValidateWorkflowDoc } from './n8n-validate-workflow';
export { n8nAutofixWorkflowDoc } from './n8n-autofix-workflow';
export { n8nTriggerWebhookWorkflowDoc } from './n8n-trigger-webhook-workflow';
export { n8nTestWorkflowDoc } from './n8n-test-workflow';
export { n8nExecutionsDoc } from './n8n-executions';
export { n8nWorkflowVersionsDoc } from './n8n-workflow-versions';
export { n8nDeployTemplateDoc } from './n8n-deploy-template';

View File

@@ -84,7 +84,7 @@ n8n_create_workflow({
'Validate with validate_workflow first',
'Use unique node IDs',
'Position nodes for readability',
'Test with n8n_trigger_webhook_workflow'
'Test with n8n_test_workflow'
],
pitfalls: [
'**REQUIRES N8N_API_URL and N8N_API_KEY environment variables** - tool unavailable without n8n API access',
@@ -95,6 +95,6 @@ n8n_create_workflow({
'**Auto-sanitization runs on creation**: All nodes sanitized before workflow created (operator structures fixed, missing metadata added)',
'**Auto-sanitization cannot prevent all failures**: Broken connections or invalid node configurations may still cause creation to fail'
],
relatedTools: ['validate_workflow', 'n8n_update_partial_workflow', 'n8n_trigger_webhook_workflow']
relatedTools: ['validate_workflow', 'n8n_update_partial_workflow', 'n8n_test_workflow']
}
};

View File

@@ -77,6 +77,6 @@ export const n8nExecutionsDoc: ToolDocumentation = {
'Execution must exist or returns 404',
'Delete is permanent - cannot undo'
],
relatedTools: ['n8n_get_workflow', 'n8n_trigger_webhook_workflow', 'n8n_validate_workflow']
relatedTools: ['n8n_get_workflow', 'n8n_test_workflow', 'n8n_validate_workflow']
}
};

View File

@@ -0,0 +1,138 @@
import { ToolDocumentation } from '../types';
export const n8nTestWorkflowDoc: ToolDocumentation = {
name: 'n8n_test_workflow',
category: 'workflow_management',
essentials: {
description: 'Test/trigger workflow execution. Auto-detects trigger type (webhook/form/chat). Only workflows with these triggers can be executed externally.',
keyParameters: ['workflowId', 'triggerType', 'data', 'message'],
example: 'n8n_test_workflow({workflowId: "123"}) - auto-detect trigger',
performance: 'Immediate trigger, response time depends on workflow complexity',
tips: [
'Auto-detects trigger type from workflow if not specified',
'Workflow must have a webhook, form, or chat trigger to be executable',
'For chat triggers, message is required',
'All trigger types require the workflow to be ACTIVE'
]
},
full: {
description: `Test and trigger n8n workflows through HTTP-based methods. This unified tool supports multiple trigger types:
**Trigger Types:**
- **webhook**: HTTP-based triggers (GET/POST/PUT/DELETE)
- **form**: Form submission triggers
- **chat**: AI chat triggers with conversation support
**Important:** n8n's public API does not support direct workflow execution. Only workflows with webhook, form, or chat triggers can be executed externally. Workflows with schedule, manual, or other trigger types cannot be triggered via this API.
The tool auto-detects the appropriate trigger type by analyzing the workflow's trigger node. You can override this with the triggerType parameter.`,
parameters: {
workflowId: {
type: 'string',
required: true,
description: 'Workflow ID to execute'
},
triggerType: {
type: 'string',
required: false,
enum: ['webhook', 'form', 'chat'],
description: 'Trigger type. Auto-detected if not specified. Workflow must have matching trigger node.'
},
httpMethod: {
type: 'string',
required: false,
enum: ['GET', 'POST', 'PUT', 'DELETE'],
description: 'For webhook: HTTP method (default: from workflow config or POST)'
},
webhookPath: {
type: 'string',
required: false,
description: 'For webhook: override the webhook path'
},
message: {
type: 'string',
required: false,
description: 'For chat: message to send (required for chat triggers)'
},
sessionId: {
type: 'string',
required: false,
description: 'For chat: session ID for conversation continuity'
},
data: {
type: 'object',
required: false,
description: 'Input data/payload for webhook or form fields'
},
headers: {
type: 'object',
required: false,
description: 'Custom HTTP headers'
},
timeout: {
type: 'number',
required: false,
description: 'Timeout in ms (default: 120000)'
},
waitForResponse: {
type: 'boolean',
required: false,
description: 'Wait for workflow completion (default: true)'
}
},
returns: `Execution response including:
- success: boolean
- data: workflow output data
- executionId: for tracking/debugging
- triggerType: detected or specified trigger type
- metadata: timing and request details`,
examples: [
'n8n_test_workflow({workflowId: "123"}) - Auto-detect and trigger',
'n8n_test_workflow({workflowId: "123", triggerType: "webhook", data: {name: "John"}}) - Webhook with data',
'n8n_test_workflow({workflowId: "123", triggerType: "chat", message: "Hello AI"}) - Chat trigger',
'n8n_test_workflow({workflowId: "123", triggerType: "form", data: {email: "test@example.com"}}) - Form submission'
],
useCases: [
'Test workflows during development',
'Trigger AI chat workflows with messages',
'Submit form data to form-triggered workflows',
'Integrate n8n workflows with external systems via webhooks'
],
performance: `Performance varies based on workflow complexity and waitForResponse setting:
- Webhook: Immediate trigger, depends on workflow
- Form: Immediate trigger, depends on workflow
- Chat: May have additional AI processing time`,
errorHandling: `**Error Response with Execution Guidance**
When execution fails, the response includes guidance for debugging:
**With Execution ID** (workflow started but failed):
- Use n8n_executions({action: 'get', id: executionId, mode: 'preview'}) to investigate
**Without Execution ID** (workflow didn't start):
- Use n8n_executions({action: 'list', workflowId: 'wf_id'}) to find recent executions
**Common Errors:**
- "Workflow not found" - Check workflow ID exists
- "Workflow not active" - Activate workflow (required for all trigger types)
- "Workflow cannot be triggered externally" - Workflow has no webhook/form/chat trigger
- "Chat message required" - Provide message parameter for chat triggers
- "SSRF protection" - URL validation failed`,
bestPractices: [
'Let auto-detection choose the trigger type when possible',
'Ensure workflow has a webhook, form, or chat trigger before testing',
'For chat workflows, provide sessionId for multi-turn conversations',
'Use mode="preview" with n8n_executions for efficient debugging',
'Test with small data payloads first',
'Activate workflows before testing (use n8n_update_partial_workflow with activateWorkflow)'
],
pitfalls: [
'All trigger types require the workflow to be ACTIVE',
'Workflows without webhook/form/chat triggers cannot be executed externally',
'Chat trigger requires message parameter',
'Form data must match expected form fields',
'Webhook method must match node configuration'
],
relatedTools: ['n8n_executions', 'n8n_get_workflow', 'n8n_create_workflow', 'n8n_validate_workflow']
}
};

View File

@@ -1,118 +0,0 @@
import { ToolDocumentation } from '../types';
export const n8nTriggerWebhookWorkflowDoc: ToolDocumentation = {
name: 'n8n_trigger_webhook_workflow',
category: 'workflow_management',
essentials: {
description: 'Trigger workflow via webhook. Must be ACTIVE with Webhook node. Method must match config.',
keyParameters: ['webhookUrl', 'httpMethod', 'data'],
example: 'n8n_trigger_webhook_workflow({webhookUrl: "https://n8n.example.com/webhook/abc-def-ghi"})',
performance: 'Immediate trigger, response time depends on workflow complexity',
tips: [
'Workflow MUST be active and contain a Webhook node for triggering',
'HTTP method must match webhook node configuration (often GET)',
'Use waitForResponse:false for async execution without waiting'
]
},
full: {
description: `Triggers a workflow execution via its webhook URL. This is the primary method for external systems to start n8n workflows. The target workflow must be active and contain a properly configured Webhook node as the trigger. The HTTP method used must match the webhook configuration.`,
parameters: {
webhookUrl: {
type: 'string',
required: true,
description: 'Full webhook URL from n8n workflow (e.g., https://n8n.example.com/webhook/abc-def-ghi)'
},
httpMethod: {
type: 'string',
required: false,
enum: ['GET', 'POST', 'PUT', 'DELETE'],
description: 'HTTP method (must match webhook configuration, often GET). Defaults to GET if not specified'
},
data: {
type: 'object',
required: false,
description: 'Data to send with the webhook request. For GET requests, becomes query parameters'
},
headers: {
type: 'object',
required: false,
description: 'Additional HTTP headers to include in the request'
},
waitForResponse: {
type: 'boolean',
required: false,
description: 'Wait for workflow completion and return results (default: true). Set to false for fire-and-forget'
}
},
returns: `Webhook response data if waitForResponse is true, or immediate acknowledgment if false. Response format depends on webhook node configuration.`,
examples: [
'n8n_trigger_webhook_workflow({webhookUrl: "https://n8n.example.com/webhook/order-process"}) - Trigger with GET',
'n8n_trigger_webhook_workflow({webhookUrl: "https://n8n.example.com/webhook/data-import", httpMethod: "POST", data: {name: "John", email: "john@example.com"}}) - POST with data',
'n8n_trigger_webhook_workflow({webhookUrl: "https://n8n.example.com/webhook/async-job", waitForResponse: false}) - Fire and forget',
'n8n_trigger_webhook_workflow({webhookUrl: "https://n8n.example.com/webhook/api", headers: {"API-Key": "secret"}}) - With auth headers'
],
useCases: [
'Trigger data processing workflows from external applications',
'Start scheduled jobs manually via webhook',
'Integrate n8n workflows with third-party services',
'Create REST API endpoints using n8n workflows',
'Implement event-driven architectures with n8n'
],
performance: `Performance varies based on workflow complexity and waitForResponse setting. Synchronous calls (waitForResponse: true) block until workflow completes. For long-running workflows, use async mode (waitForResponse: false) and monitor execution separately.`,
errorHandling: `**Enhanced Error Messages with Execution Guidance**
When a webhook trigger fails, the error response now includes specific guidance to help debug the issue:
**Error with Execution ID** (workflow started but failed):
- Format: "Workflow {workflowId} execution {executionId} failed. Use n8n_executions({action: 'get', id: '{executionId}', mode: 'preview'}) to investigate the error."
- Response includes: executionId and workflowId fields for direct access
- Recommended action: Use n8n_executions with action='get' and mode='preview' for fast, efficient error inspection
**Error without Execution ID** (workflow didn't start):
- Format: "Workflow failed to execute. Use n8n_executions({action: 'list'}) to find recent executions, then n8n_executions({action: 'get', mode: 'preview'}) to investigate."
- Recommended action: Check recent executions with n8n_executions({action: 'list'})
**Why mode='preview'?**
- Fast: <50ms response time
- Efficient: ~500 tokens (vs 50K+ for full mode)
- Safe: No timeout or token limit risks
- Informative: Shows structure, counts, and error details
- Provides recommendations for fetching more data if needed
**Example Error Responses**:
\`\`\`json
{
"success": false,
"error": "Workflow wf_123 execution exec_456 failed. Use n8n_get_execution({id: 'exec_456', mode: 'preview'}) to investigate the error.",
"executionId": "exec_456",
"workflowId": "wf_123",
"code": "SERVER_ERROR"
}
\`\`\`
**Investigation Workflow**:
1. Trigger returns error with execution ID
2. Call n8n_executions({action: 'get', id: executionId, mode: 'preview'}) to see structure and error
3. Based on preview recommendation, fetch more data if needed
4. Fix issues in workflow and retry`,
bestPractices: [
'Always verify workflow is active before attempting webhook triggers',
'Match HTTP method exactly with webhook node configuration',
'Use async mode (waitForResponse: false) for long-running workflows',
'Include authentication headers when webhook requires them',
'Test webhook URL manually first to ensure it works',
'When errors occur, use n8n_executions with action="get" and mode="preview" first for efficient debugging',
'Store execution IDs from error responses for later investigation'
],
pitfalls: [
'Workflow must be ACTIVE - inactive workflows cannot be triggered',
'HTTP method mismatch returns 404 even if URL is correct',
'Webhook node must be the trigger node in the workflow',
'Timeout errors occur with long workflows in sync mode',
'Data format must match webhook node expectations',
'Error messages always include n8n_executions guidance - follow the suggested steps for efficient debugging',
'Execution IDs in error responses are crucial for debugging - always check for and use them'
],
relatedTools: ['n8n_executions', 'n8n_get_workflow', 'n8n_create_workflow']
}
};

View File

@@ -135,7 +135,7 @@ When working with Code nodes, always start by calling the relevant guide:
- n8n_list_workflows - List workflows with filters
- n8n_validate_workflow - Validate workflow by ID
- n8n_autofix_workflow - Auto-fix common issues
- n8n_trigger_webhook_workflow - Trigger via webhook
- n8n_test_workflow - Test/trigger workflows (webhook, form, chat, execute)
- n8n_executions - Unified execution management (action='get'/'list'/'delete')
- n8n_health_check - Check n8n API connectivity
- n8n_workflow_versions - Version history and rollback

View File

@@ -276,34 +276,58 @@ export const n8nManagementTools: ToolDefinition[] = [
// Execution Management Tools
{
name: 'n8n_trigger_webhook_workflow',
description: `Trigger workflow via webhook. Must be ACTIVE with Webhook node. Method must match config.`,
name: 'n8n_test_workflow',
description: `Test/trigger workflow execution. Auto-detects trigger type (webhook/form/chat). Supports: webhook (HTTP), form (fields), chat (message). Note: Only workflows with these trigger types can be executed externally.`,
inputSchema: {
type: 'object',
properties: {
webhookUrl: {
type: 'string',
description: 'Full webhook URL from n8n workflow (e.g., https://n8n.example.com/webhook/abc-def-ghi)'
workflowId: {
type: 'string',
description: 'Workflow ID to execute (required)'
},
httpMethod: {
type: 'string',
triggerType: {
type: 'string',
enum: ['webhook', 'form', 'chat'],
description: 'Trigger type. Auto-detected if not specified. Workflow must have a matching trigger node.'
},
// Webhook options
httpMethod: {
type: 'string',
enum: ['GET', 'POST', 'PUT', 'DELETE'],
description: 'HTTP method (must match webhook configuration, often GET)'
description: 'For webhook: HTTP method (default: from workflow config or POST)'
},
data: {
type: 'object',
description: 'Data to send with the webhook request'
webhookPath: {
type: 'string',
description: 'For webhook: override the webhook path'
},
headers: {
type: 'object',
description: 'Additional HTTP headers'
// Chat options
message: {
type: 'string',
description: 'For chat: message to send (required for chat triggers)'
},
waitForResponse: {
type: 'boolean',
description: 'Wait for workflow completion (default: true)'
sessionId: {
type: 'string',
description: 'For chat: session ID for conversation continuity'
},
// Common options
data: {
type: 'object',
description: 'Input data/payload for webhook, form fields, or execution data'
},
headers: {
type: 'object',
description: 'Custom HTTP headers'
},
timeout: {
type: 'number',
description: 'Timeout in ms (default: 120000)'
},
waitForResponse: {
type: 'boolean',
description: 'Wait for workflow completion (default: true)'
}
},
required: ['webhookUrl']
required: ['workflowId']
}
},
{