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:
BIN
data/nodes.db
BIN
data/nodes.db
Binary file not shown.
4
package-lock.json
generated
4
package-lock.json
generated
@@ -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",
|
||||||
|
|||||||
@@ -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": {
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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",
|
||||||
|
"displayName": "Resource",
|
||||||
|
"type": "options",
|
||||||
|
"options": ["channel", "message", "user"],
|
||||||
|
"default": "message"
|
||||||
},
|
},
|
||||||
"examples": {
|
{
|
||||||
"postMessage": {
|
"name": "operation",
|
||||||
"resource": "message",
|
"displayName": "Operation",
|
||||||
"operation": "post",
|
"type": "options",
|
||||||
"channel": "#general",
|
"options": ["post", "update", "delete"],
|
||||||
"text": "Hello from n8n!"
|
"default": "post"
|
||||||
},
|
},
|
||||||
"withBlocks": {
|
// ... 10-20 most common properties
|
||||||
"resource": "message",
|
],
|
||||||
"operation": "post",
|
"operations": [
|
||||||
"blocks": [{"type": "section", "text": {"type": "mrkdwn", "text": "Hello"}}]
|
{"name": "Post", "description": "Post a message"},
|
||||||
}
|
{"name": "Update", "description": "Update a message"}
|
||||||
},
|
],
|
||||||
"authentication": {
|
"metadata": {
|
||||||
"required": true,
|
"totalProperties": 121,
|
||||||
"options": ["accessToken", "oAuth2"]
|
"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'
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user