mirror of
https://github.com/czlonkowski/n8n-mcp.git
synced 2026-02-06 05:23:08 +00:00
Fixes critical issue where validation system generated warnings about properties the user never configured. System was treating default values as user-provided configuration, resulting in overwhelming false positives. BEFORE: - HTTP Request (2 properties) → 29 warnings (96% false positives) - Webhook (1 property) → 6 warnings (83% false positives) - Signal-to-noise ratio: 3% AFTER: - HTTP Request (2 properties) → 1 warning (96.5% reduction) - Webhook (1 property) → 1 warning (83% reduction) - Signal-to-noise ratio: >90% Changes: - Track user-provided keys separately from defaults - Filter UI-only properties (notice, callout, infoBox) - Improve warning messages with visibility requirements - Enhance profile-aware filtering Files modified: - src/services/config-validator.ts: Add user key tracking, UI filtering - src/services/enhanced-config-validator.ts: Extract user keys, enhance profiles - src/mcp-tools-engine.ts: Pass user keys to validator - CHANGELOG.md: Document v2.18.0 release - package.json: Bump version to 2.18.0 Verified with extensive testing via n8n-mcp-tester agent. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
113 lines
3.7 KiB
TypeScript
113 lines
3.7 KiB
TypeScript
/**
|
|
* MCPEngine - A simplified interface for benchmarking MCP tool execution
|
|
* This directly implements the MCP tool functionality without server dependencies
|
|
*/
|
|
import { NodeRepository } from './database/node-repository';
|
|
import { PropertyFilter } from './services/property-filter';
|
|
import { TaskTemplates } from './services/task-templates';
|
|
import { ConfigValidator } from './services/config-validator';
|
|
import { EnhancedConfigValidator } from './services/enhanced-config-validator';
|
|
import { WorkflowValidator, WorkflowValidationResult } from './services/workflow-validator';
|
|
|
|
export class MCPEngine {
|
|
private workflowValidator: WorkflowValidator;
|
|
|
|
constructor(private repository: NodeRepository) {
|
|
this.workflowValidator = new WorkflowValidator(repository, EnhancedConfigValidator);
|
|
}
|
|
|
|
async listNodes(args: any = {}) {
|
|
return this.repository.getAllNodes(args.limit);
|
|
}
|
|
|
|
async searchNodes(args: any) {
|
|
return this.repository.searchNodes(args.query, args.mode || 'OR', args.limit || 20);
|
|
}
|
|
|
|
async getNodeInfo(args: any) {
|
|
return this.repository.getNodeByType(args.nodeType);
|
|
}
|
|
|
|
async getNodeEssentials(args: any) {
|
|
const node = await this.repository.getNodeByType(args.nodeType);
|
|
if (!node) return null;
|
|
|
|
// Filter to essentials using static method
|
|
const essentials = PropertyFilter.getEssentials(node.properties || [], args.nodeType);
|
|
return {
|
|
nodeType: node.nodeType,
|
|
displayName: node.displayName,
|
|
description: node.description,
|
|
category: node.category,
|
|
required: essentials.required,
|
|
common: essentials.common
|
|
};
|
|
}
|
|
|
|
async getNodeDocumentation(args: any) {
|
|
const node = await this.repository.getNodeByType(args.nodeType);
|
|
return node?.documentation || null;
|
|
}
|
|
|
|
async validateNodeOperation(args: any) {
|
|
// Get node properties and validate
|
|
const node = await this.repository.getNodeByType(args.nodeType);
|
|
if (!node) {
|
|
return {
|
|
valid: false,
|
|
errors: [{ type: 'invalid_configuration', property: '', message: 'Node type not found' }],
|
|
warnings: [],
|
|
suggestions: [],
|
|
visibleProperties: [],
|
|
hiddenProperties: []
|
|
};
|
|
}
|
|
|
|
// CRITICAL FIX: Extract user-provided keys before validation
|
|
// This prevents false warnings about default values
|
|
const userProvidedKeys = new Set(Object.keys(args.config || {}));
|
|
|
|
return ConfigValidator.validate(args.nodeType, args.config, node.properties || [], userProvidedKeys);
|
|
}
|
|
|
|
async validateNodeMinimal(args: any) {
|
|
// Get node and check minimal requirements
|
|
const node = await this.repository.getNodeByType(args.nodeType);
|
|
if (!node) {
|
|
return { missingFields: [], error: 'Node type not found' };
|
|
}
|
|
|
|
const missingFields: string[] = [];
|
|
const requiredFields = PropertyFilter.getEssentials(node.properties || [], args.nodeType).required;
|
|
|
|
for (const field of requiredFields) {
|
|
if (!args.config[field.name]) {
|
|
missingFields.push(field.name);
|
|
}
|
|
}
|
|
|
|
return { missingFields };
|
|
}
|
|
|
|
async searchNodeProperties(args: any) {
|
|
return this.repository.searchNodeProperties(args.nodeType, args.query, args.maxResults || 20);
|
|
}
|
|
|
|
async listAITools(args: any) {
|
|
return this.repository.getAIToolNodes();
|
|
}
|
|
|
|
async getDatabaseStatistics(args: any) {
|
|
const count = await this.repository.getNodeCount();
|
|
const aiTools = await this.repository.getAIToolNodes();
|
|
return {
|
|
totalNodes: count,
|
|
aiToolsCount: aiTools.length,
|
|
categories: ['trigger', 'transform', 'output', 'input']
|
|
};
|
|
}
|
|
|
|
async validateWorkflow(args: any): Promise<WorkflowValidationResult> {
|
|
return this.workflowValidator.validateWorkflow(args.workflow, args.options);
|
|
}
|
|
} |