mirror of
https://github.com/czlonkowski/n8n-mcp.git
synced 2026-03-20 09:23:07 +00:00
fix: correct misleading Code node documentation based on real-world testing
Critical fixes based on Claude Desktop feedback:
1. Fixed crypto documentation: require('crypto') IS available despite editor warnings
- Added clear examples of crypto usage
- Updated validation to guide correct require() usage
2. Clarified $helpers vs standalone functions
- $getWorkflowStaticData() is standalone, NOT $helpers.getWorkflowStaticData()
- Added validation to catch incorrect usage (prevents '$helpers is not defined' errors)
- Enhanced examples showing proper $helpers availability checks
3. Fixed JMESPath numeric literal documentation
- n8n requires backticks around numbers in filters: [?age >= `18`]
- Added multiple examples and validation to detect missing backticks
- Prevents 'JMESPath syntax error' that Claude Desktop encountered
4. Fixed webhook data access gotcha
- Webhook payload is at items[0].json.body, NOT items[0].json
- Added dedicated 'Webhook Data Access' section with clear examples
- Created process_webhook_data task template
- Added validation to detect incorrect webhook data access patterns
All fixes based on production workflows TaNqYoZNNeHC4Hne and JZ9urD7PNClDZ1bm
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -391,12 +391,18 @@ export class ConfigValidator {
|
||||
* Check for common configuration issues
|
||||
*/
|
||||
private static checkCommonIssues(
|
||||
_nodeType: string,
|
||||
nodeType: string,
|
||||
config: Record<string, any>,
|
||||
properties: any[],
|
||||
warnings: ValidationWarning[],
|
||||
suggestions: string[]
|
||||
): void {
|
||||
// Skip visibility checks for Code nodes as they have simple property structure
|
||||
if (nodeType === 'nodes-base.code') {
|
||||
// Code nodes don't have complex displayOptions, so skip visibility warnings
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for properties that won't be used
|
||||
const visibleProps = properties.filter(p => this.isPropertyVisible(p, config));
|
||||
const configuredKeys = Object.keys(config);
|
||||
@@ -562,20 +568,133 @@ export class ConfigValidator {
|
||||
warnings.push({
|
||||
type: 'missing_common',
|
||||
message: 'No return statement found',
|
||||
suggestion: 'Code node should return data for the next node. Add: return items (Python) or return items; (JavaScript)'
|
||||
suggestion: 'Code node must return data. Example: return [{json: {result: "success"}}]'
|
||||
});
|
||||
}
|
||||
|
||||
// Check for common n8n patterns
|
||||
// Check return format for JavaScript
|
||||
if (language === 'javascript' && hasReturn) {
|
||||
// Check for common incorrect return patterns
|
||||
if (/return\s+items\s*;/.test(code) && !code.includes('.map') && !code.includes('json:')) {
|
||||
warnings.push({
|
||||
type: 'best_practice',
|
||||
message: 'Returning items directly - ensure each item has {json: ...} structure',
|
||||
suggestion: 'If modifying items, use: return items.map(item => ({json: {...item.json, newField: "value"}}))'
|
||||
});
|
||||
}
|
||||
|
||||
// Check for return without array
|
||||
if (/return\s+{[^}]+}\s*;/.test(code) && !code.includes('[') && !code.includes(']')) {
|
||||
warnings.push({
|
||||
type: 'invalid_value',
|
||||
message: 'Return value must be an array',
|
||||
suggestion: 'Wrap your return object in an array: return [{json: {your: "data"}}]'
|
||||
});
|
||||
}
|
||||
|
||||
// Check for direct data return without json wrapper
|
||||
if (/return\s+\[['"`]/.test(code) || /return\s+\[\d/.test(code)) {
|
||||
warnings.push({
|
||||
type: 'invalid_value',
|
||||
message: 'Items must be objects with json property',
|
||||
suggestion: 'Use format: return [{json: {value: "data"}}] not return ["data"]'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Check return format for Python
|
||||
if (language === 'python' && hasReturn) {
|
||||
// Check for common incorrect patterns
|
||||
if (/return\s+items\s*$/.test(code) && !code.includes('json') && !code.includes('dict')) {
|
||||
warnings.push({
|
||||
type: 'best_practice',
|
||||
message: 'Returning items directly - ensure each item is a dict with "json" key',
|
||||
suggestion: 'Use: return [{"json": item.json} for item in items]'
|
||||
});
|
||||
}
|
||||
|
||||
// Check for dict return without list
|
||||
if (/return\s+{['"]/.test(code) && !code.includes('[') && !code.includes(']')) {
|
||||
warnings.push({
|
||||
type: 'invalid_value',
|
||||
message: 'Return value must be a list',
|
||||
suggestion: 'Wrap your return dict in a list: return [{"json": {"your": "data"}}]'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Check for common n8n variables and patterns
|
||||
if (language === 'javascript') {
|
||||
if (!code.includes('items') && !code.includes('$input')) {
|
||||
// Check if accessing items/input
|
||||
if (!code.includes('items') && !code.includes('$input') && !code.includes('$json')) {
|
||||
warnings.push({
|
||||
type: 'missing_common',
|
||||
message: 'Code doesn\'t reference input items',
|
||||
suggestion: 'Access input data with: items or $input.all()'
|
||||
message: 'Code doesn\'t reference input data',
|
||||
suggestion: 'Access input with: items, $input.all(), or $json (in single-item mode)'
|
||||
});
|
||||
}
|
||||
|
||||
// Check for common mistakes with $json
|
||||
if (code.includes('$json') && !code.includes('mode')) {
|
||||
warnings.push({
|
||||
type: 'best_practice',
|
||||
message: '$json only works in "Run Once for Each Item" mode',
|
||||
suggestion: 'For all items mode, use: items[0].json or loop through items'
|
||||
});
|
||||
}
|
||||
|
||||
// Check for undefined variable usage
|
||||
const commonVars = ['$node', '$workflow', '$execution', '$prevNode', 'DateTime', 'jmespath'];
|
||||
const usedVars = commonVars.filter(v => code.includes(v));
|
||||
|
||||
// Check for incorrect $helpers usage patterns
|
||||
if (code.includes('$helpers.getWorkflowStaticData')) {
|
||||
warnings.push({
|
||||
type: 'invalid_value',
|
||||
message: '$helpers.getWorkflowStaticData() is incorrect - causes "$helpers is not defined" error',
|
||||
suggestion: 'Use $getWorkflowStaticData() as a standalone function (no $helpers prefix)'
|
||||
});
|
||||
}
|
||||
|
||||
// Check for $helpers usage without checking availability
|
||||
if (code.includes('$helpers') && !code.includes('typeof $helpers')) {
|
||||
warnings.push({
|
||||
type: 'best_practice',
|
||||
message: '$helpers availability varies by n8n version',
|
||||
suggestion: 'Check availability first: if (typeof $helpers !== "undefined" && $helpers.httpRequest) { ... }'
|
||||
});
|
||||
}
|
||||
|
||||
// Check for async without await
|
||||
if (code.includes('async') || code.includes('.then(')) {
|
||||
if (!code.includes('await')) {
|
||||
warnings.push({
|
||||
type: 'best_practice',
|
||||
message: 'Using async operations without await',
|
||||
suggestion: 'Use await for async operations: await $helpers.httpRequest(...)'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Check for crypto usage without require
|
||||
if ((code.includes('crypto.') || code.includes('randomBytes') || code.includes('randomUUID')) && !code.includes('require')) {
|
||||
warnings.push({
|
||||
type: 'invalid_value',
|
||||
message: 'Using crypto without require statement',
|
||||
suggestion: 'Add: const crypto = require("crypto"); at the beginning (ignore editor warnings)'
|
||||
});
|
||||
}
|
||||
|
||||
// Check for console.log (informational)
|
||||
if (code.includes('console.log')) {
|
||||
warnings.push({
|
||||
type: 'best_practice',
|
||||
message: 'console.log output appears in n8n execution logs',
|
||||
suggestion: 'Remove console.log statements in production or use them sparingly'
|
||||
});
|
||||
}
|
||||
} else if (language === 'python') {
|
||||
// Python-specific checks
|
||||
if (!code.includes('items') && !code.includes('_input')) {
|
||||
warnings.push({
|
||||
type: 'missing_common',
|
||||
@@ -583,6 +702,44 @@ export class ConfigValidator {
|
||||
suggestion: 'Access input data with: items variable'
|
||||
});
|
||||
}
|
||||
|
||||
// Check for print statements
|
||||
if (code.includes('print(')) {
|
||||
warnings.push({
|
||||
type: 'best_practice',
|
||||
message: 'print() output appears in n8n execution logs',
|
||||
suggestion: 'Remove print statements in production or use them sparingly'
|
||||
});
|
||||
}
|
||||
|
||||
// Check for common Python mistakes
|
||||
if (code.includes('import requests') || code.includes('import pandas')) {
|
||||
warnings.push({
|
||||
type: 'invalid_value',
|
||||
message: 'External libraries not available in Code node',
|
||||
suggestion: 'Only Python standard library is available. For HTTP requests, use JavaScript with $helpers.httpRequest'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Check for infinite loops
|
||||
if (/while\s*\(\s*true\s*\)|while\s+True:/.test(code)) {
|
||||
warnings.push({
|
||||
type: 'security',
|
||||
message: 'Infinite loop detected',
|
||||
suggestion: 'Add a break condition or use a for loop with limits'
|
||||
});
|
||||
}
|
||||
|
||||
// Check for error handling
|
||||
if (!code.includes('try') && !code.includes('catch') && !code.includes('except')) {
|
||||
if (code.length > 200) { // Only suggest for non-trivial code
|
||||
warnings.push({
|
||||
type: 'best_practice',
|
||||
message: 'No error handling found',
|
||||
suggestion: 'Consider adding try/catch (JavaScript) or try/except (Python) for robust error handling'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user