mirror of
https://github.com/czlonkowski/n8n-mcp.git
synced 2026-01-29 22:12:05 +00:00
* 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>
150 lines
3.7 KiB
TypeScript
150 lines
3.7 KiB
TypeScript
/**
|
|
* Base trigger handler - abstract class for all trigger handlers
|
|
*/
|
|
|
|
import { z } from 'zod';
|
|
import { Workflow } from '../../types/n8n-api';
|
|
import { InstanceContext } from '../../types/instance-context';
|
|
import { N8nApiClient } from '../../services/n8n-api-client';
|
|
import { getN8nApiConfig } from '../../config/n8n-api';
|
|
import {
|
|
TriggerType,
|
|
TriggerResponse,
|
|
TriggerHandlerCapabilities,
|
|
DetectedTrigger,
|
|
BaseTriggerInput,
|
|
} from '../types';
|
|
|
|
/**
|
|
* Constructor type for trigger handlers
|
|
*/
|
|
export type TriggerHandlerConstructor = new (
|
|
client: N8nApiClient,
|
|
context?: InstanceContext
|
|
) => BaseTriggerHandler;
|
|
|
|
/**
|
|
* Abstract base class for all trigger handlers
|
|
*
|
|
* Each handler implements:
|
|
* - Input validation via Zod schema
|
|
* - Capability declaration (active workflow required, etc.)
|
|
* - Execution logic specific to the trigger type
|
|
*/
|
|
export abstract class BaseTriggerHandler<T extends BaseTriggerInput = BaseTriggerInput> {
|
|
protected client: N8nApiClient;
|
|
protected context?: InstanceContext;
|
|
|
|
/** The trigger type this handler supports */
|
|
abstract readonly triggerType: TriggerType;
|
|
|
|
/** Handler capabilities */
|
|
abstract readonly capabilities: TriggerHandlerCapabilities;
|
|
|
|
/** Zod schema for input validation */
|
|
abstract readonly inputSchema: z.ZodSchema<T>;
|
|
|
|
constructor(client: N8nApiClient, context?: InstanceContext) {
|
|
this.client = client;
|
|
this.context = context;
|
|
}
|
|
|
|
/**
|
|
* Validate input against schema
|
|
* @throws ZodError if validation fails
|
|
*/
|
|
validate(input: unknown): T {
|
|
return this.inputSchema.parse(input);
|
|
}
|
|
|
|
/**
|
|
* Execute the trigger
|
|
*
|
|
* @param input - Validated trigger input
|
|
* @param workflow - The workflow being triggered
|
|
* @param triggerInfo - Detected trigger information (may be undefined for 'execute' type)
|
|
*/
|
|
abstract execute(
|
|
input: T,
|
|
workflow: Workflow,
|
|
triggerInfo?: DetectedTrigger
|
|
): Promise<TriggerResponse>;
|
|
|
|
/**
|
|
* Get the n8n instance base URL from context or environment config
|
|
*/
|
|
protected getBaseUrl(): string | undefined {
|
|
// First try context (for multi-tenant scenarios)
|
|
if (this.context?.n8nApiUrl) {
|
|
return this.context.n8nApiUrl.replace(/\/api\/v1\/?$/, '');
|
|
}
|
|
// Fallback to environment config
|
|
const config = getN8nApiConfig();
|
|
if (config?.baseUrl) {
|
|
return config.baseUrl.replace(/\/api\/v1\/?$/, '');
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
/**
|
|
* Get the n8n API key from context or environment config
|
|
*/
|
|
protected getApiKey(): string | undefined {
|
|
// First try context (for multi-tenant scenarios)
|
|
if (this.context?.n8nApiKey) {
|
|
return this.context.n8nApiKey;
|
|
}
|
|
// Fallback to environment config
|
|
const config = getN8nApiConfig();
|
|
return config?.apiKey;
|
|
}
|
|
|
|
/**
|
|
* Normalize response to unified format
|
|
*/
|
|
protected normalizeResponse(
|
|
result: unknown,
|
|
input: T,
|
|
startTime: number,
|
|
extra?: Partial<TriggerResponse>
|
|
): TriggerResponse {
|
|
const endTime = Date.now();
|
|
const duration = endTime - startTime;
|
|
|
|
return {
|
|
success: true,
|
|
triggerType: this.triggerType,
|
|
workflowId: input.workflowId,
|
|
data: result,
|
|
metadata: {
|
|
duration,
|
|
},
|
|
...extra,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Create error response
|
|
*/
|
|
protected errorResponse(
|
|
input: BaseTriggerInput,
|
|
error: string,
|
|
startTime: number,
|
|
extra?: Partial<TriggerResponse>
|
|
): TriggerResponse {
|
|
const endTime = Date.now();
|
|
const duration = endTime - startTime;
|
|
|
|
return {
|
|
success: false,
|
|
triggerType: this.triggerType,
|
|
workflowId: input.workflowId,
|
|
error,
|
|
metadata: {
|
|
duration,
|
|
},
|
|
...extra,
|
|
};
|
|
}
|
|
}
|