fix: remove faulty auto-generated examples from MCP tools

- Remove examples from get_node_essentials responses
- Remove examples from validate_node_operation when errors occur
- Update documentation to reflect removal of examples
- Keep helpful format hints in get_node_for_task (different purpose)

The auto-generated examples were misleading AI agents with incorrect
configurations (e.g., Slack "channel" vs "select" property). Tools
now focus on validation and error messages instead of examples.

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
czlonkowski
2025-07-17 21:17:35 +02:00
parent 24d775960b
commit ba7f8f9ea6
6 changed files with 47 additions and 146 deletions

Binary file not shown.

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "n8n-mcp", "name": "n8n-mcp",
"version": "2.7.11", "version": "2.7.17",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "n8n-mcp", "name": "n8n-mcp",
"version": "2.7.11", "version": "2.7.17",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@modelcontextprotocol/sdk": "^1.13.2", "@modelcontextprotocol/sdk": "^1.13.2",

View File

@@ -1,6 +1,6 @@
{ {
"name": "n8n-mcp", "name": "n8n-mcp",
"version": "2.7.16", "version": "2.7.17",
"description": "Integration between n8n workflow automation and Model Context Protocol (MCP)", "description": "Integration between n8n workflow automation and Model Context Protocol (MCP)",
"main": "dist/index.js", "main": "dist/index.js",
"bin": { "bin": {

View File

@@ -13,7 +13,6 @@ import { logger } from '../utils/logger';
import { NodeRepository } from '../database/node-repository'; import { NodeRepository } from '../database/node-repository';
import { DatabaseAdapter, createDatabaseAdapter } from '../database/database-adapter'; import { DatabaseAdapter, createDatabaseAdapter } from '../database/database-adapter';
import { PropertyFilter } from '../services/property-filter'; import { PropertyFilter } from '../services/property-filter';
import { ExampleGenerator } from '../services/example-generator';
import { TaskTemplates } from '../services/task-templates'; import { TaskTemplates } from '../services/task-templates';
import { ConfigValidator } from '../services/config-validator'; import { ConfigValidator } from '../services/config-validator';
import { EnhancedConfigValidator, ValidationMode, ValidationProfile } from '../services/enhanced-config-validator'; import { EnhancedConfigValidator, ValidationMode, ValidationProfile } from '../services/enhanced-config-validator';
@@ -1061,9 +1060,6 @@ Full documentation is being prepared. For now, use get_node_essentials for confi
// Get essential properties // Get essential properties
const essentials = PropertyFilter.getEssentials(allProperties, node.nodeType); const essentials = PropertyFilter.getEssentials(allProperties, node.nodeType);
// Generate examples
const examples = ExampleGenerator.getExamples(node.nodeType, essentials);
// Get operations (already parsed by repository) // Get operations (already parsed by repository)
const operations = node.operations || []; const operations = node.operations || [];
@@ -1082,7 +1078,7 @@ Full documentation is being prepared. For now, use get_node_essentials for confi
action: op.action, action: op.action,
resource: op.resource resource: op.resource
})), })),
examples, // Examples removed - use validate_node_operation for working configurations
metadata: { metadata: {
totalProperties: allProperties.length, totalProperties: allProperties.length,
isAITool: node.isAITool, isAITool: node.isAITool,

View File

@@ -4,48 +4,54 @@ export const getNodeEssentialsDoc: ToolDocumentation = {
name: 'get_node_essentials', name: 'get_node_essentials',
category: 'configuration', category: 'configuration',
essentials: { essentials: {
description: 'Returns only the most commonly-used properties for a node (10-20 fields) with working examples. Response is 95% smaller than get_node_info (5KB vs 100KB+). Essential properties include required fields, common options, and authentication settings.', description: 'Returns only the most commonly-used properties for a node (10-20 fields). Response is 95% smaller than get_node_info (5KB vs 100KB+). Essential properties include required fields, common options, and authentication settings. Use validate_node_operation for working configurations.',
keyParameters: ['nodeType'], keyParameters: ['nodeType'],
example: 'get_node_essentials({nodeType: "nodes-base.slack"})', example: 'get_node_essentials({nodeType: "nodes-base.slack"})',
performance: '<10ms, ~5KB response', performance: '<10ms, ~5KB response',
tips: [ tips: [
'Always use this before get_node_info', 'Always use this before get_node_info',
'Includes ready-to-use examples', 'Use validate_node_operation for examples',
'Perfect for configuring nodes quickly' 'Perfect for understanding node structure'
] ]
}, },
full: { full: {
description: 'Returns a curated subset of node properties focusing on the most commonly-used fields. Essential properties are hand-picked for each node type and include: required fields, primary operations, authentication options, and the most frequent configuration patterns. Each response includes working examples you can copy and modify.', description: 'Returns a curated subset of node properties focusing on the most commonly-used fields. Essential properties are hand-picked for each node type and include: required fields, primary operations, authentication options, and the most frequent configuration patterns. NOTE: Examples have been removed to avoid confusion - use validate_node_operation to get working configurations with proper validation.',
parameters: { parameters: {
nodeType: { type: 'string', description: 'Full node type with prefix, e.g., "nodes-base.slack", "nodes-base.httpRequest"', required: true } nodeType: { type: 'string', description: 'Full node type with prefix, e.g., "nodes-base.slack", "nodes-base.httpRequest"', required: true }
}, },
returns: `Object containing: returns: `Object containing:
{ {
"nodeType": "nodes-base.slack", "nodeType": "nodes-base.slack",
"essentialProperties": { "displayName": "Slack",
"resource": ["channel", "message", "user"], "description": "Consume Slack API",
"operation": ["post", "update", "delete"], "category": "output",
"authentication": ["accessToken", "oAuth2"], "version": "2.3",
"channel": "Channel ID or name", "requiredProperties": [], // Most nodes have no strictly required fields
"text": "Message content", "commonProperties": [
"blocks": "Advanced formatting (optional)" {
}, "name": "resource",
"examples": { "displayName": "Resource",
"postMessage": { "type": "options",
"resource": "message", "options": ["channel", "message", "user"],
"operation": "post", "default": "message"
"channel": "#general",
"text": "Hello from n8n!"
}, },
"withBlocks": { {
"resource": "message", "name": "operation",
"operation": "post", "displayName": "Operation",
"blocks": [{"type": "section", "text": {"type": "mrkdwn", "text": "Hello"}}] "type": "options",
} "options": ["post", "update", "delete"],
}, "default": "post"
"authentication": { },
"required": true, // ... 10-20 most common properties
"options": ["accessToken", "oAuth2"] ],
"operations": [
{"name": "Post", "description": "Post a message"},
{"name": "Update", "description": "Update a message"}
],
"metadata": {
"totalProperties": 121,
"isAITool": false,
"hasCredentials": true
} }
}`, }`,
examples: [ examples: [
@@ -55,18 +61,18 @@ export const getNodeEssentialsDoc: ToolDocumentation = {
'// Workflow: search → essentials → validate', '// Workflow: search → essentials → validate',
'const nodes = search_nodes({query: "database"});', 'const nodes = search_nodes({query: "database"});',
'const mysql = get_node_essentials({nodeType: "nodes-base.mySql"});', 'const mysql = get_node_essentials({nodeType: "nodes-base.mySql"});',
'validate_node_minimal("nodes-base.mySql", mysql.examples.select);' 'validate_node_operation("nodes-base.mySql", {operation: "select"}, "minimal");'
], ],
useCases: [ useCases: [
'Quickly configure nodes without information overload', 'Quickly understand node structure without information overload',
'Get working examples for common operations', 'Identify which properties are most important',
'Learn node basics before diving into advanced features', 'Learn node basics before diving into advanced features',
'Build workflows faster with curated property sets' 'Build workflows faster with curated property sets'
], ],
performance: '<10ms response time, ~5KB payload (vs 100KB+ for full schema)', performance: '<10ms response time, ~5KB payload (vs 100KB+ for full schema)',
bestPractices: [ bestPractices: [
'Always start with essentials, only use get_node_info if needed', 'Always start with essentials, only use get_node_info if needed',
'Copy examples as configuration starting points', 'Use validate_node_operation to get working configurations',
'Check authentication requirements first', 'Check authentication requirements first',
'Use search_node_properties if specific property not in essentials' 'Use search_node_properties if specific property not in essentials'
], ],

View File

@@ -7,7 +7,6 @@
import { ConfigValidator, ValidationResult, ValidationError, ValidationWarning } from './config-validator'; import { ConfigValidator, ValidationResult, ValidationError, ValidationWarning } from './config-validator';
import { NodeSpecificValidators, NodeValidationContext } from './node-specific-validators'; import { NodeSpecificValidators, NodeValidationContext } from './node-specific-validators';
import { ExampleGenerator } from './example-generator';
export type ValidationMode = 'full' | 'operation' | 'minimal'; export type ValidationMode = 'full' | 'operation' | 'minimal';
export type ValidationProfile = 'strict' | 'runtime' | 'ai-friendly' | 'minimal'; export type ValidationProfile = 'strict' | 'runtime' | 'ai-friendly' | 'minimal';
@@ -78,10 +77,7 @@ export class EnhancedConfigValidator extends ConfigValidator {
// Deduplicate errors // Deduplicate errors
enhancedResult.errors = this.deduplicateErrors(enhancedResult.errors); enhancedResult.errors = this.deduplicateErrors(enhancedResult.errors);
// Add examples from ExampleGenerator if there are errors // Examples removed - use validate_node_operation for configuration guidance
if (enhancedResult.errors.length > 0) {
this.addExamplesFromGenerator(nodeType, enhancedResult);
}
// Generate next steps based on errors // Generate next steps based on errors
enhancedResult.nextSteps = this.generateNextSteps(enhancedResult); enhancedResult.nextSteps = this.generateNextSteps(enhancedResult);
@@ -253,16 +249,7 @@ export class EnhancedConfigValidator extends ConfigValidator {
const { resource, operation } = result.operation || {}; const { resource, operation } = result.operation || {};
if (resource === 'message' && operation === 'send') { if (resource === 'message' && operation === 'send') {
// Add example for sending a message // Examples removed - validation focuses on error detection
result.examples?.push({
description: 'Send a simple text message to a channel',
config: {
resource: 'message',
operation: 'send',
channel: '#general',
text: 'Hello from n8n!'
}
});
// Check for common issues // Check for common issues
if (!config.channel && !config.channelId) { if (!config.channel && !config.channelId) {
@@ -274,15 +261,6 @@ export class EnhancedConfigValidator extends ConfigValidator {
channelError.fix = 'Add channel: "#general" or use a channel ID like "C1234567890"'; channelError.fix = 'Add channel: "#general" or use a channel ID like "C1234567890"';
} }
} }
} else if (resource === 'user' && operation === 'get') {
result.examples?.push({
description: 'Get user information by email',
config: {
resource: 'user',
operation: 'get',
user: 'user@example.com'
}
});
} }
} }
@@ -296,17 +274,7 @@ export class EnhancedConfigValidator extends ConfigValidator {
const { operation } = result.operation || {}; const { operation } = result.operation || {};
if (operation === 'append') { if (operation === 'append') {
result.examples?.push({ // Examples removed - validation focuses on configuration correctness
description: 'Append data to a spreadsheet',
config: {
operation: 'append',
sheetId: '1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms',
range: 'Sheet1!A:B',
options: {
valueInputMode: 'USER_ENTERED'
}
}
});
// Validate range format // Validate range format
if (config.range && !config.range.includes('!')) { if (config.range && !config.range.includes('!')) {
@@ -327,33 +295,7 @@ export class EnhancedConfigValidator extends ConfigValidator {
config: Record<string, any>, config: Record<string, any>,
result: EnhancedValidationResult result: EnhancedValidationResult
): void { ): void {
// Add common examples based on method // Examples removed - validation provides error messages and fixes instead
if (config.method === 'GET') {
result.examples?.push({
description: 'GET request with query parameters',
config: {
method: 'GET',
url: 'https://api.example.com/users',
queryParameters: {
parameters: [
{ name: 'page', value: '1' },
{ name: 'limit', value: '10' }
]
}
}
});
} else if (config.method === 'POST') {
result.examples?.push({
description: 'POST request with JSON body',
config: {
method: 'POST',
url: 'https://api.example.com/users',
sendBody: true,
bodyContentType: 'json',
jsonBody: JSON.stringify({ name: 'John Doe', email: 'john@example.com' })
}
});
}
} }
/** /**
@@ -383,56 +325,13 @@ export class EnhancedConfigValidator extends ConfigValidator {
steps.push('Consider addressing warnings for better reliability'); steps.push('Consider addressing warnings for better reliability');
} }
if (result.examples && result.examples.length > 0 && result.errors.length > 0) { if (result.errors.length > 0) {
steps.push('See examples above for working configurations'); steps.push('Fix the errors above following the provided suggestions');
} }
return steps; return steps;
} }
/**
* Add examples from ExampleGenerator to help fix validation errors
*/
private static addExamplesFromGenerator(
nodeType: string,
result: EnhancedValidationResult
): void {
const examples = ExampleGenerator.getExamples(nodeType);
if (!examples) {
return;
}
// Add minimal example if there are missing required fields
if (result.errors.some(e => e.type === 'missing_required')) {
result.examples?.push({
description: 'Minimal working configuration',
config: examples.minimal
});
}
// Add common example if available
if (examples.common) {
// Check if the common example matches the operation context
const { operation } = result.operation || {};
const commonOp = examples.common.operation || examples.common.action;
if (!operation || operation === commonOp) {
result.examples?.push({
description: 'Common configuration pattern',
config: examples.common
});
}
}
// Add advanced example for complex validation errors
if (examples.advanced && result.errors.length > 2) {
result.examples?.push({
description: 'Advanced configuration with all options',
config: examples.advanced
});
}
}
/** /**
* Deduplicate errors based on property and type * Deduplicate errors based on property and type