From ad4b5214029e3ddc34105c76052da64a4f0e12c4 Mon Sep 17 00:00:00 2001 From: czlonkowski <56956555+czlonkowski@users.noreply.github.com> Date: Fri, 24 Oct 2025 16:51:18 +0200 Subject: [PATCH] enhance: Add HTTP Request node validation suggestions (issue #361) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added helpful suggestions for HTTP Request node best practices after thorough investigation of issue #361. ## What's New 1. **alwaysOutputData Suggestion** - Suggests adding alwaysOutputData: true at node level - Prevents silent workflow failures when HTTP requests error - Ensures downstream error handling can process failed requests 2. **responseFormat Suggestion for API Endpoints** - Suggests setting options.response.response.responseFormat - Prevents JSON parsing confusion - Triggered for URLs containing /api, /rest, supabase, firebase, googleapis, .com/v 3. **Enhanced URL Protocol Validation** - Detects missing protocol in expression-based URLs - Warns about patterns like =www.{{ $json.domain }}.com - Warns about expressions without protocol ## Investigation Findings **Key Discoveries:** - Mixed expression syntax =literal{{ expression }} actually works in n8n (claim was incorrect) - Real validation gaps: missing alwaysOutputData and responseFormat checks - Compared broken vs fixed workflows to identify actual production issues **Testing Evidence:** - Analyzed workflow SwjKJsJhe8OsYfBk with mixed syntax - executions successful - Compared broken workflow (mBmkyj460i5rYTG4) with fixed workflow (hQI9pby3nSFtk4TV) - Identified that fixed workflow has alwaysOutputData: true and explicit responseFormat ## Impact - Non-Breaking: All changes are suggestions/warnings, not errors - Actionable: Clear guidance on how to implement best practices - Production-Focused: Addresses real workflow reliability concerns ## Test Coverage Added 8 new test cases covering: - alwaysOutputData suggestion for all HTTP Request nodes - responseFormat suggestion for API endpoint detection - responseFormat NOT suggested when already configured - URL protocol validation for expression-based URLs - No false positives when protocol is correctly included ## Files Changed - src/services/enhanced-config-validator.ts - Added enhanceHttpRequestValidation() - tests/unit/services/enhanced-config-validator.test.ts - Added 8 test cases - CHANGELOG.md - Documented enhancement with investigation findings - package.json - Bump version to 2.22.2 Fixes #361 Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en --- CHANGELOG.md | 83 +++++++ data/nodes.db | Bin 62623744 -> 62623744 bytes package.json | 2 +- src/services/enhanced-config-validator.ts | 43 +++- .../enhanced-config-validator.test.ts | 219 ++++++++++++++++++ 5 files changed, 345 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc4783a..0fe327c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,89 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### ✨ Enhancements + +**Issue #361: Enhanced HTTP Request Node Validation Suggestions** + +Added helpful suggestions for HTTP Request node best practices to prevent common production issues discovered through real-world workflow analysis. + +#### What's New + +1. **alwaysOutputData Suggestion** + - Suggests adding `alwaysOutputData: true` at node level (not in parameters) + - Prevents silent workflow failures when HTTP requests error + - Ensures downstream error handling can process failed requests + - Example suggestion: "Consider adding alwaysOutputData: true at node level for better error handling. This ensures the node produces output even when HTTP requests fail, allowing downstream error handling." + +2. **responseFormat Suggestion for API Endpoints** + - Suggests setting `options.response.response.responseFormat` for API endpoints + - Prevents JSON parsing confusion + - Triggered when URL contains `/api`, `/rest`, `supabase`, `firebase`, `googleapis`, or `.com/v` patterns + - Example suggestion: "API endpoints should explicitly set options.response.response.responseFormat to 'json' or 'text' to prevent confusion about response parsing" + +3. **Enhanced URL Protocol Validation** + - Detects missing protocol in expression-based URLs + - Warns about patterns like `=www.{{ $json.domain }}.com` (missing http://) + - Warns about expressions without protocol: `={{ $json.domain }}/api/data` + - Example warning: "URL expression appears to be missing http:// or https:// protocol" + +#### Investigation Findings + +This enhancement was developed after thorough investigation of issue #361: + +**Key Discoveries:** +- ✅ Mixed expression syntax `=literal{{ expression }}` **actually works in n8n** - the issue report's primary claim was incorrect +- ✅ Real validation gaps identified: missing `alwaysOutputData` and `responseFormat` checks +- ✅ Workflow analysis showed "?" icon in UI caused by missing required URL (already caught by validation) +- ✅ Compared broken vs fixed workflows to identify actual production issues + +**Testing Evidence:** +- Analyzed workflow SwjKJsJhe8OsYfBk with mixed syntax - executions successful +- Compared broken workflow (mBmkyj460i5rYTG4) with fixed workflow (hQI9pby3nSFtk4TV) +- Identified that fixed workflow has `alwaysOutputData: true` and explicit `responseFormat: "json"` + +#### Impact + +- **Non-Breaking**: All changes are suggestions/warnings, not errors +- **Profile-Aware**: Suggestions shown in all profiles for maximum helpfulness +- **Actionable**: Clear guidance on how to implement best practices +- **Production-Focused**: Addresses real workflow reliability concerns from actual broken workflows + +#### Test Coverage + +Added 8 new test cases covering: +- alwaysOutputData suggestion for all HTTP Request nodes +- responseFormat suggestion for API endpoint detection (various patterns) +- responseFormat NOT suggested when already configured +- URL protocol validation for expression-based URLs +- Protocol warnings for missing http:// in expressions +- No false positives when protocol is correctly included + +#### Technical Details + +**Files Modified:** +- `src/services/enhanced-config-validator.ts` - Added `enhanceHttpRequestValidation()` implementation +- `tests/unit/services/enhanced-config-validator.test.ts` - Added 8 comprehensive test cases + +**Validation Flow:** +1. Check for alwaysOutputData suggestion (all HTTP Request nodes) +2. Detect API endpoints by URL patterns +3. Check for explicit responseFormat configuration +4. Validate expression-based URLs for protocol issues + +#### Related + +- **Issue**: #361 - validate_node_operation: Missing critical HTTP Request node configuration checks +- **Analysis**: Deep investigation with @agent-Explore and @agent-n8n-mcp-tester +- **Workflows Analyzed**: + - SwjKJsJhe8OsYfBk (mixed syntax test) + - mBmkyj460i5rYTG4 (broken workflow) + - hQI9pby3nSFtk4TV (fixed workflow) + +Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en + +--- + ### 🐛 Bug Fixes **Issue #360: Enhanced Warnings for If/Switch Node Connection Parameters** diff --git a/data/nodes.db b/data/nodes.db index b67f14688a6158d4ed04154ecca065ceebb0e91c..e22fcad531e550c45c3bfbd4698081589a9dca7e 100644 GIT binary patch delta 23630 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 F{{h~EyG{TA diff --git a/package.json b/package.json index 4a608cf..6ff5306 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "n8n-mcp", - "version": "2.22.1", + "version": "2.22.2", "description": "Integration between n8n workflow automation and Model Context Protocol (MCP)", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/src/services/enhanced-config-validator.ts b/src/services/enhanced-config-validator.ts index beff9a2..472d224 100644 --- a/src/services/enhanced-config-validator.ts +++ b/src/services/enhanced-config-validator.ts @@ -401,7 +401,48 @@ export class EnhancedConfigValidator extends ConfigValidator { config: Record, result: EnhancedValidationResult ): void { - // Examples removed - validation provides error messages and fixes instead + const url = String(config.url || ''); + const options = config.options || {}; + + // 1. Suggest alwaysOutputData for better error handling (node-level property) + // Note: We can't check if it exists (it's node-level, not in parameters), + // but we can suggest it as a best practice + if (!result.suggestions.some(s => typeof s === 'string' && s.includes('alwaysOutputData'))) { + result.suggestions.push( + 'Consider adding alwaysOutputData: true at node level (not in parameters) for better error handling. ' + + 'This ensures the node produces output even when HTTP requests fail, allowing downstream error handling.' + ); + } + + // 2. Suggest responseFormat for API endpoints + const isApiEndpoint = url.includes('/api') || url.includes('/rest') || + url.includes('supabase') || url.includes('firebase') || + url.includes('googleapis') || url.includes('.com/v'); + + if (isApiEndpoint && !options.response?.response?.responseFormat) { + result.suggestions.push( + 'API endpoints should explicitly set options.response.response.responseFormat to "json" or "text" ' + + 'to prevent confusion about response parsing. Example: ' + + '{ "options": { "response": { "response": { "responseFormat": "json" } } } }' + ); + } + + // 3. Enhanced URL protocol validation for expressions + if (url && url.startsWith('=')) { + // Expression-based URL - check for common protocol issues + const expressionContent = url.slice(1); // Remove = prefix + + // Check for missing protocol in expression + if (expressionContent.startsWith('www.') || + (expressionContent.includes('{{') && !expressionContent.includes('http'))) { + result.warnings.push({ + type: 'invalid_value', + property: 'url', + message: 'URL expression appears to be missing http:// or https:// protocol', + suggestion: 'Include protocol in your expression. Example: ={{ "https://" + $json.domain + ".com" }}' + }); + } + } } /** diff --git a/tests/unit/services/enhanced-config-validator.test.ts b/tests/unit/services/enhanced-config-validator.test.ts index 1998d46..e3217f6 100644 --- a/tests/unit/services/enhanced-config-validator.test.ts +++ b/tests/unit/services/enhanced-config-validator.test.ts @@ -802,4 +802,223 @@ describe('EnhancedConfigValidator', () => { expect(result.errors[0].property).toBe('test'); }); }); + + describe('enhanceHttpRequestValidation', () => { + it('should suggest alwaysOutputData for HTTP Request nodes', () => { + const nodeType = 'nodes-base.httpRequest'; + const config = { + url: 'https://api.example.com/data', + method: 'GET' + }; + const properties = [ + { name: 'url', type: 'string', required: true }, + { name: 'method', type: 'options', required: false } + ]; + + const result = EnhancedConfigValidator.validateWithMode( + nodeType, + config, + properties, + 'operation', + 'ai-friendly' + ); + + expect(result.valid).toBe(true); + expect(result.suggestions).toContainEqual( + expect.stringContaining('alwaysOutputData: true at node level') + ); + expect(result.suggestions).toContainEqual( + expect.stringContaining('ensures the node produces output even when HTTP requests fail') + ); + }); + + it('should suggest responseFormat for API endpoint URLs', () => { + const nodeType = 'nodes-base.httpRequest'; + const config = { + url: 'https://api.example.com/data', + method: 'GET', + options: {} // Empty options, no responseFormat + }; + const properties = [ + { name: 'url', type: 'string', required: true }, + { name: 'method', type: 'options', required: false }, + { name: 'options', type: 'collection', required: false } + ]; + + const result = EnhancedConfigValidator.validateWithMode( + nodeType, + config, + properties, + 'operation', + 'ai-friendly' + ); + + expect(result.valid).toBe(true); + expect(result.suggestions).toContainEqual( + expect.stringContaining('responseFormat') + ); + expect(result.suggestions).toContainEqual( + expect.stringContaining('options.response.response.responseFormat') + ); + }); + + it('should suggest responseFormat for Supabase URLs', () => { + const nodeType = 'nodes-base.httpRequest'; + const config = { + url: 'https://xxciwnthnnywanbplqwg.supabase.co/rest/v1/messages', + method: 'GET', + options: {} + }; + const properties = [ + { name: 'url', type: 'string', required: true } + ]; + + const result = EnhancedConfigValidator.validateWithMode( + nodeType, + config, + properties, + 'operation', + 'ai-friendly' + ); + + expect(result.suggestions).toContainEqual( + expect.stringContaining('responseFormat') + ); + }); + + it('should NOT suggest responseFormat when already configured', () => { + const nodeType = 'nodes-base.httpRequest'; + const config = { + url: 'https://api.example.com/data', + method: 'GET', + options: { + response: { + response: { + responseFormat: 'json' + } + } + } + }; + const properties = [ + { name: 'url', type: 'string', required: true }, + { name: 'options', type: 'collection', required: false } + ]; + + const result = EnhancedConfigValidator.validateWithMode( + nodeType, + config, + properties, + 'operation', + 'ai-friendly' + ); + + const responseFormatSuggestion = result.suggestions.find( + (s: string) => s.includes('responseFormat') + ); + expect(responseFormatSuggestion).toBeUndefined(); + }); + + it('should warn about missing protocol in expression-based URLs', () => { + const nodeType = 'nodes-base.httpRequest'; + const config = { + url: '=www.{{ $json.domain }}.com', + method: 'GET' + }; + const properties = [ + { name: 'url', type: 'string', required: true } + ]; + + const result = EnhancedConfigValidator.validateWithMode( + nodeType, + config, + properties, + 'operation', + 'ai-friendly' + ); + + expect(result.warnings).toContainEqual( + expect.objectContaining({ + type: 'invalid_value', + property: 'url', + message: expect.stringContaining('missing http:// or https://') + }) + ); + }); + + it('should warn about missing protocol in expressions with template markers', () => { + const nodeType = 'nodes-base.httpRequest'; + const config = { + url: '={{ $json.domain }}/api/data', + method: 'GET' + }; + const properties = [ + { name: 'url', type: 'string', required: true } + ]; + + const result = EnhancedConfigValidator.validateWithMode( + nodeType, + config, + properties, + 'operation', + 'ai-friendly' + ); + + expect(result.warnings).toContainEqual( + expect.objectContaining({ + type: 'invalid_value', + property: 'url', + message: expect.stringContaining('missing http:// or https://') + }) + ); + }); + + it('should NOT warn when expression includes http protocol', () => { + const nodeType = 'nodes-base.httpRequest'; + const config = { + url: '={{ "https://" + $json.domain + ".com" }}', + method: 'GET' + }; + const properties = [ + { name: 'url', type: 'string', required: true } + ]; + + const result = EnhancedConfigValidator.validateWithMode( + nodeType, + config, + properties, + 'operation', + 'ai-friendly' + ); + + const urlWarning = result.warnings.find( + (w: any) => w.property === 'url' && w.message.includes('protocol') + ); + expect(urlWarning).toBeUndefined(); + }); + + it('should NOT suggest responseFormat for non-API URLs', () => { + const nodeType = 'nodes-base.httpRequest'; + const config = { + url: 'https://example.com/page.html', + method: 'GET', + options: {} + }; + const properties = [ + { name: 'url', type: 'string', required: true } + ]; + + const result = EnhancedConfigValidator.validateWithMode( + nodeType, + config, + properties, + 'operation', + 'ai-friendly' + ); + + const responseFormatSuggestion = result.suggestions.find( + (s: string) => s.includes('responseFormat') + ); + expect(responseFormatSuggestion).toBeUndefined(); + }); + }); }); \ No newline at end of file