This is page 42 of 59. Use http://codebase.md/czlonkowski/n8n-mcp?lines=true&page={x} to view the full context. # Directory Structure ``` ├── _config.yml ├── .claude │ └── agents │ ├── code-reviewer.md │ ├── context-manager.md │ ├── debugger.md │ ├── deployment-engineer.md │ ├── mcp-backend-engineer.md │ ├── n8n-mcp-tester.md │ ├── technical-researcher.md │ └── test-automator.md ├── .dockerignore ├── .env.docker ├── .env.example ├── .env.n8n.example ├── .env.test ├── .env.test.example ├── .github │ ├── ABOUT.md │ ├── BENCHMARK_THRESHOLDS.md │ ├── FUNDING.yml │ ├── gh-pages.yml │ ├── secret_scanning.yml │ └── workflows │ ├── benchmark-pr.yml │ ├── benchmark.yml │ ├── docker-build-fast.yml │ ├── docker-build-n8n.yml │ ├── docker-build.yml │ ├── release.yml │ ├── test.yml │ └── update-n8n-deps.yml ├── .gitignore ├── .npmignore ├── ATTRIBUTION.md ├── CHANGELOG.md ├── CLAUDE.md ├── codecov.yml ├── coverage.json ├── data │ ├── .gitkeep │ ├── nodes.db │ ├── nodes.db-shm │ ├── nodes.db-wal │ └── templates.db ├── deploy │ └── quick-deploy-n8n.sh ├── docker │ ├── docker-entrypoint.sh │ ├── n8n-mcp │ ├── parse-config.js │ └── README.md ├── docker-compose.buildkit.yml ├── docker-compose.extract.yml ├── docker-compose.n8n.yml ├── docker-compose.override.yml.example ├── docker-compose.test-n8n.yml ├── docker-compose.yml ├── Dockerfile ├── Dockerfile.railway ├── Dockerfile.test ├── docs │ ├── AUTOMATED_RELEASES.md │ ├── BENCHMARKS.md │ ├── CHANGELOG.md │ ├── CLAUDE_CODE_SETUP.md │ ├── CLAUDE_INTERVIEW.md │ ├── CODECOV_SETUP.md │ ├── CODEX_SETUP.md │ ├── CURSOR_SETUP.md │ ├── DEPENDENCY_UPDATES.md │ ├── DOCKER_README.md │ ├── DOCKER_TROUBLESHOOTING.md │ ├── FINAL_AI_VALIDATION_SPEC.md │ ├── FLEXIBLE_INSTANCE_CONFIGURATION.md │ ├── HTTP_DEPLOYMENT.md │ ├── img │ │ ├── cc_command.png │ │ ├── cc_connected.png │ │ ├── codex_connected.png │ │ ├── cursor_tut.png │ │ ├── Railway_api.png │ │ ├── Railway_server_address.png │ │ ├── vsc_ghcp_chat_agent_mode.png │ │ ├── vsc_ghcp_chat_instruction_files.png │ │ ├── vsc_ghcp_chat_thinking_tool.png │ │ └── windsurf_tut.png │ ├── INSTALLATION.md │ ├── LIBRARY_USAGE.md │ ├── local │ │ ├── DEEP_DIVE_ANALYSIS_2025-10-02.md │ │ ├── DEEP_DIVE_ANALYSIS_README.md │ │ ├── Deep_dive_p1_p2.md │ │ ├── integration-testing-plan.md │ │ ├── integration-tests-phase1-summary.md │ │ ├── N8N_AI_WORKFLOW_BUILDER_ANALYSIS.md │ │ ├── P0_IMPLEMENTATION_PLAN.md │ │ └── TEMPLATE_MINING_ANALYSIS.md │ ├── MCP_ESSENTIALS_README.md │ ├── MCP_QUICK_START_GUIDE.md │ ├── N8N_DEPLOYMENT.md │ ├── RAILWAY_DEPLOYMENT.md │ ├── README_CLAUDE_SETUP.md │ ├── README.md │ ├── tools-documentation-usage.md │ ├── VS_CODE_PROJECT_SETUP.md │ ├── WINDSURF_SETUP.md │ └── workflow-diff-examples.md ├── examples │ └── enhanced-documentation-demo.js ├── fetch_log.txt ├── LICENSE ├── MEMORY_N8N_UPDATE.md ├── MEMORY_TEMPLATE_UPDATE.md ├── monitor_fetch.sh ├── N8N_HTTP_STREAMABLE_SETUP.md ├── n8n-nodes.db ├── P0-R3-TEST-PLAN.md ├── package-lock.json ├── package.json ├── package.runtime.json ├── PRIVACY.md ├── railway.json ├── README.md ├── renovate.json ├── scripts │ ├── analyze-optimization.sh │ ├── audit-schema-coverage.ts │ ├── build-optimized.sh │ ├── compare-benchmarks.js │ ├── demo-optimization.sh │ ├── deploy-http.sh │ ├── deploy-to-vm.sh │ ├── export-webhook-workflows.ts │ ├── extract-changelog.js │ ├── extract-from-docker.js │ ├── extract-nodes-docker.sh │ ├── extract-nodes-simple.sh │ ├── format-benchmark-results.js │ ├── generate-benchmark-stub.js │ ├── generate-detailed-reports.js │ ├── generate-test-summary.js │ ├── http-bridge.js │ ├── mcp-http-client.js │ ├── migrate-nodes-fts.ts │ ├── migrate-tool-docs.ts │ ├── n8n-docs-mcp.service │ ├── nginx-n8n-mcp.conf │ ├── prebuild-fts5.ts │ ├── prepare-release.js │ ├── publish-npm-quick.sh │ ├── publish-npm.sh │ ├── quick-test.ts │ ├── run-benchmarks-ci.js │ ├── sync-runtime-version.js │ ├── test-ai-validation-debug.ts │ ├── test-code-node-enhancements.ts │ ├── test-code-node-fixes.ts │ ├── test-docker-config.sh │ ├── test-docker-fingerprint.ts │ ├── test-docker-optimization.sh │ ├── test-docker.sh │ ├── test-empty-connection-validation.ts │ ├── test-error-message-tracking.ts │ ├── test-error-output-validation.ts │ ├── test-error-validation.js │ ├── test-essentials.ts │ ├── test-expression-code-validation.ts │ ├── test-expression-format-validation.js │ ├── test-fts5-search.ts │ ├── test-fuzzy-fix.ts │ ├── test-fuzzy-simple.ts │ ├── test-helpers-validation.ts │ ├── test-http-search.ts │ ├── test-http.sh │ ├── test-jmespath-validation.ts │ ├── test-multi-tenant-simple.ts │ ├── test-multi-tenant.ts │ ├── test-n8n-integration.sh │ ├── test-node-info.js │ ├── test-node-type-validation.ts │ ├── test-nodes-base-prefix.ts │ ├── test-operation-validation.ts │ ├── test-optimized-docker.sh │ ├── test-release-automation.js │ ├── test-search-improvements.ts │ ├── test-security.ts │ ├── test-single-session.sh │ ├── test-sqljs-triggers.ts │ ├── test-telemetry-debug.ts │ ├── test-telemetry-direct.ts │ ├── test-telemetry-env.ts │ ├── test-telemetry-integration.ts │ ├── test-telemetry-no-select.ts │ ├── test-telemetry-security.ts │ ├── test-telemetry-simple.ts │ ├── test-typeversion-validation.ts │ ├── test-url-configuration.ts │ ├── test-user-id-persistence.ts │ ├── test-webhook-validation.ts │ ├── test-workflow-insert.ts │ ├── test-workflow-sanitizer.ts │ ├── test-workflow-tracking-debug.ts │ ├── update-and-publish-prep.sh │ ├── update-n8n-deps.js │ ├── update-readme-version.js │ ├── vitest-benchmark-json-reporter.js │ └── vitest-benchmark-reporter.ts ├── SECURITY.md ├── src │ ├── config │ │ └── n8n-api.ts │ ├── data │ │ └── canonical-ai-tool-examples.json │ ├── database │ │ ├── database-adapter.ts │ │ ├── migrations │ │ │ └── add-template-node-configs.sql │ │ ├── node-repository.ts │ │ ├── nodes.db │ │ ├── schema-optimized.sql │ │ └── schema.sql │ ├── errors │ │ └── validation-service-error.ts │ ├── http-server-single-session.ts │ ├── http-server.ts │ ├── index.ts │ ├── loaders │ │ └── node-loader.ts │ ├── mappers │ │ └── docs-mapper.ts │ ├── mcp │ │ ├── handlers-n8n-manager.ts │ │ ├── handlers-workflow-diff.ts │ │ ├── index.ts │ │ ├── server.ts │ │ ├── stdio-wrapper.ts │ │ ├── tool-docs │ │ │ ├── configuration │ │ │ │ ├── get-node-as-tool-info.ts │ │ │ │ ├── get-node-documentation.ts │ │ │ │ ├── get-node-essentials.ts │ │ │ │ ├── get-node-info.ts │ │ │ │ ├── get-property-dependencies.ts │ │ │ │ ├── index.ts │ │ │ │ └── search-node-properties.ts │ │ │ ├── discovery │ │ │ │ ├── get-database-statistics.ts │ │ │ │ ├── index.ts │ │ │ │ ├── list-ai-tools.ts │ │ │ │ ├── list-nodes.ts │ │ │ │ └── search-nodes.ts │ │ │ ├── guides │ │ │ │ ├── ai-agents-guide.ts │ │ │ │ └── index.ts │ │ │ ├── index.ts │ │ │ ├── system │ │ │ │ ├── index.ts │ │ │ │ ├── n8n-diagnostic.ts │ │ │ │ ├── n8n-health-check.ts │ │ │ │ ├── n8n-list-available-tools.ts │ │ │ │ └── tools-documentation.ts │ │ │ ├── templates │ │ │ │ ├── get-template.ts │ │ │ │ ├── get-templates-for-task.ts │ │ │ │ ├── index.ts │ │ │ │ ├── list-node-templates.ts │ │ │ │ ├── list-tasks.ts │ │ │ │ ├── search-templates-by-metadata.ts │ │ │ │ └── search-templates.ts │ │ │ ├── types.ts │ │ │ ├── validation │ │ │ │ ├── index.ts │ │ │ │ ├── validate-node-minimal.ts │ │ │ │ ├── validate-node-operation.ts │ │ │ │ ├── validate-workflow-connections.ts │ │ │ │ ├── validate-workflow-expressions.ts │ │ │ │ └── validate-workflow.ts │ │ │ └── workflow_management │ │ │ ├── index.ts │ │ │ ├── n8n-autofix-workflow.ts │ │ │ ├── n8n-create-workflow.ts │ │ │ ├── n8n-delete-execution.ts │ │ │ ├── n8n-delete-workflow.ts │ │ │ ├── n8n-get-execution.ts │ │ │ ├── n8n-get-workflow-details.ts │ │ │ ├── n8n-get-workflow-minimal.ts │ │ │ ├── n8n-get-workflow-structure.ts │ │ │ ├── n8n-get-workflow.ts │ │ │ ├── n8n-list-executions.ts │ │ │ ├── n8n-list-workflows.ts │ │ │ ├── n8n-trigger-webhook-workflow.ts │ │ │ ├── n8n-update-full-workflow.ts │ │ │ ├── n8n-update-partial-workflow.ts │ │ │ └── n8n-validate-workflow.ts │ │ ├── tools-documentation.ts │ │ ├── tools-n8n-friendly.ts │ │ ├── tools-n8n-manager.ts │ │ ├── tools.ts │ │ └── workflow-examples.ts │ ├── mcp-engine.ts │ ├── mcp-tools-engine.ts │ ├── n8n │ │ ├── MCPApi.credentials.ts │ │ └── MCPNode.node.ts │ ├── parsers │ │ ├── node-parser.ts │ │ ├── property-extractor.ts │ │ └── simple-parser.ts │ ├── scripts │ │ ├── debug-http-search.ts │ │ ├── extract-from-docker.ts │ │ ├── fetch-templates-robust.ts │ │ ├── fetch-templates.ts │ │ ├── rebuild-database.ts │ │ ├── rebuild-optimized.ts │ │ ├── rebuild.ts │ │ ├── sanitize-templates.ts │ │ ├── seed-canonical-ai-examples.ts │ │ ├── test-autofix-documentation.ts │ │ ├── test-autofix-workflow.ts │ │ ├── test-execution-filtering.ts │ │ ├── test-node-suggestions.ts │ │ ├── test-protocol-negotiation.ts │ │ ├── test-summary.ts │ │ ├── test-webhook-autofix.ts │ │ ├── validate.ts │ │ └── validation-summary.ts │ ├── services │ │ ├── ai-node-validator.ts │ │ ├── ai-tool-validators.ts │ │ ├── confidence-scorer.ts │ │ ├── config-validator.ts │ │ ├── enhanced-config-validator.ts │ │ ├── example-generator.ts │ │ ├── execution-processor.ts │ │ ├── expression-format-validator.ts │ │ ├── expression-validator.ts │ │ ├── n8n-api-client.ts │ │ ├── n8n-validation.ts │ │ ├── node-documentation-service.ts │ │ ├── node-similarity-service.ts │ │ ├── node-specific-validators.ts │ │ ├── operation-similarity-service.ts │ │ ├── property-dependencies.ts │ │ ├── property-filter.ts │ │ ├── resource-similarity-service.ts │ │ ├── sqlite-storage-service.ts │ │ ├── task-templates.ts │ │ ├── universal-expression-validator.ts │ │ ├── workflow-auto-fixer.ts │ │ ├── workflow-diff-engine.ts │ │ └── workflow-validator.ts │ ├── telemetry │ │ ├── batch-processor.ts │ │ ├── config-manager.ts │ │ ├── early-error-logger.ts │ │ ├── error-sanitization-utils.ts │ │ ├── error-sanitizer.ts │ │ ├── event-tracker.ts │ │ ├── event-validator.ts │ │ ├── index.ts │ │ ├── performance-monitor.ts │ │ ├── rate-limiter.ts │ │ ├── startup-checkpoints.ts │ │ ├── telemetry-error.ts │ │ ├── telemetry-manager.ts │ │ ├── telemetry-types.ts │ │ └── workflow-sanitizer.ts │ ├── templates │ │ ├── batch-processor.ts │ │ ├── metadata-generator.ts │ │ ├── README.md │ │ ├── template-fetcher.ts │ │ ├── template-repository.ts │ │ └── template-service.ts │ ├── types │ │ ├── index.ts │ │ ├── instance-context.ts │ │ ├── n8n-api.ts │ │ ├── node-types.ts │ │ └── workflow-diff.ts │ └── utils │ ├── auth.ts │ ├── bridge.ts │ ├── cache-utils.ts │ ├── console-manager.ts │ ├── documentation-fetcher.ts │ ├── enhanced-documentation-fetcher.ts │ ├── error-handler.ts │ ├── example-generator.ts │ ├── fixed-collection-validator.ts │ ├── logger.ts │ ├── mcp-client.ts │ ├── n8n-errors.ts │ ├── node-source-extractor.ts │ ├── node-type-normalizer.ts │ ├── node-type-utils.ts │ ├── node-utils.ts │ ├── npm-version-checker.ts │ ├── protocol-version.ts │ ├── simple-cache.ts │ ├── ssrf-protection.ts │ ├── template-node-resolver.ts │ ├── template-sanitizer.ts │ ├── url-detector.ts │ ├── validation-schemas.ts │ └── version.ts ├── test-output.txt ├── test-reinit-fix.sh ├── tests │ ├── __snapshots__ │ │ └── .gitkeep │ ├── auth.test.ts │ ├── benchmarks │ │ ├── database-queries.bench.ts │ │ ├── index.ts │ │ ├── mcp-tools.bench.ts │ │ ├── mcp-tools.bench.ts.disabled │ │ ├── mcp-tools.bench.ts.skip │ │ ├── node-loading.bench.ts.disabled │ │ ├── README.md │ │ ├── search-operations.bench.ts.disabled │ │ └── validation-performance.bench.ts.disabled │ ├── bridge.test.ts │ ├── comprehensive-extraction-test.js │ ├── data │ │ └── .gitkeep │ ├── debug-slack-doc.js │ ├── demo-enhanced-documentation.js │ ├── docker-tests-README.md │ ├── error-handler.test.ts │ ├── examples │ │ └── using-database-utils.test.ts │ ├── extracted-nodes-db │ │ ├── database-import.json │ │ ├── extraction-report.json │ │ ├── insert-nodes.sql │ │ ├── n8n-nodes-base__Airtable.json │ │ ├── n8n-nodes-base__Discord.json │ │ ├── n8n-nodes-base__Function.json │ │ ├── n8n-nodes-base__HttpRequest.json │ │ ├── n8n-nodes-base__If.json │ │ ├── n8n-nodes-base__Slack.json │ │ ├── n8n-nodes-base__SplitInBatches.json │ │ └── n8n-nodes-base__Webhook.json │ ├── factories │ │ ├── node-factory.ts │ │ └── property-definition-factory.ts │ ├── fixtures │ │ ├── .gitkeep │ │ ├── database │ │ │ └── test-nodes.json │ │ ├── factories │ │ │ ├── node.factory.ts │ │ │ └── parser-node.factory.ts │ │ └── template-configs.ts │ ├── helpers │ │ └── env-helpers.ts │ ├── http-server-auth.test.ts │ ├── integration │ │ ├── ai-validation │ │ │ ├── ai-agent-validation.test.ts │ │ │ ├── ai-tool-validation.test.ts │ │ │ ├── chat-trigger-validation.test.ts │ │ │ ├── e2e-validation.test.ts │ │ │ ├── helpers.ts │ │ │ ├── llm-chain-validation.test.ts │ │ │ ├── README.md │ │ │ └── TEST_REPORT.md │ │ ├── ci │ │ │ └── database-population.test.ts │ │ ├── database │ │ │ ├── connection-management.test.ts │ │ │ ├── empty-database.test.ts │ │ │ ├── fts5-search.test.ts │ │ │ ├── node-fts5-search.test.ts │ │ │ ├── node-repository.test.ts │ │ │ ├── performance.test.ts │ │ │ ├── template-node-configs.test.ts │ │ │ ├── template-repository.test.ts │ │ │ ├── test-utils.ts │ │ │ └── transactions.test.ts │ │ ├── database-integration.test.ts │ │ ├── docker │ │ │ ├── docker-config.test.ts │ │ │ ├── docker-entrypoint.test.ts │ │ │ └── test-helpers.ts │ │ ├── flexible-instance-config.test.ts │ │ ├── mcp │ │ │ └── template-examples-e2e.test.ts │ │ ├── mcp-protocol │ │ │ ├── basic-connection.test.ts │ │ │ ├── error-handling.test.ts │ │ │ ├── performance.test.ts │ │ │ ├── protocol-compliance.test.ts │ │ │ ├── README.md │ │ │ ├── session-management.test.ts │ │ │ ├── test-helpers.ts │ │ │ ├── tool-invocation.test.ts │ │ │ └── workflow-error-validation.test.ts │ │ ├── msw-setup.test.ts │ │ ├── n8n-api │ │ │ ├── executions │ │ │ │ ├── delete-execution.test.ts │ │ │ │ ├── get-execution.test.ts │ │ │ │ ├── list-executions.test.ts │ │ │ │ └── trigger-webhook.test.ts │ │ │ ├── scripts │ │ │ │ └── cleanup-orphans.ts │ │ │ ├── system │ │ │ │ ├── diagnostic.test.ts │ │ │ │ ├── health-check.test.ts │ │ │ │ └── list-tools.test.ts │ │ │ ├── test-connection.ts │ │ │ ├── types │ │ │ │ └── mcp-responses.ts │ │ │ ├── utils │ │ │ │ ├── cleanup-helpers.ts │ │ │ │ ├── credentials.ts │ │ │ │ ├── factories.ts │ │ │ │ ├── fixtures.ts │ │ │ │ ├── mcp-context.ts │ │ │ │ ├── n8n-client.ts │ │ │ │ ├── node-repository.ts │ │ │ │ ├── response-types.ts │ │ │ │ ├── test-context.ts │ │ │ │ └── webhook-workflows.ts │ │ │ └── workflows │ │ │ ├── autofix-workflow.test.ts │ │ │ ├── create-workflow.test.ts │ │ │ ├── delete-workflow.test.ts │ │ │ ├── get-workflow-details.test.ts │ │ │ ├── get-workflow-minimal.test.ts │ │ │ ├── get-workflow-structure.test.ts │ │ │ ├── get-workflow.test.ts │ │ │ ├── list-workflows.test.ts │ │ │ ├── smart-parameters.test.ts │ │ │ ├── update-partial-workflow.test.ts │ │ │ ├── update-workflow.test.ts │ │ │ └── validate-workflow.test.ts │ │ ├── security │ │ │ ├── command-injection-prevention.test.ts │ │ │ └── rate-limiting.test.ts │ │ ├── setup │ │ │ ├── integration-setup.ts │ │ │ └── msw-test-server.ts │ │ ├── telemetry │ │ │ ├── docker-user-id-stability.test.ts │ │ │ └── mcp-telemetry.test.ts │ │ ├── templates │ │ │ └── metadata-operations.test.ts │ │ └── workflow-creation-node-type-format.test.ts │ ├── logger.test.ts │ ├── MOCKING_STRATEGY.md │ ├── mocks │ │ ├── n8n-api │ │ │ ├── data │ │ │ │ ├── credentials.ts │ │ │ │ ├── executions.ts │ │ │ │ └── workflows.ts │ │ │ ├── handlers.ts │ │ │ └── index.ts │ │ └── README.md │ ├── node-storage-export.json │ ├── setup │ │ ├── global-setup.ts │ │ ├── msw-setup.ts │ │ ├── TEST_ENV_DOCUMENTATION.md │ │ └── test-env.ts │ ├── test-database-extraction.js │ ├── test-direct-extraction.js │ ├── test-enhanced-documentation.js │ ├── test-enhanced-integration.js │ ├── test-mcp-extraction.js │ ├── test-mcp-server-extraction.js │ ├── test-mcp-tools-integration.js │ ├── test-node-documentation-service.js │ ├── test-node-list.js │ ├── test-package-info.js │ ├── test-parsing-operations.js │ ├── test-slack-node-complete.js │ ├── test-small-rebuild.js │ ├── test-sqlite-search.js │ ├── test-storage-system.js │ ├── unit │ │ ├── __mocks__ │ │ │ ├── n8n-nodes-base.test.ts │ │ │ ├── n8n-nodes-base.ts │ │ │ └── README.md │ │ ├── database │ │ │ ├── __mocks__ │ │ │ │ └── better-sqlite3.ts │ │ │ ├── database-adapter-unit.test.ts │ │ │ ├── node-repository-core.test.ts │ │ │ ├── node-repository-operations.test.ts │ │ │ ├── node-repository-outputs.test.ts │ │ │ ├── README.md │ │ │ └── template-repository-core.test.ts │ │ ├── docker │ │ │ ├── config-security.test.ts │ │ │ ├── edge-cases.test.ts │ │ │ ├── parse-config.test.ts │ │ │ └── serve-command.test.ts │ │ ├── errors │ │ │ └── validation-service-error.test.ts │ │ ├── examples │ │ │ └── using-n8n-nodes-base-mock.test.ts │ │ ├── flexible-instance-security-advanced.test.ts │ │ ├── flexible-instance-security.test.ts │ │ ├── http-server │ │ │ └── multi-tenant-support.test.ts │ │ ├── http-server-n8n-mode.test.ts │ │ ├── http-server-n8n-reinit.test.ts │ │ ├── http-server-session-management.test.ts │ │ ├── loaders │ │ │ └── node-loader.test.ts │ │ ├── mappers │ │ │ └── docs-mapper.test.ts │ │ ├── mcp │ │ │ ├── get-node-essentials-examples.test.ts │ │ │ ├── handlers-n8n-manager-simple.test.ts │ │ │ ├── handlers-n8n-manager.test.ts │ │ │ ├── handlers-workflow-diff.test.ts │ │ │ ├── lru-cache-behavior.test.ts │ │ │ ├── multi-tenant-tool-listing.test.ts.disabled │ │ │ ├── parameter-validation.test.ts │ │ │ ├── search-nodes-examples.test.ts │ │ │ ├── tools-documentation.test.ts │ │ │ └── tools.test.ts │ │ ├── monitoring │ │ │ └── cache-metrics.test.ts │ │ ├── MULTI_TENANT_TEST_COVERAGE.md │ │ ├── multi-tenant-integration.test.ts │ │ ├── parsers │ │ │ ├── node-parser-outputs.test.ts │ │ │ ├── node-parser.test.ts │ │ │ ├── property-extractor.test.ts │ │ │ └── simple-parser.test.ts │ │ ├── scripts │ │ │ └── fetch-templates-extraction.test.ts │ │ ├── services │ │ │ ├── ai-node-validator.test.ts │ │ │ ├── ai-tool-validators.test.ts │ │ │ ├── confidence-scorer.test.ts │ │ │ ├── config-validator-basic.test.ts │ │ │ ├── config-validator-edge-cases.test.ts │ │ │ ├── config-validator-node-specific.test.ts │ │ │ ├── config-validator-security.test.ts │ │ │ ├── debug-validator.test.ts │ │ │ ├── enhanced-config-validator-integration.test.ts │ │ │ ├── enhanced-config-validator-operations.test.ts │ │ │ ├── enhanced-config-validator.test.ts │ │ │ ├── example-generator.test.ts │ │ │ ├── execution-processor.test.ts │ │ │ ├── expression-format-validator.test.ts │ │ │ ├── expression-validator-edge-cases.test.ts │ │ │ ├── expression-validator.test.ts │ │ │ ├── fixed-collection-validation.test.ts │ │ │ ├── loop-output-edge-cases.test.ts │ │ │ ├── n8n-api-client.test.ts │ │ │ ├── n8n-validation.test.ts │ │ │ ├── node-similarity-service.test.ts │ │ │ ├── node-specific-validators.test.ts │ │ │ ├── operation-similarity-service-comprehensive.test.ts │ │ │ ├── operation-similarity-service.test.ts │ │ │ ├── property-dependencies.test.ts │ │ │ ├── property-filter-edge-cases.test.ts │ │ │ ├── property-filter.test.ts │ │ │ ├── resource-similarity-service-comprehensive.test.ts │ │ │ ├── resource-similarity-service.test.ts │ │ │ ├── task-templates.test.ts │ │ │ ├── template-service.test.ts │ │ │ ├── universal-expression-validator.test.ts │ │ │ ├── validation-fixes.test.ts │ │ │ ├── workflow-auto-fixer.test.ts │ │ │ ├── workflow-diff-engine.test.ts │ │ │ ├── workflow-fixed-collection-validation.test.ts │ │ │ ├── workflow-validator-comprehensive.test.ts │ │ │ ├── workflow-validator-edge-cases.test.ts │ │ │ ├── workflow-validator-error-outputs.test.ts │ │ │ ├── workflow-validator-expression-format.test.ts │ │ │ ├── workflow-validator-loops-simple.test.ts │ │ │ ├── workflow-validator-loops.test.ts │ │ │ ├── workflow-validator-mocks.test.ts │ │ │ ├── workflow-validator-performance.test.ts │ │ │ ├── workflow-validator-with-mocks.test.ts │ │ │ └── workflow-validator.test.ts │ │ ├── telemetry │ │ │ ├── batch-processor.test.ts │ │ │ ├── config-manager.test.ts │ │ │ ├── event-tracker.test.ts │ │ │ ├── event-validator.test.ts │ │ │ ├── rate-limiter.test.ts │ │ │ ├── telemetry-error.test.ts │ │ │ ├── telemetry-manager.test.ts │ │ │ ├── v2.18.3-fixes-verification.test.ts │ │ │ └── workflow-sanitizer.test.ts │ │ ├── templates │ │ │ ├── batch-processor.test.ts │ │ │ ├── metadata-generator.test.ts │ │ │ ├── template-repository-metadata.test.ts │ │ │ └── template-repository-security.test.ts │ │ ├── test-env-example.test.ts │ │ ├── test-infrastructure.test.ts │ │ ├── types │ │ │ ├── instance-context-coverage.test.ts │ │ │ └── instance-context-multi-tenant.test.ts │ │ ├── utils │ │ │ ├── auth-timing-safe.test.ts │ │ │ ├── cache-utils.test.ts │ │ │ ├── console-manager.test.ts │ │ │ ├── database-utils.test.ts │ │ │ ├── fixed-collection-validator.test.ts │ │ │ ├── n8n-errors.test.ts │ │ │ ├── node-type-normalizer.test.ts │ │ │ ├── node-type-utils.test.ts │ │ │ ├── node-utils.test.ts │ │ │ ├── simple-cache-memory-leak-fix.test.ts │ │ │ ├── ssrf-protection.test.ts │ │ │ └── template-node-resolver.test.ts │ │ └── validation-fixes.test.ts │ └── utils │ ├── assertions.ts │ ├── builders │ │ └── workflow.builder.ts │ ├── data-generators.ts │ ├── database-utils.ts │ ├── README.md │ └── test-helpers.ts ├── thumbnail.png ├── tsconfig.build.json ├── tsconfig.json ├── types │ ├── mcp.d.ts │ └── test-env.d.ts ├── verify-telemetry-fix.js ├── versioned-nodes.md ├── vitest.config.benchmark.ts ├── vitest.config.integration.ts └── vitest.config.ts ``` # Files -------------------------------------------------------------------------------- /docs/local/P0_IMPLEMENTATION_PLAN.md: -------------------------------------------------------------------------------- ```markdown 1 | # P0 Priorities Implementation Plan 2 | ## Critical Fixes for n8n-mcp Based on Production Telemetry Data 3 | 4 | **Date:** October 2, 2025 5 | **Analysis Period:** September 26 - October 2, 2025 6 | **Data Volume:** 212,375 events | 5,751 workflows | 2,119 users 7 | **Target:** Reduce error rate from 5-10% to <2% 8 | 9 | --- 10 | 11 | ## Executive Summary 12 | 13 | This document provides a comprehensive implementation plan for the three P0 (Priority 0 - Critical) issues identified through deep analysis of production telemetry data. These fixes will eliminate **80% of all validation errors** and significantly improve the AI agent experience. 14 | 15 | ### Impact Summary 16 | 17 | | Issue | Current Failure Rate | Post-Fix Target | Affected Users | Estimated Effort | 18 | |-------|---------------------|-----------------|----------------|------------------| 19 | | **P0-R1**: Node Type Prefix Normalization | 80% of validation errors | <1% | Hundreds | 2 days | 20 | | **P0-R2**: Null-Safety Audit | 10-18% TypeError rate | <1% | 30+ | 2 days | 21 | | **P0-R3**: Pre-extract Template Configs + Remove get_node_for_task | 28% failure rate, 5.9% coverage | N/A (tool removed), 100% coverage | 197 (migrated) | 5 days | 22 | 23 | **Total Effort:** 2 weeks (v2.15.0 release) 24 | 25 | --- 26 | 27 | ## Table of Contents 28 | 29 | 1. [P0-R1: Auto-Normalize Node Type Prefixes](#p0-r1-auto-normalize-node-type-prefixes) 30 | 2. [P0-R2: Complete Null-Safety Audit](#p0-r2-complete-null-safety-audit) 31 | 3. [P0-R3: Pre-extract Template Configurations + Remove get_node_for_task](#p0-r3-pre-extract-template-configurations--remove-get_node_for_task) 32 | 4. [Implementation Order & Timeline](#implementation-order--timeline) 33 | 5. [Testing Strategy](#testing-strategy) 34 | 6. [Rollback Plan](#rollback-plan) 35 | 7. [Success Metrics](#success-metrics) 36 | 37 | --- 38 | 39 | ## P0-R1: Auto-Normalize Node Type Prefixes 40 | 41 | ### Problem Statement 42 | 43 | **Impact:** 4,800+ validation errors (80% of all validation errors) from a single root cause 44 | 45 | AI agents frequently produce `nodes-base.X` instead of `n8n-nodes-base.X`, causing validation failures. This is the single largest source of user frustration. 46 | 47 | **Example Error:** 48 | ``` 49 | Error: Invalid node type: "nodes-base.set". Use "n8n-nodes-base.set" instead. 50 | ``` 51 | 52 | ### Root Cause Analysis 53 | 54 | **Current Implementation Issues:** 55 | 56 | 1. **Existing normalization is BACKWARD:** 57 | - `src/utils/node-type-utils.ts` normalizes TO short form (`nodes-base.`) 58 | - But validation expects full form (`n8n-nodes-base.`) 59 | - This is the **opposite** of what we need 60 | 61 | 2. **Location of the bug:** 62 | ```typescript 63 | // src/utils/node-type-utils.ts:18-20 64 | return type 65 | .replace(/^n8n-nodes-base\./, 'nodes-base.') // ❌ WRONG DIRECTION 66 | .replace(/^@n8n\/n8n-nodes-langchain\./, 'nodes-langchain.'); 67 | ``` 68 | 69 | 3. **Why AI agents produce short form:** 70 | - Token efficiency (LLMs abbreviate to save tokens) 71 | - Pattern learning from examples 72 | - Natural language preference for concise names 73 | 74 | ### Solution Architecture 75 | 76 | **Strategy:** Normalize ALL node types to FULL form before validation 77 | 78 | #### 1. Create Universal Node Type Normalizer 79 | 80 | **File:** `src/utils/node-type-normalizer.ts` (NEW) 81 | 82 | ```typescript 83 | /** 84 | * Universal Node Type Normalizer 85 | * 86 | * Converts ANY node type variation to the canonical full form expected by n8n 87 | * 88 | * Handles: 89 | * - Short form → Full form (nodes-base.X → n8n-nodes-base.X) 90 | * - Already full form → Unchanged 91 | * - LangChain nodes → Proper @n8n/ prefix 92 | */ 93 | 94 | export interface NodeTypeNormalizationResult { 95 | original: string; 96 | normalized: string; 97 | wasNormalized: boolean; 98 | package: 'base' | 'langchain' | 'community' | 'unknown'; 99 | } 100 | 101 | export class NodeTypeNormalizer { 102 | 103 | /** 104 | * Normalize node type to canonical full form 105 | * 106 | * @example 107 | * normalizeToFullForm('nodes-base.webhook') 108 | * // → 'n8n-nodes-base.webhook' 109 | * 110 | * normalizeToFullForm('n8n-nodes-base.webhook') 111 | * // → 'n8n-nodes-base.webhook' (unchanged) 112 | * 113 | * normalizeToFullForm('nodes-langchain.agent') 114 | * // → '@n8n/n8n-nodes-langchain.agent' 115 | */ 116 | static normalizeToFullForm(type: string): string { 117 | if (!type || typeof type !== 'string') { 118 | return type; 119 | } 120 | 121 | // Already in full form - return unchanged 122 | if (type.startsWith('n8n-nodes-base.')) { 123 | return type; 124 | } 125 | if (type.startsWith('@n8n/n8n-nodes-langchain.')) { 126 | return type; 127 | } 128 | 129 | // Normalize short forms to full form 130 | if (type.startsWith('nodes-base.')) { 131 | return type.replace(/^nodes-base\./, 'n8n-nodes-base.'); 132 | } 133 | if (type.startsWith('nodes-langchain.')) { 134 | return type.replace(/^nodes-langchain\./, '@n8n/n8n-nodes-langchain.'); 135 | } 136 | if (type.startsWith('n8n-nodes-langchain.')) { 137 | return type.replace(/^n8n-nodes-langchain\./, '@n8n/n8n-nodes-langchain.'); 138 | } 139 | 140 | // No prefix - might be community node or error 141 | return type; 142 | } 143 | 144 | /** 145 | * Normalize with detailed result 146 | */ 147 | static normalizeWithDetails(type: string): NodeTypeNormalizationResult { 148 | const original = type; 149 | const normalized = this.normalizeToFullForm(type); 150 | 151 | return { 152 | original, 153 | normalized, 154 | wasNormalized: original !== normalized, 155 | package: this.detectPackage(normalized) 156 | }; 157 | } 158 | 159 | /** 160 | * Detect package type from node type 161 | */ 162 | private static detectPackage(type: string): 'base' | 'langchain' | 'community' | 'unknown' { 163 | if (type.startsWith('n8n-nodes-base.')) return 'base'; 164 | if (type.startsWith('@n8n/n8n-nodes-langchain.')) return 'langchain'; 165 | if (type.includes('.')) return 'community'; 166 | return 'unknown'; 167 | } 168 | 169 | /** 170 | * Batch normalize multiple node types 171 | */ 172 | static normalizeBatch(types: string[]): Map<string, string> { 173 | const result = new Map<string, string>(); 174 | for (const type of types) { 175 | result.set(type, this.normalizeToFullForm(type)); 176 | } 177 | return result; 178 | } 179 | 180 | /** 181 | * Normalize all node types in a workflow 182 | */ 183 | static normalizeWorkflowNodeTypes(workflow: any): any { 184 | if (!workflow?.nodes || !Array.isArray(workflow.nodes)) { 185 | return workflow; 186 | } 187 | 188 | return { 189 | ...workflow, 190 | nodes: workflow.nodes.map((node: any) => ({ 191 | ...node, 192 | type: this.normalizeToFullForm(node.type) 193 | })) 194 | }; 195 | } 196 | } 197 | ``` 198 | 199 | #### 2. Apply Normalization in All Entry Points 200 | 201 | **File:** `src/services/workflow-validator.ts` 202 | 203 | **Change at line 250:** (validateWorkflowStructure method) 204 | 205 | ```typescript 206 | // BEFORE (line 250-252): 207 | const normalizedType = normalizeNodeType(singleNode.type); 208 | const isWebhook = normalizedType === 'nodes-base.webhook' || 209 | normalizedType === 'nodes-base.webhookTrigger'; 210 | 211 | // AFTER: 212 | import { NodeTypeNormalizer } from '../utils/node-type-normalizer'; 213 | 214 | const normalizedType = NodeTypeNormalizer.normalizeToFullForm(singleNode.type); 215 | const isWebhook = normalizedType === 'n8n-nodes-base.webhook' || 216 | normalizedType === 'n8n-nodes-base.webhookTrigger'; 217 | ``` 218 | 219 | **Change at line 368-376:** (validateAllNodes method) 220 | 221 | ```typescript 222 | // BEFORE: 223 | // Get node definition - try multiple formats 224 | let nodeInfo = this.nodeRepository.getNode(node.type); 225 | 226 | // If not found, try with normalized type 227 | if (!nodeInfo) { 228 | const normalizedType = normalizeNodeType(node.type); 229 | if (normalizedType !== node.type) { 230 | nodeInfo = this.nodeRepository.getNode(normalizedType); 231 | } 232 | } 233 | 234 | // AFTER: 235 | // Normalize node type FIRST 236 | const normalizedType = NodeTypeNormalizer.normalizeToFullForm(node.type); 237 | const nodeInfo = this.nodeRepository.getNode(normalizedType); 238 | 239 | // Update node type in place if normalized 240 | if (normalizedType !== node.type) { 241 | node.type = normalizedType; 242 | } 243 | ``` 244 | 245 | **File:** `src/mcp/handlers-n8n-manager.ts` 246 | 247 | **Add normalization in handleCreateWorkflow (line 281-310):** 248 | 249 | ```typescript 250 | // BEFORE validation: 251 | const input = createWorkflowSchema.parse(args); 252 | 253 | // AFTER: Add normalization 254 | const input = createWorkflowSchema.parse(args); 255 | 256 | // Normalize all node types before validation 257 | const normalizedInput = NodeTypeNormalizer.normalizeWorkflowNodeTypes(input); 258 | 259 | // Validate workflow structure 260 | const errors = validateWorkflowStructure(normalizedInput); 261 | ``` 262 | 263 | **Apply same pattern to:** 264 | - `handleUpdateWorkflow` (line 520) 265 | - `validateWorkflow` tool handler 266 | - Any other workflow creation/update entry points 267 | 268 | #### 3. Update Node Repository for Flexible Lookups 269 | 270 | **File:** `src/database/node-repository.ts` 271 | 272 | **Enhance getNode method (line 54):** 273 | 274 | ```typescript 275 | /** 276 | * Get node with automatic type normalization 277 | */ 278 | getNode(nodeType: string): any { 279 | // Try normalized type first 280 | const normalizedType = NodeTypeNormalizer.normalizeToFullForm(nodeType); 281 | 282 | const row = this.db.prepare(` 283 | SELECT * FROM nodes WHERE node_type = ? 284 | `).get(normalizedType) as any; 285 | 286 | if (!row) { 287 | // Fallback: try original type if normalization didn't help 288 | if (normalizedType !== nodeType) { 289 | const originalRow = this.db.prepare(` 290 | SELECT * FROM nodes WHERE node_type = ? 291 | `).get(nodeType) as any; 292 | 293 | if (originalRow) return this.parseNodeRow(originalRow); 294 | } 295 | return null; 296 | } 297 | 298 | return this.parseNodeRow(row); 299 | } 300 | ``` 301 | 302 | ### Testing Requirements 303 | 304 | **File:** `tests/unit/utils/node-type-normalizer.test.ts` (NEW) 305 | 306 | ```typescript 307 | describe('NodeTypeNormalizer', () => { 308 | describe('normalizeToFullForm', () => { 309 | it('should normalize short base form to full form', () => { 310 | expect(NodeTypeNormalizer.normalizeToFullForm('nodes-base.webhook')) 311 | .toBe('n8n-nodes-base.webhook'); 312 | }); 313 | 314 | it('should normalize short langchain form to full form', () => { 315 | expect(NodeTypeNormalizer.normalizeToFullForm('nodes-langchain.agent')) 316 | .toBe('@n8n/n8n-nodes-langchain.agent'); 317 | }); 318 | 319 | it('should leave full forms unchanged', () => { 320 | expect(NodeTypeNormalizer.normalizeToFullForm('n8n-nodes-base.webhook')) 321 | .toBe('n8n-nodes-base.webhook'); 322 | }); 323 | 324 | it('should handle edge cases', () => { 325 | expect(NodeTypeNormalizer.normalizeToFullForm('')).toBe(''); 326 | expect(NodeTypeNormalizer.normalizeToFullForm(null as any)).toBe(null); 327 | }); 328 | }); 329 | 330 | describe('normalizeWorkflowNodeTypes', () => { 331 | it('should normalize all nodes in workflow', () => { 332 | const workflow = { 333 | nodes: [ 334 | { type: 'nodes-base.webhook', id: '1', name: 'Webhook' }, 335 | { type: 'nodes-base.set', id: '2', name: 'Set' } 336 | ], 337 | connections: {} 338 | }; 339 | 340 | const result = NodeTypeNormalizer.normalizeWorkflowNodeTypes(workflow); 341 | 342 | expect(result.nodes[0].type).toBe('n8n-nodes-base.webhook'); 343 | expect(result.nodes[1].type).toBe('n8n-nodes-base.set'); 344 | }); 345 | }); 346 | }); 347 | ``` 348 | 349 | ### Success Criteria 350 | 351 | - [x] All workflow validation tests pass with both short and full node type forms 352 | - [x] 0 "Invalid node type" errors for variations of core nodes 353 | - [x] Telemetry shows <1% validation errors related to node type prefixes 354 | - [x] No breaking changes to existing workflows 355 | 356 | **Status:** ✅ COMPLETED (October 2, 2025) 357 | **Commit:** ed7de10 358 | 359 | ### Estimated Effort 360 | 361 | **Total: 2-4 hours** 362 | 363 | - Implementation: 1-2 hours 364 | - Testing: 1 hour 365 | - Documentation: 30 minutes 366 | - Code review: 30 minutes 367 | 368 | --- 369 | 370 | ## P0-R2: Complete Null-Safety Audit 371 | 372 | ### Problem Statement 373 | 374 | **Impact:** 10-18% TypeError failures in node information tools affecting 1,000+ calls 375 | 376 | ``` 377 | TypeError: Cannot read property 'text' of undefined 378 | ``` 379 | 380 | **Affected Tools:** 381 | - `get_node_essentials`: 483 failures (10% of 4,909 calls) 382 | - `get_node_info`: 352 failures (18% of 1,988 calls) 383 | - `get_node_documentation`: 136 failures (7% of 1,919 calls) 384 | 385 | ### Root Cause Analysis 386 | 387 | **From CHANGELOG 2.14.0:** 388 | > "Fixed TypeErrors in get_node_info, get_node_essentials, and get_node_documentation tools" 389 | > "Added null safety checks for undefined node properties" 390 | 391 | **The fix was incomplete.** Residual issues remain in: 392 | 393 | 1. Nested property access without guards 394 | 2. Edge cases with unusual/legacy node structures 395 | 3. Missing properties in database 396 | 4. Assumptions about property structure 397 | 398 | ### Current Implementation Analysis 399 | 400 | **File:** `src/database/node-repository.ts` 401 | 402 | **Problem areas identified:** 403 | 404 | ```typescript 405 | // Line 73-78: Good - has safeJsonParse 406 | properties: this.safeJsonParse(row.properties_schema, []), 407 | operations: this.safeJsonParse(row.operations, []), 408 | credentials: this.safeJsonParse(row.credentials_required, []), 409 | 410 | // But doesn't protect against: 411 | // - properties being null after parse 412 | // - Nested properties like properties[0].description.text 413 | // - Missing fields in properties array 414 | ``` 415 | 416 | **handlers for get_node_essentials/info need to be found and audited** 417 | 418 | ### Solution Architecture 419 | 420 | #### 1. Enhanced Safe Property Access Utilities 421 | 422 | **File:** `src/utils/safe-property-access.ts` (NEW) 423 | 424 | ```typescript 425 | /** 426 | * Safe Property Access Utilities 427 | * 428 | * Provides defensive property access with fallbacks 429 | */ 430 | 431 | export class SafePropertyAccess { 432 | /** 433 | * Safely get nested property with default 434 | */ 435 | static get<T>(obj: any, path: string, defaultValue: T): T { 436 | if (!obj || typeof obj !== 'object') return defaultValue; 437 | 438 | const keys = path.split('.'); 439 | let current = obj; 440 | 441 | for (const key of keys) { 442 | if (current === null || current === undefined) { 443 | return defaultValue; 444 | } 445 | if (typeof current !== 'object') { 446 | return defaultValue; 447 | } 448 | current = current[key]; 449 | } 450 | 451 | return current !== undefined ? current : defaultValue; 452 | } 453 | 454 | /** 455 | * Safely get array with default 456 | */ 457 | static getArray<T>(obj: any, path: string, defaultValue: T[] = []): T[] { 458 | const value = this.get(obj, path, defaultValue); 459 | return Array.isArray(value) ? value : defaultValue; 460 | } 461 | 462 | /** 463 | * Safely get string with default 464 | */ 465 | static getString(obj: any, path: string, defaultValue: string = ''): string { 466 | const value = this.get(obj, path, defaultValue); 467 | return typeof value === 'string' ? value : defaultValue; 468 | } 469 | 470 | /** 471 | * Safely get number with default 472 | */ 473 | static getNumber(obj: any, path: string, defaultValue: number = 0): number { 474 | const value = this.get(obj, path, defaultValue); 475 | return typeof value === 'number' && !isNaN(value) ? value : defaultValue; 476 | } 477 | 478 | /** 479 | * Safely get boolean with default 480 | */ 481 | static getBoolean(obj: any, path: string, defaultValue: boolean = false): boolean { 482 | const value = this.get(obj, path, defaultValue); 483 | return typeof value === 'boolean' ? value : defaultValue; 484 | } 485 | 486 | /** 487 | * Extract description from multiple possible locations 488 | */ 489 | static extractDescription(obj: any): string { 490 | // Try common description locations 491 | const locations = [ 492 | 'description', 493 | 'properties.description', 494 | 'properties.description.text', 495 | 'subtitle', 496 | 'displayName' 497 | ]; 498 | 499 | for (const location of locations) { 500 | const value = this.getString(obj, location); 501 | if (value) return value; 502 | } 503 | 504 | return 'No description available'; 505 | } 506 | 507 | /** 508 | * Extract display name from multiple possible locations 509 | */ 510 | static extractDisplayName(obj: any, fallback: string = 'Unknown'): string { 511 | const locations = [ 512 | 'displayName', 513 | 'name', 514 | 'label', 515 | 'title' 516 | ]; 517 | 518 | for (const location of locations) { 519 | const value = this.getString(obj, location); 520 | if (value) return value; 521 | } 522 | 523 | return fallback; 524 | } 525 | } 526 | ``` 527 | 528 | #### 2. Null-Safe Node Repository Methods 529 | 530 | **File:** `src/database/node-repository.ts` 531 | 532 | **Refactor getNode method (line 54):** 533 | 534 | ```typescript 535 | import { SafePropertyAccess } from '../utils/safe-property-access'; 536 | 537 | /** 538 | * Get node with comprehensive null-safety 539 | */ 540 | getNode(nodeType: string): any | null { 541 | try { 542 | // Normalize type first 543 | const normalizedType = NodeTypeNormalizer.normalizeToFullForm(nodeType); 544 | 545 | const row = this.db.prepare(` 546 | SELECT * FROM nodes WHERE node_type = ? 547 | `).get(normalizedType) as any; 548 | 549 | if (!row) return null; 550 | 551 | // Use safe property access for all fields 552 | return { 553 | nodeType: SafePropertyAccess.getString(row, 'node_type', normalizedType), 554 | displayName: SafePropertyAccess.extractDisplayName(row, 555 | SafePropertyAccess.getString(row, 'display_name', 'Unknown Node')), 556 | description: SafePropertyAccess.extractDescription(row), 557 | category: SafePropertyAccess.getString(row, 'category', 'Uncategorized'), 558 | developmentStyle: SafePropertyAccess.getString(row, 'development_style', 'declarative'), 559 | package: SafePropertyAccess.getString(row, 'package_name', 'unknown'), 560 | isAITool: SafePropertyAccess.getBoolean(row, 'is_ai_tool', false), 561 | isTrigger: SafePropertyAccess.getBoolean(row, 'is_trigger', false), 562 | isWebhook: SafePropertyAccess.getBoolean(row, 'is_webhook', false), 563 | isVersioned: SafePropertyAccess.getBoolean(row, 'is_versioned', false), 564 | version: SafePropertyAccess.getNumber(row, 'version', 1), 565 | properties: this.safeParseProperties(row.properties_schema), 566 | operations: this.safeParseArray(row.operations), 567 | credentials: this.safeParseArray(row.credentials_required), 568 | hasDocumentation: !!row.documentation, 569 | outputs: row.outputs ? this.safeJsonParse(row.outputs, null) : null, 570 | outputNames: row.output_names ? this.safeJsonParse(row.output_names, null) : null 571 | }; 572 | } catch (error) { 573 | console.error(`Error getting node ${nodeType}:`, error); 574 | return null; 575 | } 576 | } 577 | 578 | /** 579 | * Safely parse properties with validation 580 | */ 581 | private safeParseProperties(json: string): any[] { 582 | try { 583 | const parsed = JSON.parse(json); 584 | if (!Array.isArray(parsed)) return []; 585 | 586 | // Validate each property has minimum required fields 587 | return parsed.map(prop => ({ 588 | name: SafePropertyAccess.getString(prop, 'name', 'unknown'), 589 | displayName: SafePropertyAccess.extractDisplayName(prop), 590 | type: SafePropertyAccess.getString(prop, 'type', 'string'), 591 | required: SafePropertyAccess.getBoolean(prop, 'required', false), 592 | default: prop.default !== undefined ? prop.default : null, 593 | description: SafePropertyAccess.extractDescription(prop), 594 | options: SafePropertyAccess.getArray(prop, 'options', []), 595 | displayOptions: prop.displayOptions || null 596 | })); 597 | } catch { 598 | return []; 599 | } 600 | } 601 | 602 | /** 603 | * Safely parse array field 604 | */ 605 | private safeParseArray(json: string): any[] { 606 | try { 607 | const parsed = JSON.parse(json); 608 | return Array.isArray(parsed) ? parsed : []; 609 | } catch { 610 | return []; 611 | } 612 | } 613 | ``` 614 | 615 | #### 3. Find and Fix Handler Functions 616 | 617 | **Action Required:** Search for handler functions that call getNode and add null checks 618 | 619 | **Pattern to search for:** 620 | ```bash 621 | grep -r "getNode\|getNodeEssentials\|getNodeInfo" src/mcp/ --include="*.ts" 622 | ``` 623 | 624 | **Add null checks like:** 625 | ```typescript 626 | const node = repository.getNode(nodeType); 627 | if (!node) { 628 | return { 629 | success: false, 630 | error: `Node type "${nodeType}" not found. Use search_nodes to find available nodes.` 631 | }; 632 | } 633 | ``` 634 | 635 | ### Testing Requirements 636 | 637 | **File:** `tests/unit/database/node-repository-null-safety.test.ts` (NEW) 638 | 639 | ```typescript 640 | describe('NodeRepository - Null Safety', () => { 641 | it('should handle node with missing description', () => { 642 | // Insert node with minimal data 643 | const node = { type: 'test.node', name: 'Test' }; 644 | db.prepare('INSERT INTO nodes (node_type, display_name) VALUES (?, ?)').run(node.type, node.name); 645 | 646 | const result = repository.getNode('test.node'); 647 | expect(result).not.toBeNull(); 648 | expect(result.description).toBe('No description available'); 649 | expect(result.properties).toEqual([]); 650 | }); 651 | 652 | it('should handle node with malformed JSON', () => { 653 | db.prepare('INSERT INTO nodes (node_type, properties_schema) VALUES (?, ?)').run('test.node', 'invalid json'); 654 | 655 | const result = repository.getNode('test.node'); 656 | expect(result).not.toBeNull(); 657 | expect(result.properties).toEqual([]); 658 | }); 659 | 660 | it('should handle non-existent node gracefully', () => { 661 | const result = repository.getNode('non.existent'); 662 | expect(result).toBeNull(); 663 | }); 664 | 665 | it('should handle null database row', () => { 666 | // Simulate database returning null 667 | const result = repository.getNode('null.node'); 668 | expect(result).toBeNull(); 669 | }); 670 | }); 671 | ``` 672 | 673 | ### Success Criteria 674 | 675 | - [ ] get_node_essentials failure rate: 10% → <1% 676 | - [ ] get_node_info failure rate: 18% → <1% 677 | - [ ] get_node_documentation failure rate: 7% → <1% 678 | - [ ] 100% test coverage for null cases 679 | - [ ] No TypeErrors in production logs 680 | 681 | ### Estimated Effort 682 | 683 | **Total: 1 day (8 hours)** 684 | 685 | - Safe property access utility: 2 hours 686 | - Repository refactoring: 3 hours 687 | - Handler updates: 2 hours 688 | - Testing: 1 hour 689 | 690 | --- 691 | 692 | ## P0-R3: Pre-extract Template Configurations + Remove get_node_for_task 693 | 694 | ### Problem Statement 695 | 696 | **Impact:** 28% failure rate (worst-performing tool) + redundant with better alternatives 697 | 698 | `get_node_for_task` failing 109 times out of 392 calls (27.8%) 699 | 700 | **Current State:** 701 | - Only 31 predefined tasks in `task-templates.ts` (5.9% node coverage) 702 | - 22.5:1 usage ratio favoring `search_nodes` (8,839 calls vs 392) 703 | - Hardcoded configurations require manual maintenance 704 | - Tool provides no unique value over `search_nodes` 705 | 706 | **Discovery:** We have 2,646 real production workflow templates from n8n.io with: 707 | - 3,820 httpRequest configurations 708 | - 1,700 googleSheets configurations 709 | - 466 webhook configurations 710 | - 100% AI-generated metadata coverage 711 | - Real-world best practices and patterns 712 | 713 | ### Architectural Decision: Pre-extraction 714 | 715 | **Analysis:** On-the-fly vs Pre-extraction (see `/docs/local/TEMPLATE_MINING_ANALYSIS.md`) 716 | 717 | **Decision:** Pre-extract node configurations into separate table 718 | 719 | **Rationale:** 720 | - **Performance:** 1ms vs 30-60ms (30-60x faster) 721 | - **Storage:** Only 513 KB for 2,625 configs (negligible) 722 | - **Simplicity:** No cache management, TTL, or eviction logic 723 | - **Features:** Enables filtering by complexity, auth (indexed queries) 724 | - **Scalability:** Handles 10,000+ templates without degradation 725 | - **Predictability:** Consistent sub-millisecond response times 726 | 727 | **Trade-offs (acceptable):** 728 | - +30-60 seconds rebuild time (rare operation) 729 | - Incremental updates needed when templates change 730 | 731 | ### Solution Architecture 732 | 733 | **Strategy:** 734 | 1. Pre-extract top 10 node configurations per node type into new table 735 | 2. Enhance `get_node_essentials` with optional examples 736 | 3. Enhance `search_nodes` with optional examples 737 | 4. **Remove** `get_node_for_task` entirely (no redirect) 738 | 739 | See `/docs/local/TEMPLATE_MINING_ANALYSIS.md` for complete analysis 740 | 741 | #### 1. Add Database Schema for Pre-extracted Configurations 742 | 743 | **File:** `src/database/schema.sql` 744 | 745 | Add new table after `templates` table: 746 | 747 | ```sql 748 | -- Pre-extracted node configurations from templates 749 | CREATE TABLE template_node_configs ( 750 | id INTEGER PRIMARY KEY, 751 | node_type TEXT NOT NULL, 752 | template_id INTEGER NOT NULL, 753 | template_name TEXT NOT NULL, 754 | template_views INTEGER DEFAULT 0, 755 | 756 | -- Node configuration (extracted from workflow) 757 | node_name TEXT, -- Node name in workflow (e.g., "HTTP Request") 758 | parameters_json TEXT NOT NULL, -- JSON: node.parameters 759 | credentials_json TEXT, -- JSON: node.credentials (if present) 760 | 761 | -- Pre-calculated metadata for filtering 762 | has_credentials INTEGER DEFAULT 0, 763 | has_expressions INTEGER DEFAULT 0, -- Contains {{...}} or $json/$node 764 | complexity TEXT CHECK(complexity IN ('simple', 'medium', 'complex')), 765 | use_cases TEXT, -- JSON array from template.metadata.use_cases 766 | 767 | -- Pre-calculated ranking (1 = best, 2 = second best, etc.) 768 | rank INTEGER DEFAULT 0, 769 | 770 | created_at DATETIME DEFAULT CURRENT_TIMESTAMP, 771 | FOREIGN KEY (template_id) REFERENCES templates(id) ON DELETE CASCADE 772 | ); 773 | 774 | -- Indexes for fast queries 775 | CREATE INDEX idx_config_node_type_rank 776 | ON template_node_configs(node_type, rank); 777 | 778 | CREATE INDEX idx_config_complexity 779 | ON template_node_configs(node_type, complexity, rank); 780 | 781 | CREATE INDEX idx_config_auth 782 | ON template_node_configs(node_type, has_credentials, rank); 783 | 784 | -- View for easy querying of top configs 785 | CREATE VIEW ranked_node_configs AS 786 | SELECT 787 | node_type, 788 | template_name, 789 | template_views, 790 | parameters_json, 791 | credentials_json, 792 | has_credentials, 793 | has_expressions, 794 | complexity, 795 | use_cases, 796 | rank 797 | FROM template_node_configs 798 | WHERE rank <= 5 -- Top 5 per node type 799 | ORDER BY node_type, rank; 800 | ``` 801 | 802 | **Migration Script:** `src/database/migrations/add-template-node-configs.sql` 803 | 804 | ```sql 805 | -- Migration for existing databases 806 | -- Run during `npm run rebuild` or `npm run fetch:templates` 807 | 808 | -- Check if table exists 809 | CREATE TABLE IF NOT EXISTS template_node_configs ( 810 | -- ... schema as above 811 | ); 812 | 813 | -- Populate from existing templates 814 | -- (handled by extraction logic in fetch:templates script) 815 | ``` 816 | 817 | #### 2. Add Extraction Logic to fetch:templates Script 818 | 819 | **File:** `src/scripts/fetch-templates.ts` 820 | 821 | Add extraction function: 822 | 823 | ```typescript 824 | import gzip from 'zlib'; 825 | 826 | /** 827 | * Extract node configurations from a template workflow 828 | */ 829 | function extractNodeConfigs( 830 | templateId: number, 831 | templateName: string, 832 | templateViews: number, 833 | workflowCompressed: string, 834 | metadata: any 835 | ): Array<{ 836 | node_type: string; 837 | template_id: number; 838 | template_name: string; 839 | template_views: number; 840 | node_name: string; 841 | parameters_json: string; 842 | credentials_json: string | null; 843 | has_credentials: number; 844 | has_expressions: number; 845 | complexity: string; 846 | use_cases: string; 847 | }> { 848 | try { 849 | // Decompress workflow 850 | const decompressed = gzip.gunzipSync(Buffer.from(workflowCompressed, 'base64')); 851 | const workflow = JSON.parse(decompressed.toString('utf-8')); 852 | 853 | const configs: any[] = []; 854 | 855 | for (const node of workflow.nodes || []) { 856 | // Skip UI-only nodes 857 | if (node.type.includes('stickyNote') || !node.parameters) { 858 | continue; 859 | } 860 | 861 | configs.push({ 862 | node_type: node.type, 863 | template_id: templateId, 864 | template_name: templateName, 865 | template_views: templateViews, 866 | node_name: node.name, 867 | parameters_json: JSON.stringify(node.parameters), 868 | credentials_json: node.credentials ? JSON.stringify(node.credentials) : null, 869 | has_credentials: node.credentials ? 1 : 0, 870 | has_expressions: detectExpressions(node.parameters) ? 1 : 0, 871 | complexity: metadata?.complexity || 'medium', 872 | use_cases: JSON.stringify(metadata?.use_cases || []) 873 | }); 874 | } 875 | 876 | return configs; 877 | } catch (error) { 878 | console.error(`Error extracting configs from template ${templateId}:`, error); 879 | return []; 880 | } 881 | } 882 | 883 | /** 884 | * Detect n8n expressions in parameters 885 | */ 886 | function detectExpressions(params: any): boolean { 887 | const json = JSON.stringify(params); 888 | return json.includes('={{') || json.includes('$json') || json.includes('$node'); 889 | } 890 | 891 | /** 892 | * Insert extracted configs into database and rank them 893 | */ 894 | function insertAndRankConfigs(db: Database, configs: any[]) { 895 | // Clear old configs for these templates 896 | const templateIds = [...new Set(configs.map(c => c.template_id))]; 897 | db.prepare(`DELETE FROM template_node_configs WHERE template_id IN (${templateIds.join(',')})`).run(); 898 | 899 | // Insert new configs 900 | const insertStmt = db.prepare(` 901 | INSERT INTO template_node_configs ( 902 | node_type, template_id, template_name, template_views, 903 | node_name, parameters_json, credentials_json, 904 | has_credentials, has_expressions, complexity, use_cases 905 | ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) 906 | `); 907 | 908 | for (const config of configs) { 909 | insertStmt.run( 910 | config.node_type, 911 | config.template_id, 912 | config.template_name, 913 | config.template_views, 914 | config.node_name, 915 | config.parameters_json, 916 | config.credentials_json, 917 | config.has_credentials, 918 | config.has_expressions, 919 | config.complexity, 920 | config.use_cases 921 | ); 922 | } 923 | 924 | // Rank configs per node_type by template popularity 925 | db.exec(` 926 | UPDATE template_node_configs 927 | SET rank = ( 928 | SELECT COUNT(*) + 1 929 | FROM template_node_configs AS t2 930 | WHERE t2.node_type = template_node_configs.node_type 931 | AND t2.template_views > template_node_configs.template_views 932 | ) 933 | `); 934 | 935 | // Keep only top 10 per node_type 936 | db.exec(` 937 | DELETE FROM template_node_configs 938 | WHERE id NOT IN ( 939 | SELECT id FROM template_node_configs 940 | WHERE rank <= 10 941 | ) 942 | `); 943 | 944 | console.log(`Extracted and ranked ${configs.length} node configurations`); 945 | } 946 | ``` 947 | 948 | #### 3. Enhance get_node_essentials with Examples 949 | 950 | **File:** `src/mcp/handlers-*.ts` or `src/mcp/server.ts` 951 | 952 | Update `get_node_essentials` handler: 953 | 954 | ```typescript 955 | async function getNodeEssentials( 956 | nodeType: string, 957 | options?: { includeExamples?: boolean } 958 | ): Promise<any> { 959 | const node = repository.getNode(nodeType); 960 | if (!node) { 961 | return { 962 | success: false, 963 | error: `Node type "${nodeType}" not found. Use search_nodes to find available nodes.` 964 | }; 965 | } 966 | 967 | const result = { 968 | nodeType, 969 | displayName: node.displayName, 970 | description: node.description, 971 | category: node.category, 972 | // ... existing essentials fields ... 973 | }; 974 | 975 | // NEW: Add real-world examples if requested 976 | if (options?.includeExamples) { 977 | const examples = db.prepare(` 978 | SELECT 979 | parameters_json, 980 | template_name, 981 | template_views, 982 | complexity, 983 | use_cases, 984 | has_credentials, 985 | has_expressions 986 | FROM template_node_configs 987 | WHERE node_type = ? 988 | ORDER BY rank 989 | LIMIT 3 990 | `).all(nodeType); 991 | 992 | result.examples = examples.map(ex => ({ 993 | config: JSON.parse(ex.parameters_json), 994 | source: `${ex.template_name} (${(ex.template_views / 1000).toFixed(0)}k views)`, 995 | complexity: ex.complexity, 996 | useCases: JSON.parse(ex.use_cases).slice(0, 2), 997 | hasAuth: ex.has_credentials === 1, 998 | hasExpressions: ex.has_expressions === 1 999 | })); 1000 | } 1001 | 1002 | return result; 1003 | } 1004 | ``` 1005 | 1006 | **Tool definition update:** 1007 | 1008 | ```typescript 1009 | { 1010 | name: 'get_node_essentials', 1011 | description: 'Get essential information about a specific n8n node type...', 1012 | inputSchema: { 1013 | type: 'object', 1014 | properties: { 1015 | nodeType: { 1016 | type: 'string', 1017 | description: 'Full node type (e.g., "n8n-nodes-base.httpRequest")' 1018 | }, 1019 | includeExamples: { // NEW 1020 | type: 'boolean', 1021 | description: 'Include 2-3 real configuration examples from popular templates', 1022 | default: false 1023 | } 1024 | }, 1025 | required: ['nodeType'] 1026 | } 1027 | } 1028 | ``` 1029 | 1030 | #### 4. Enhance search_nodes with Examples 1031 | 1032 | **File:** `src/mcp/handlers-*.ts` or `src/mcp/server.ts` 1033 | 1034 | Update `search_nodes` handler: 1035 | 1036 | ```typescript 1037 | async function searchNodes( 1038 | query: string, 1039 | options?: { 1040 | limit?: number; 1041 | includeExamples?: boolean; 1042 | } 1043 | ): Promise<any> { 1044 | const nodes = repository.searchNodes(query, 'OR', options?.limit || 20); 1045 | 1046 | const results = nodes.map(node => { 1047 | const result = { 1048 | nodeType: node.nodeType, 1049 | displayName: node.displayName, 1050 | description: node.description, 1051 | category: node.category 1052 | }; 1053 | 1054 | // NEW: Add examples if requested 1055 | if (options?.includeExamples) { 1056 | const examples = db.prepare(` 1057 | SELECT parameters_json, template_name, complexity 1058 | FROM template_node_configs 1059 | WHERE node_type = ? 1060 | ORDER BY rank 1061 | LIMIT 2 1062 | `).all(node.nodeType); 1063 | 1064 | result.examples = examples.map(ex => ({ 1065 | config: JSON.parse(ex.parameters_json), 1066 | source: ex.template_name, 1067 | complexity: ex.complexity 1068 | })); 1069 | } 1070 | 1071 | return result; 1072 | }); 1073 | 1074 | return results; 1075 | } 1076 | ``` 1077 | 1078 | **Tool definition update:** 1079 | 1080 | ```typescript 1081 | { 1082 | name: 'search_nodes', 1083 | description: 'Search for n8n nodes by keyword...', 1084 | inputSchema: { 1085 | type: 'object', 1086 | properties: { 1087 | query: { type: 'string', description: 'Search query' }, 1088 | limit: { type: 'number', default: 20 }, 1089 | includeExamples: { // NEW 1090 | type: 'boolean', 1091 | description: 'Include 2 real configuration examples per node', 1092 | default: false 1093 | } 1094 | }, 1095 | required: ['query'] 1096 | } 1097 | } 1098 | ``` 1099 | 1100 | #### 5. Remove get_node_for_task Tool Entirely 1101 | 1102 | **Files to modify:** 1103 | 1104 | 1. **`src/mcp/server.ts`** - Remove handler function 1105 | 2. **`src/mcp/tools.ts`** - Remove tool definition 1106 | 3. **`src/mcp/tools-documentation.ts`** - Remove from documentation 1107 | 4. **`src/services/task-templates.ts`** - Can be deprecated (keep for now, remove in v2.16.0) 1108 | 5. **`README.md`** - Remove from available tools list 1109 | 6. **`CHANGELOG.md`** - Document removal 1110 | 1111 | **Steps:** 1112 | 1113 | ```bash 1114 | # Search for all references 1115 | grep -r "get_node_for_task" src/ 1116 | grep -r "getNodeForTask" src/ 1117 | 1118 | # Remove handler 1119 | # Remove tool definition 1120 | # Remove from documentation 1121 | # Update README 1122 | ``` 1123 | 1124 | **Migration note for users (add to CHANGELOG):** 1125 | 1126 | ```markdown 1127 | ### BREAKING CHANGES in v2.15.0 1128 | 1129 | - **Removed:** `get_node_for_task` tool 1130 | - **Replacement:** Use `search_nodes` with `includeExamples: true` 1131 | - **Migration:** `get_node_for_task({task: "webhook"})` → `search_nodes({query: "webhook", includeExamples: true})` 1132 | - **Benefit:** Access to 2,646 real templates vs 31 hardcoded tasks 1133 | ``` 1134 | 1135 | ### Testing Requirements 1136 | 1137 | **File:** `tests/unit/services/template-config-extraction.test.ts` (NEW) 1138 | 1139 | ```typescript 1140 | describe('Template Config Extraction', () => { 1141 | it('should extract node configs from workflow', () => { 1142 | const workflow = { 1143 | nodes: [ 1144 | { 1145 | type: 'n8n-nodes-base.httpRequest', 1146 | name: 'HTTP Request', 1147 | parameters: { url: 'https://api.example.com', method: 'GET' } 1148 | } 1149 | ] 1150 | }; 1151 | 1152 | const configs = extractNodeConfigs(1, 'Test', 1000, compressWorkflow(workflow), {}); 1153 | expect(configs).toHaveLength(1); 1154 | expect(configs[0].node_type).toBe('n8n-nodes-base.httpRequest'); 1155 | }); 1156 | 1157 | it('should detect expressions in parameters', () => { 1158 | const params = { url: '={{$json.api_url}}' }; 1159 | expect(detectExpressions(params)).toBe(true); 1160 | }); 1161 | 1162 | it('should rank configs by popularity', () => { 1163 | // Insert configs with different views 1164 | // Verify ranking order 1165 | }); 1166 | }); 1167 | ``` 1168 | 1169 | **File:** `tests/integration/enhanced-tools.test.ts` (NEW) 1170 | 1171 | ```typescript 1172 | describe('Enhanced Tools with Examples', () => { 1173 | it('get_node_essentials should return examples when requested', async () => { 1174 | const result = await getNodeEssentials('n8n-nodes-base.httpRequest', { 1175 | includeExamples: true 1176 | }); 1177 | 1178 | expect(result.examples).toBeDefined(); 1179 | expect(result.examples.length).toBeGreaterThan(0); 1180 | expect(result.examples[0].config).toHaveProperty('url'); 1181 | }); 1182 | 1183 | it('search_nodes should return examples when requested', async () => { 1184 | const result = await searchNodes('webhook', { includeExamples: true }); 1185 | 1186 | expect(result.length).toBeGreaterThan(0); 1187 | expect(result[0].examples).toBeDefined(); 1188 | }); 1189 | 1190 | it('get_node_for_task should not exist', async () => { 1191 | expect(toolRegistry.has('get_node_for_task')).toBe(false); 1192 | }); 1193 | }); 1194 | ``` 1195 | 1196 | ### Success Criteria 1197 | 1198 | - [ ] Extract 2,000+ node configurations from templates 1199 | - [ ] Query performance: <1ms for pre-extracted configs 1200 | - [ ] `get_node_essentials` with examples: <5ms total 1201 | - [ ] `search_nodes` with examples: <10ms total 1202 | - [ ] Database size increase: <1 MB 1203 | - [ ] `get_node_for_task` completely removed from codebase 1204 | - [ ] All documentation updated 1205 | 1206 | ### Estimated Effort 1207 | 1208 | **Total: 1 week (5 days)** 1209 | 1210 | - **Day 1:** Database schema + migration (8 hours) 1211 | - Design schema 1212 | - Create migration script 1213 | - Test with existing database 1214 | 1215 | - **Day 2:** Extraction logic in fetch:templates (8 hours) 1216 | - Write extraction function 1217 | - Write ranking logic 1218 | - Test with 2,646 templates 1219 | 1220 | - **Day 3:** Enhance get_node_essentials + search_nodes (8 hours) 1221 | - Add includeExamples parameter 1222 | - Update tool definitions 1223 | - Integration testing 1224 | 1225 | - **Day 4:** Remove get_node_for_task + documentation (8 hours) 1226 | - Remove from all files 1227 | - Update README, CHANGELOG 1228 | - Update tools_documentation 1229 | - Migration guide 1230 | 1231 | - **Day 5:** Testing + optimization (8 hours) 1232 | - Unit tests 1233 | - Integration tests 1234 | - Performance testing 1235 | - Bug fixes 1236 | 1237 | --- 1238 | 1239 | ## Implementation Order & Timeline 1240 | 1241 | ### Version 2.15.0 - All P0 Fixes in One Release 1242 | 1243 | **Total Timeline:** 2 weeks (10 working days) 1244 | 1245 | ### Week 1: Foundation + P0-R1 + P0-R2 1246 | 1247 | **Monday (Day 1-2): P0-R1 - Node Type Normalization** 1248 | - AM: Create NodeTypeNormalizer utility 1249 | - PM: Apply to workflow validator, handlers, and repository 1250 | - Testing and validation 1251 | - **Deliverable:** 80% of validation errors eliminated 1252 | 1253 | **Tuesday (Day 3): P0-R2 - Null-Safety Audit (Part 1)** 1254 | - AM: Create SafePropertyAccess utility 1255 | - PM: Refactor node repository methods 1256 | - **Deliverable:** Safe property access framework 1257 | 1258 | **Wednesday (Day 4): P0-R2 - Null-Safety Audit (Part 2)** 1259 | - AM: Find and fix all handlers 1260 | - PM: Comprehensive null-safety testing 1261 | - **Deliverable:** 10-18% TypeError rate → <1% 1262 | 1263 | **Thursday (Day 5): P0-R3 - Database Schema** 1264 | - AM: Design and implement template_node_configs table 1265 | - PM: Create migration script and test with existing database 1266 | - **Deliverable:** Schema ready for extraction 1267 | 1268 | **Friday (Day 6): P0-R3 - Extraction Logic** 1269 | - AM: Write extraction function in fetch:templates 1270 | - PM: Write ranking logic and test with 2,646 templates 1271 | - **Deliverable:** 2,000+ configs extracted and ranked 1272 | 1273 | ### Week 2: P0-R3 Integration + Testing + Documentation 1274 | 1275 | **Monday (Day 7): Tool Enhancements** 1276 | - AM: Enhance get_node_essentials with includeExamples 1277 | - PM: Enhance search_nodes with includeExamples 1278 | - **Deliverable:** Both tools return real examples 1279 | 1280 | **Tuesday (Day 8): Tool Removal + Documentation** 1281 | - AM: Remove get_node_for_task from all files 1282 | - PM: Update README, CHANGELOG, tools_documentation 1283 | - **Deliverable:** Clean removal, migration guide complete 1284 | 1285 | **Wednesday (Day 9): Comprehensive Testing** 1286 | - AM: Unit tests for extraction and enhanced tools 1287 | - PM: Integration tests for all P0 fixes 1288 | - **Deliverable:** 95%+ test coverage 1289 | 1290 | **Thursday (Day 10): Performance + Final Testing** 1291 | - AM: Performance testing and optimization 1292 | - PM: E2E testing and bug fixes 1293 | - **Deliverable:** All success criteria met 1294 | 1295 | **Friday (Day 11): Release Preparation** 1296 | - AM: Code review and documentation review 1297 | - PM: Prepare release notes, tag v2.15.0 1298 | - **Deliverable:** Ready for release 1299 | 1300 | ### Parallel Activities 1301 | 1302 | - **Documentation updates:** Days 1-11 1303 | - **Code reviews:** End of Days 2, 4, 6, 8, 10 1304 | - **Telemetry preparation:** Day 10-11 (prepare monitoring dashboard) 1305 | 1306 | --- 1307 | 1308 | ## Testing Strategy 1309 | 1310 | ### Unit Tests 1311 | 1312 | **Coverage Target:** 95% for new code 1313 | 1314 | - **Node Type Normalizer:** 20+ test cases 1315 | - **Safe Property Access:** 30+ test cases 1316 | - **Task Discovery Service:** 40+ test cases 1317 | 1318 | ### Integration Tests 1319 | 1320 | - Workflow validation with mixed node type forms 1321 | - Node repository with edge case data 1322 | - Task discovery with real node database 1323 | 1324 | ### E2E Tests 1325 | 1326 | - Create workflow with short-form node types → Should succeed 1327 | - Get node info for nodes with missing properties → Should return safe defaults 1328 | - Query task discovery with variations → Should find matches 1329 | 1330 | ### Regression Tests 1331 | 1332 | - All existing tests must pass 1333 | - No breaking changes to public APIs 1334 | 1335 | ### Performance Tests 1336 | 1337 | - Normalization overhead: <1ms per workflow 1338 | - Safe property access: <0.1ms per node 1339 | - Task discovery: <50ms average 1340 | 1341 | --- 1342 | 1343 | ## Rollback Plan 1344 | 1345 | ### If P0-R1 Causes Issues 1346 | 1347 | 1. **Symptom:** Workflows fail validation after normalization 1348 | 2. **Action:** Revert node-type-normalizer changes 1349 | 3. **Fallback:** Use original normalizeNodeType 1350 | 4. **Recovery time:** 15 minutes 1351 | 1352 | ### If P0-R2 Causes Performance Issues 1353 | 1354 | 1. **Symptom:** Node lookup becomes slow 1355 | 2. **Action:** Cache safe property access results 1356 | 3. **Fallback:** Keep safe parsing but reduce validation 1357 | 4. **Recovery time:** 1 hour 1358 | 1359 | ### If P0-R3 Template Extraction Causes Issues 1360 | 1361 | 1. **Symptom:** Database bloat or slow queries 1362 | 2. **Action:** Reduce rank limit from 10 to 5 per node 1363 | 3. **Fallback:** Disable includeExamples parameter temporarily 1364 | 4. **Recovery time:** 15 minutes (just disable parameter) 1365 | 1366 | ### If get_node_for_task Removal Causes User Issues 1367 | 1368 | 1. **Symptom:** Users report missing tool 1369 | 2. **Action:** Add prominent migration guide to error messages 1370 | 3. **Fallback:** N/A (breaking change, users must migrate) 1371 | 4. **Communication:** Update docs, add migration examples 1372 | 1373 | --- 1374 | 1375 | ## Success Metrics 1376 | 1377 | ### Overall Goals 1378 | 1379 | | Metric | Current | Target | How to Measure | 1380 | |--------|---------|--------|----------------| 1381 | | Overall error rate | 5-10% | <2% | Telemetry events | 1382 | | Validation errors | 4,800/week | <100/week | Error logs | 1383 | | TypeError rate | 10-18% | <1% | Tool execution logs | 1384 | | Node configs extracted | 0 | 2,000+ | Database count | 1385 | | Config query performance | N/A | <1ms | Performance tests | 1386 | | get_node_for_task usage | 392 calls | 0 (removed) | Tool usage stats | 1387 | | search_nodes w/ examples | 0 | Monitored | New feature adoption | 1388 | 1389 | ### Telemetry Monitoring 1390 | 1391 | After deployment, monitor for 1 week: 1392 | 1393 | - Error rate by tool (should decrease 80-90%) 1394 | - User success rate (should increase 5-10%) 1395 | - Average errors per user (should decrease from 2.5 to <0.5) 1396 | 1397 | --- 1398 | 1399 | ## Dependencies 1400 | 1401 | ### NPM Packages 1402 | 1403 | No new NPM packages required - all functionality uses existing dependencies. 1404 | 1405 | ### Internal Dependencies 1406 | 1407 | - **P0-R3** requires database schema update (template_node_configs table) 1408 | - **P0-R3** requires migration script for existing databases 1409 | - All changes are backward compatible except removal of `get_node_for_task` 1410 | 1411 | --- 1412 | 1413 | ## Documentation Updates 1414 | 1415 | ### Files to Update 1416 | 1417 | 1. **CHANGELOG.md** - Add entries for each P0 fix + breaking changes 1418 | 2. **README.md** - Remove get_node_for_task, add includeExamples parameter 1419 | 3. **src/mcp/tools-documentation.ts** - Remove get_node_for_task documentation 1420 | 4. **API.md** - Document enhanced tool parameters 1421 | 5. **MIGRATION.md** - Add migration guide from get_node_for_task to search_nodes (NEW) 1422 | 1423 | ### Example CHANGELOG Entry 1424 | 1425 | ```markdown 1426 | ## [2.15.0] - 2025-10-09 1427 | 1428 | ### BREAKING CHANGES 1429 | - **Removed:** `get_node_for_task` tool 1430 | - **Replacement:** Use `search_nodes` with `includeExamples: true` 1431 | - **Migration:** `get_node_for_task({task: "webhook"})` → `search_nodes({query: "webhook", includeExamples: true})` 1432 | - **Benefit:** Access to 2,646 real templates vs 31 hardcoded tasks 1433 | 1434 | ### Fixed 1435 | - **P0-R1:** Auto-normalize node type prefixes (eliminates 80% of validation errors) 1436 | - **P0-R2:** Complete null-safety audit for node information tools (reduces TypeError failures from 10-18% to <1%) 1437 | 1438 | ### Added 1439 | - `NodeTypeNormalizer` utility for universal node type normalization 1440 | - `SafePropertyAccess` utility for defensive property access 1441 | - `template_node_configs` table with 2,000+ pre-extracted configurations 1442 | - `includeExamples` parameter for `get_node_essentials` (returns 2-3 real configs) 1443 | - `includeExamples` parameter for `search_nodes` (returns 2 real configs per node) 1444 | - Real-world configuration examples from popular n8n templates 1445 | 1446 | ### Performance 1447 | - Node configuration queries: <1ms (30-60x faster than on-the-fly extraction) 1448 | - Sub-millisecond response time for configuration examples 1449 | ``` 1450 | 1451 | --- 1452 | 1453 | ## Conclusion 1454 | 1455 | These P0 fixes represent the highest-impact improvements we can make to n8n-mcp based on real production telemetry data. By implementing all three fixes in v2.15.0, we will: 1456 | 1457 | 1. **Eliminate 80% of validation errors** (P0-R1: Node type normalization) 1458 | 2. **Fix the majority of TypeError failures** (P0-R2: Null-safety audit) 1459 | 3. **Replace inferior tool with superior alternative** (P0-R3: Template-based configs + remove get_node_for_task) 1460 | 1461 | **Expected Overall Impact:** 1462 | - Error rate: 5-10% → <2% 1463 | - Configuration examples: 31 hardcoded → 2,000+ real templates 1464 | - Query performance: 30-60ms → <1ms (30-60x faster) 1465 | - User experience: Significant improvement across all tools 1466 | - Support burden: Reduced by 50%+ 1467 | 1468 | **Key Innovation (P0-R3):** 1469 | - Pre-extraction delivers 30-60x performance improvement 1470 | - 2,646 real templates provide richer context than hardcoded tasks 1471 | - Breaking change justified by superior replacement 1472 | - Database increase: Only +513 KB for 2,625 configurations 1473 | 1474 | The implementation is well-architected, delivers exceptional value, and sets up future enhancements. 1475 | 1476 | --- 1477 | 1478 | **Next Steps:** 1479 | 1480 | 1. ✅ Review implementation plan with team (COMPLETED) 1481 | 2. ✅ Finalize architectural decisions (COMPLETED - pre-extraction chosen) 1482 | 3. ✅ Create feature branch: `feature/p0-priorities-fixes` (COMPLETED) 1483 | 4. ✅ **P0-R1**: Auto-Normalize Node Type Prefixes (COMPLETED - commit ed7de10) 1484 | 5. ⏳ **P0-R2**: Complete Null-Safety Audit (PENDING) 1485 | 6. ⏳ **P0-R3**: Pre-extract Template Configs + Remove get_node_for_task (PENDING) 1486 | 7. ⏳ Deploy v2.15.0 with monitoring and telemetry analysis 1487 | 1488 | **Target Release:** v2.15.0 (estimated 1.5 weeks remaining) 1489 | 1490 | ``` -------------------------------------------------------------------------------- /docs/local/DEEP_DIVE_ANALYSIS_2025-10-02.md: -------------------------------------------------------------------------------- ```markdown 1 | # **N8N-MCP DEEP DIVE ANALYSIS** 2 | ## **Usage Patterns & Refactoring Recommendations** 3 | 4 | **Analysis Period:** September 26 - October 2, 2025 (6 days) 5 | **Data Volume:** 212,375 events | 5,751 workflows | 2,119 unique users 6 | **Database:** Supabase telemetry with 15 analytical views 7 | **Analyst:** Claude Code with Supabase MCP integration 8 | **Date:** October 2, 2025 9 | 10 | --- 11 | 12 | ## **EXECUTIVE SUMMARY** 13 | 14 | n8n-mcp has achieved **strong adoption** with 2,119 users generating 212K+ events in 6 days. The system demonstrates **excellent reliability** (96-100% success rates for most tools) but has **critical pain points** that are blocking users and degrading the AI agent experience. The upcoming refactor should focus on: 15 | 16 | 1. **Fixing the "node type prefix" validation catastrophe** (5,000+ validation errors from a single root cause) 17 | 2. **Resolving TypeError issues** in node information tools (1,000+ failures affecting 10% of calls) 18 | 3. **Streamlining the workflow update experience** (iterative updates dominate usage) 19 | 4. **Improving node discovery** (search is the #2 most-used tool but has UX gaps) 20 | 5. **Optimizing for power users** who drive 60% of activity 21 | 22 | **Key Metrics:** 23 | - **Overall Success Rate:** 96-98% across all user segments 24 | - **Daily Event Growth:** 16K → 40K events (2.5x growth in 3 days) 25 | - **Power User Concentration:** Top 3% of users generate 27% of events 26 | - **Most Used Tools:** update_partial_workflow (10,177), search_nodes (8,839), create_workflow (6,046) 27 | - **Critical Failure Rates:** get_node_for_task (28%), get_node_info (18%), get_node_essentials (10%) 28 | 29 | --- 30 | 31 | ## **TABLE OF CONTENTS** 32 | 33 | 1. [Tool Performance Analysis](#1-tool-performance-analysis) 34 | 2. [Validation Catastrophe](#2-validation-catastrophe) 35 | 3. [Usage Patterns & User Segmentation](#3-usage-patterns--user-segmentation) 36 | 4. [Tool Sequence Analysis](#4-tool-sequence-analysis) 37 | 5. [Workflow Creation Patterns](#5-workflow-creation-patterns) 38 | 6. [Platform & Version Distribution](#6-platform--version-distribution) 39 | 7. [Error Patterns & Root Causes](#7-error-patterns--root-causes) 40 | 8. [Prioritized Refactoring Recommendations](#8-prioritized-refactoring-recommendations) 41 | 9. [Architectural Recommendations](#9-architectural-recommendations) 42 | 10. [Telemetry Enhancements](#10-telemetry-enhancements) 43 | 11. [Specific Code Changes](#11-specific-code-changes) 44 | 12. [CHANGELOG Integration](#12-changelog-integration) 45 | 13. [Final Recommendations Summary](#13-final-recommendations-summary) 46 | 47 | --- 48 | 49 | ## **1. TOOL PERFORMANCE ANALYSIS** 50 | 51 | ### **1.1 Success Rate Tiers** 52 | 53 | **EXCELLENT (95-100% success):** 54 | - ✅ `n8n_update_partial_workflow` - 10,177 calls, 98.7% success, 846 users 55 | - ✅ `search_nodes` - 8,839 calls, 99.8% success, 1,283 users 56 | - ✅ `n8n_create_workflow` - 6,046 calls, 96.1% success, 1,305 users 57 | - ✅ `n8n_validate_workflow` - 3,222 calls, 99.8% success, 597 users 58 | - ✅ `n8n_get_workflow` - 3,368 calls, 99.8% success, 790 users 59 | - ✅ `n8n_update_full_workflow` - 2,640 calls, 99.4% success, 486 users 60 | - ✅ `tools_documentation` - 1,886 calls, 100% success, 879 users 61 | - ✅ `validate_workflow` - 1,667 calls, 95.4% success, 472 users 62 | - ✅ All n8n workflow management tools (list/delete/health) - 100% success 63 | 64 | **GOOD (80-95% success):** 65 | - ⚠️ `get_node_essentials` - 4,909 calls, **90.2% success**, 921 users (**9.8% failure**) 66 | - ⚠️ `get_node_documentation` - 1,919 calls, 92.9% success, 657 users (**7.1% failure**) 67 | - ⚠️ `validate_node_operation` - 998 calls, 88.6% success, 240 users (**11.4% failure**) 68 | - ⚠️ `list_ai_tools` - 234 calls, 84.2% success, 184 users (**15.8% failure**) 69 | - ⚠️ `get_node_info` - 1,988 calls, **82.3% success**, 677 users (**17.7% failure**) 70 | 71 | **POOR (50-80% success):** 72 | - 🔴 `get_node_for_task` - 392 calls, **72.2% success**, 197 users (**27.8% failure**) 73 | 74 | ### **1.2 Performance Metrics** 75 | 76 | **Ultra-Fast (<10ms avg):** 77 | - `get_node_essentials`: 3.27ms avg (median: 1ms) 78 | - `get_node_info`: 4.78ms avg (median: 1ms) 79 | - `get_node_documentation`: 2.16ms avg (median: 1ms) 80 | - `tools_documentation`: 3.42ms avg (median: 1ms) 81 | - `validate_node_minimal`: 1.79ms avg (median: 1ms) 82 | 83 | **Fast (10-100ms avg):** 84 | - `search_nodes`: 20.47ms avg (median: 5ms, p95: 84ms) 85 | - `validate_workflow`: 31.59ms avg (median: 12ms, p95: 103ms) 86 | - `list_nodes`: 41.86ms avg (median: 11ms, p95: 196ms) 87 | 88 | **Acceptable (100-500ms avg):** 89 | - `n8n_get_workflow`: 248.79ms avg (median: 111ms, p95: 830ms) 90 | - `n8n_validate_workflow`: 229.37ms avg (median: 106ms, p95: 722ms) 91 | - `n8n_update_full_workflow`: 302.70ms avg (median: 119ms, p95: 1,069ms) 92 | - `n8n_delete_workflow`: 308.85ms avg (median: 166ms, p95: 950ms) 93 | - `n8n_create_workflow`: 333.37ms avg (median: 85ms, p95: 1,251ms) 94 | - `n8n_list_workflows`: 476.05ms avg (median: 231ms, p95: 1,465ms) 95 | - `n8n_autofix_workflow`: 486.49ms avg (median: 174ms, p95: 1,152ms) 96 | 97 | **Slow (>500ms avg):** 98 | - `n8n_get_execution`: 670.35ms avg (median: 121ms, p95: 1,166ms) 99 | - `n8n_trigger_webhook_workflow`: 1,884.67ms avg (median: 157ms, p95: 4,865ms) 100 | 101 | ### **1.3 Critical Findings** 102 | 103 | **FINDING #1: Node information tools have systematic TypeError issues** 104 | - `get_node_essentials`: 483 failures (10% of calls) 105 | - `get_node_info`: 352 failures (18% of calls) 106 | - `get_node_documentation`: 136 failures (7% of calls) 107 | - **Root cause**: Accessing undefined properties on node objects (from CHANGELOG 2.14.0 fix) 108 | - **Impact**: AI agents cannot get basic node information, blocking workflow creation 109 | - **Evidence**: 400+ TypeError occurrences from error logs 110 | 111 | **FINDING #2: Task-based node discovery is failing 28% of the time** 112 | - `get_node_for_task` has the worst success rate (72%) 113 | - **Impact**: When AI agents ask "which node should I use for X task?", they fail 1 in 4 times 114 | - **Likely cause**: Limited task library or poor task-to-node mapping 115 | - **Usage pattern**: 392 calls, 197 users → high demand but low reliability 116 | 117 | **FINDING #3: Performance is excellent across the board** 118 | - Node lookup tools: <5ms average (ultra-fast SQLite queries) 119 | - Search operations: ~20ms average (efficient FTS5 indexing) 120 | - Network operations (n8n API): 200-500ms average (acceptable for HTTP calls) 121 | - Webhook triggers: 1,885ms average (expected for workflow execution) 122 | - **Conclusion**: Performance is NOT a bottleneck; reliability is 123 | 124 | **FINDING #4: Workflow management tools have perfect reliability** 125 | - `n8n_list_workflows`: 100% success (1,489 calls) 126 | - `n8n_health_check`: 100% success (1,304 calls) 127 | - `n8n_list_executions`: 100% success (1,297 calls) 128 | - `n8n_delete_workflow`: 100% success (1,230 calls) 129 | - **Takeaway**: The n8n API integration layer is rock-solid 130 | 131 | --- 132 | 133 | ## **2. VALIDATION CATASTROPHE** 134 | 135 | ### **2.1 The "Node Type Prefix" Disaster** 136 | 137 | **CRITICAL ISSUE:** 5,000+ validation errors from a single root cause 138 | 139 | ``` 140 | Error: Invalid node type: "nodes-base.set". Use "n8n-nodes-base.set" instead. 141 | ``` 142 | 143 | **Breakdown by error type:** 144 | 1. **4,800 occurrences**: "Invalid node type" prefix errors across Node0-Node19 145 | - Pattern: `nodes-base.X` instead of `n8n-nodes-base.X` 146 | - Affected nodes: ALL node types (Set, HTTP Request, Code, etc.) 147 | - **Impact**: 1 affected user with systematic errors across entire workflow 148 | 149 | 2. **2,500 occurrences**: "Multi-node workflow has no connections" 150 | - Likely same 2 users testing/debugging 151 | - Message: "Multi-node workflow has no connections. Nodes must be connected..." 152 | - **Pattern**: Valid validation but high repetition suggests test loops 153 | 154 | 3. **58 occurrences**: "Single-node workflows are only valid for webhook endpoints" 155 | - 42 affected users 156 | - **This is good validation** - appropriate message 157 | 158 | 4. **22 occurrences**: Duplicate node ID: "undefined" 159 | - 5 affected users 160 | - **Likely bug**: Nodes being created without IDs 161 | 162 | ### **2.2 Root Cause Analysis** 163 | 164 | **Why AI agents produce `nodes-base.X` instead of `n8n-nodes-base.X`:** 165 | 166 | 1. **Token efficiency**: LLMs may abbreviate to save tokens 167 | 2. **Pattern learning**: AI may see shortened versions in docs/examples 168 | 3. **Natural language**: "nodes-base" is more concise than "n8n-nodes-base" 169 | 4. **Inconsistency**: Some tools may accept both formats, creating confusion 170 | 171 | **Why the system doesn't auto-correct:** 172 | 173 | From CHANGELOG 2.14.2: 174 | > "Fixed validation false positives for Google Drive nodes with 'fileFolder' resource 175 | > - Added node type normalization to handle both `n8n-nodes-base.` and `nodes-base.` prefixes correctly" 176 | 177 | **Analysis**: A fix was attempted in 2.14.2, but it's **incomplete or not applied universally**. The normalization logic exists but isn't being called in all validation paths. 178 | 179 | ### **2.3 Impact Assessment** 180 | 181 | **User Experience:** 182 | - **Frustration**: AI agents receive validation errors requiring manual intervention 183 | - **Token waste**: Multiple retry attempts with failed validations 184 | - **Broken flow**: Interrupts the natural workflow creation process 185 | 186 | **Quantitative Impact:** 187 | - **80% of all validation errors** stem from this single issue 188 | - **Affects ALL node types**, not specific to certain nodes 189 | - **Systematic pattern**: Once a user hits this, they hit it repeatedly 190 | 191 | **Why This Is Critical:** 192 | - Easy to fix (normalization helper already exists) 193 | - Massive impact (eliminates 4,800+ errors) 194 | - Improves AI agent experience significantly 195 | 196 | ### **2.4 Other Validation Issues** 197 | 198 | **Connection Validation:** 199 | - "Connection uses node ID instead of node name" - 1 occurrence 200 | - Good error message with clear guidance 201 | - Not a systemic issue 202 | 203 | **Node Configuration:** 204 | - Various property-specific validation errors (low frequency) 205 | - Generally well-handled with actionable messages 206 | 207 | --- 208 | 209 | ## **3. USAGE PATTERNS & USER SEGMENTATION** 210 | 211 | ### **3.1 User Distribution** 212 | 213 | | Segment | Users | % | Events | Avg Events | Workflows | Success Rate | 214 | |---------|-------|---|--------|------------|-----------|--------------| 215 | | **Power Users (1000+)** | 12 | 0.6% | 25,346 | 2,112 | 33 | 97.1% | 216 | | **Heavy Users (500-999)** | 47 | 2.2% | 31,608 | 673 | 18 | 98.0% | 217 | | **Regular Users (100-499)** | 516 | 24.3% | 102,931 | 199 | 7 | 96.5% | 218 | | **Active Users (20-99)** | 919 | 43.4% | 47,768 | 52 | 2 | 97.0% | 219 | | **Casual Users (<20)** | 625 | 29.5% | 4,958 | 8 | 1 | 97.6% | 220 | | **TOTAL** | 2,119 | 100% | 212,611 | 100 | - | 97.0% | 221 | 222 | ### **3.2 Key Insights** 223 | 224 | **INSIGHT #1: Extreme power law distribution** 225 | - **Top 59 users (3%)** generate **27% of all events** (57K events) 226 | - **Top 575 users (27%)** generate **76% of all events** (160K events) 227 | - **Bottom 625 users (30%)** generate only **2% of events** (5K events) 228 | 229 | **Implications:** 230 | - Optimize for power users → 3x impact per feature 231 | - Onboarding is good (casual users have 97.6% success rate) 232 | - Need enterprise features for heavy users (monitoring, analytics, team features) 233 | 234 | **INSIGHT #2: Regular users (516) are the core audience** 235 | - 103K events total (48% of all activity) 236 | - 7 workflows/user average (meaningful engagement) 237 | - 96.5% success rate (room for improvement) 238 | - **This is the growth segment** - convert to heavy users 239 | 240 | **INSIGHT #3: Consistent success rates across segments** 241 | - All segments: 96-98% success rate 242 | - **Paradox**: Power users have LOWER success rate (97.1% vs 97.6% casual) 243 | - **Explanation**: Power users attempt harder tasks → more edge cases 244 | - **Opportunity**: Focus reliability improvements on advanced features 245 | 246 | **INSIGHT #4: Workflow creation correlates with engagement** 247 | - Power users: 33 workflows 248 | - Heavy users: 18 workflows 249 | - Regular users: 7 workflows 250 | - Active users: 2 workflows 251 | - **Metric**: Workflows created = proxy for value delivered 252 | 253 | ### **3.3 Daily Usage Trends** 254 | 255 | | Date | Events | Users | Events/User | Growth | 256 | |------|--------|-------|-------------|--------| 257 | | Sep 26 | 16,334 | 958 | 17.0 | baseline | 258 | | Sep 27 | 26,042 | 2,075 | 12.6 | +59% events | 259 | | Sep 28 | 35,687 | 2,655 | 13.4 | +37% events | 260 | | **Sep 29** | **40,361** | **3,039** | **13.3** | **+13% events (peak)** | 261 | | Sep 30 | 39,833 | 3,319 | 12.0 | -1% events | 262 | | Oct 1 | 39,854 | 3,528 | 11.3 | 0% events | 263 | | Oct 2 | 14,500 | 1,057 | 13.7 | partial day | 264 | 265 | **Growth Analysis:** 266 | - **Rapid adoption**: 16K → 40K daily events (2.5x in 3 days) 267 | - **Plateau**: Sep 29-Oct 1 stable at ~40K events/day 268 | - **User growth**: 958 → 3,528 users (3.7x growth) 269 | - **Efficiency**: Events per user declining (17 → 11) as user base broadens 270 | 271 | **Interpretation:** 272 | - System reached **initial scale** (~40K events/day, ~3K users/day) 273 | - Now in **consolidation phase** - need to improve retention 274 | - **Next growth phase** requires solving reliability issues (see P0 recommendations) 275 | 276 | --- 277 | 278 | ## **4. TOOL SEQUENCE ANALYSIS** 279 | 280 | ### **4.1 Most Common Patterns** 281 | 282 | **Top 15 Tool Sequences (all show 300s = 5 min time delta):** 283 | 284 | | Rank | Sequence | Count | Users | Pattern | 285 | |------|----------|-------|-------|---------| 286 | | 1 | `update_partial_workflow` → `update_partial_workflow` | 549 | 153 | Iterative refinement | 287 | | 2 | `create_workflow` → `update_partial_workflow` | 297 | 118 | Create then refine | 288 | | 3 | `update_partial_workflow` → `get_workflow` | 265 | 91 | Update then verify | 289 | | 4 | `create_workflow` → `create_workflow` | 237 | 97 | Multiple attempts | 290 | | 5 | `create_workflow` → `get_workflow` | 185 | 81 | Create then inspect | 291 | | 6 | `create_workflow` → `search_nodes` | 166 | 72 | Create then discover | 292 | | 7 | `validate_workflow` → `update_partial_workflow` | 161 | 63 | Validate then fix | 293 | | 8 | `validate_workflow` → `validate_workflow` | 152 | 44 | Re-validation | 294 | | 9 | `validate_workflow` → `get_workflow` | 134 | 53 | Validate then inspect | 295 | | 10 | `update_partial_workflow` → `create_workflow` | 130 | 59 | Update then recreate | 296 | | 11 | `get_workflow` → `update_partial_workflow` | 117 | 50 | Inspect then update | 297 | | 12 | `update_full_workflow` → `update_partial_workflow` | 98 | 41 | Full to partial update | 298 | | 13 | `update_partial_workflow` → `search_nodes` | 94 | 42 | Update then discover | 299 | | 14 | `get_workflow` → `create_workflow` | 87 | 42 | Inspect then recreate | 300 | | 15 | `create_workflow` → `tools_documentation` | 85 | 36 | Create then learn | 301 | 302 | ### **4.2 Critical Insights** 303 | 304 | **INSIGHT #1: AI agents iterate heavily on workflows** 305 | - **#1 sequence**: `update → update → update` (549 occurrences) 306 | - **Pattern**: Create → Validate → Update → Validate → Update (feedback loop) 307 | - **Workflow**: 308 | 1. AI creates initial workflow 309 | 2. Validates it (finds issues) 310 | 3. Updates to fix issues 311 | 4. Validates again (finds more issues) 312 | 5. Continues iterating until success 313 | 314 | **Implication**: The diff-based update system (v2.7.0) is **CRUCIAL** for token efficiency 315 | - Without diff updates: Would need full workflow JSON each time (~10-50KB) 316 | - With diff updates: Only send changed operations (~1-5KB) 317 | - **Token savings**: 80-90% per update iteration 318 | 319 | **INSIGHT #2: All transitions show 5-minute time deltas** 320 | - **This is NOT actual elapsed time** - it's the telemetry "slow transition" threshold 321 | - All sequences are marked as `is_slow_transition: true` 322 | - **Actual insight**: AI agents take thinking time between tool calls (expected for LLMs) 323 | - **Limitation**: Cannot determine real workflow creation speed with current data 324 | 325 | **Recommendation**: Add fine-grained timing (see T1 in Telemetry Enhancements) 326 | 327 | **INSIGHT #3: Node discovery happens AFTER workflow creation** 328 | - `create_workflow → search_nodes` (166 occurrences) 329 | - **Flow**: 330 | 1. AI creates workflow with known nodes 331 | 2. Realizes it needs additional nodes 332 | 3. Searches for them 333 | 4. Updates workflow with new nodes 334 | 335 | **Opportunity**: Proactive node suggestions during creation (see P1-R5) 336 | 337 | **INSIGHT #4: Validation drives updates** 338 | - `validate_workflow → update_partial_workflow` (161 occurrences) 339 | - `validate_workflow → validate_workflow` (152 occurrences) 340 | - **Pattern**: Validation → Fix → Re-validate loop 341 | 342 | **Quality**: This is GOOD behavior (AI agents using validation to improve) 343 | **Optimization**: Better validation error messages → fewer iterations (see P1-R6) 344 | 345 | ### **4.3 Common Workflow Patterns** 346 | 347 | **Pattern A: Create-Update-Validate Loop** 348 | ``` 349 | create_workflow → update_partial_workflow → validate_workflow → update_partial_workflow 350 | ``` 351 | - Most common for new workflows 352 | - 3-5 iterations average before success 353 | 354 | **Pattern B: Inspect-Modify-Deploy** 355 | ``` 356 | get_workflow → update_partial_workflow → validate_workflow → get_workflow 357 | ``` 358 | - Common for modifying existing workflows 359 | - "Get" used to verify final state 360 | 361 | **Pattern C: Search-Create-Refine** 362 | ``` 363 | search_nodes → create_workflow → update_partial_workflow → validate_workflow 364 | ``` 365 | - Discovery-driven workflow creation 366 | - Users explore capabilities before creating 367 | 368 | ### **4.4 Tools Leading to Workflow Creation** 369 | 370 | **Tools used within 5 minutes BEFORE workflow creation:** 371 | 372 | | Tool | Occurrences | Users | Conversion Rate | 373 | |------|-------------|-------|-----------------| 374 | | `update_partial_workflow` | 6,271 | 547 | High | 375 | | `search_nodes` | 6,099 | 901 | High | 376 | | `get_node_essentials` | 3,361 | 649 | Medium | 377 | | `create_workflow` | 2,810 | 742 | Medium (re-creation) | 378 | | `get_workflow` | 2,057 | 512 | Medium | 379 | | `validate_workflow` | 2,014 | 417 | Medium | 380 | | `get_node_documentation` | 1,301 | 456 | Low | 381 | | `tools_documentation` | 1,290 | 596 | Low | 382 | 383 | **Interpretation:** 384 | - **Discovery tools** (search_nodes, get_node_essentials) → high workflow creation 385 | - **Documentation tools** → lower conversion (learning/exploring phase) 386 | - **Workflow management** (update/validate) → iterative creation process 387 | 388 | --- 389 | 390 | ## **5. WORKFLOW CREATION PATTERNS** 391 | 392 | ### **5.1 Complexity Distribution** 393 | 394 | | Complexity | Count | % | Avg Nodes | Median | Triggers | Webhooks | 395 | |------------|-------|---|-----------|--------|----------|----------| 396 | | **Simple** | 4,290 | 75% | 5.5 | 5 | 1,330 (31%) | 1,330 (31%) | 397 | | **Medium** | 1,282 | 22% | 14.0 | 13 | 424 (33%) | 424 (33%) | 398 | | **Complex** | 187 | 3% | 27.5 | 23 | 71 (38%) | 71 (38%) | 399 | | **TOTAL** | 5,759 | 100% | 8.2 | 6 | 1,825 (32%) | 1,825 (32%) | 400 | 401 | **Complexity Definitions:** 402 | - **Simple**: ≤8 nodes 403 | - **Medium**: 9-20 nodes 404 | - **Complex**: 21+ nodes 405 | 406 | ### **5.2 Key Findings** 407 | 408 | **FINDING #1: 75% of workflows are simple** 409 | - AI agents prefer minimalism (5-6 nodes average) 410 | - Small workflows are easier to reason about 411 | - Faster creation and debugging 412 | - **Implication**: Optimize for simple workflow creation experience 413 | 414 | **FINDING #2: Complex workflows are rare but important** 415 | - Only 3% of workflows (187 total) 416 | - Average 27.5 nodes (large automation) 417 | - 38% have triggers/webhooks (production use) 418 | - **User profile**: Likely power users building production systems 419 | 420 | **FINDING #3: Webhook usage is consistent across complexity** 421 | - Simple: 31% have webhooks 422 | - Medium: 33% have webhooks 423 | - Complex: 38% have webhooks 424 | - **Insight**: Webhooks are a fundamental pattern, not correlated with complexity 425 | 426 | ### **5.3 Most Popular Nodes** 427 | 428 | **Top 20 Nodes by Workflow Count:** 429 | 430 | | Rank | Node Type | Workflows | % | Users | Avg Workflow Size | 431 | |------|-----------|-----------|---|-------|-------------------| 432 | | 1 | `n8n-nodes-base.code` | 3,051 | 53% | 1,056 | 9.4 | 433 | | 2 | `n8n-nodes-base.httpRequest` | 2,686 | 47% | 1,033 | 9.6 | 434 | | 3 | `n8n-nodes-base.webhook` | 1,812 | 32% | 750 | 8.5 | 435 | | 4 | `n8n-nodes-base.set` | 1,738 | 30% | 742 | 9.2 | 436 | | 5 | `n8n-nodes-base.if` | 1,400 | 24% | 653 | 12.2 | 437 | | 6 | `n8n-nodes-base.manualTrigger` | 1,391 | 24% | 590 | 7.9 | 438 | | 7 | `n8n-nodes-base.respondToWebhook` | 1,113 | 19% | 484 | 8.7 | 439 | | 8 | `@n8n/n8n-nodes-langchain.agent` | 884 | 15% | 403 | 9.7 | 440 | | 9 | `n8n-nodes-base.scheduleTrigger` | 825 | 14% | 412 | 9.5 | 441 | | 10 | `n8n-nodes-base.googleSheets` | 732 | 13% | 324 | 10.5 | 442 | | 11 | `n8n-nodes-base.merge` | 599 | 10% | 311 | 13.8 | 443 | | 12 | `@n8n/n8n-nodes-langchain.lmChatOpenAi` | 564 | 10% | 268 | 10.6 | 444 | | 13 | `n8n-nodes-base.switch` | 534 | 9% | 262 | 13.7 | 445 | | 14 | `n8n-nodes-base.openAi` | 486 | 8% | 261 | 10.1 | 446 | | 15 | `n8n-nodes-base.splitInBatches` | 457 | 8% | 229 | 13.2 | 447 | | 16 | `n8n-nodes-base.telegram` | 416 | 7% | 168 | 10.0 | 448 | | 17 | `n8n-nodes-base.function` | 414 | 7% | 162 | 9.6 | 449 | | 18 | `n8n-nodes-base.gmail` | 400 | 7% | 212 | 9.5 | 450 | | 19 | `n8n-nodes-base.cron` | 380 | 7% | 182 | 9.2 | 451 | | 20 | `n8n-nodes-base.noOp` | 322 | 6% | 174 | 10.9 | 452 | 453 | ### **5.4 Critical Insights** 454 | 455 | **INSIGHT #1: Code node dominates (53% of workflows)** 456 | - AI agents LOVE programmatic control 457 | - Code node enables custom logic that other nodes can't provide 458 | - **Implication**: Ensure code node documentation and examples are excellent 459 | 460 | **INSIGHT #2: HTTP Request is in nearly half of workflows (47%)** 461 | - Integration-heavy usage pattern 462 | - Most workflows interact with external APIs 463 | - **Synergy**: Code + HTTP Request (see co-occurrence analysis) 464 | 465 | **INSIGHT #3: LangChain nodes show strong adoption (15%)** 466 | - AI-on-AI workflows 467 | - `langchain.agent` in 884 workflows 468 | - `lmChatOpenAi` in 564 workflows 469 | - **Trend**: AI-building-AI-workflows is a real use case 470 | 471 | **INSIGHT #4: Average workflow size correlates with node type** 472 | - Control flow nodes (if/switch): 12-14 nodes average (complex workflows) 473 | - Data nodes (set/code): 9-10 nodes average (medium workflows) 474 | - Trigger nodes (manualTrigger): 7-8 nodes average (simple workflows) 475 | 476 | ### **5.5 Node Co-occurrence Patterns** 477 | 478 | **Top 20 Node Pairs:** 479 | 480 | | Rank | Node 1 | Node 2 | Co-occurrence | Users | Avg Size | Pattern | 481 | |------|--------|--------|---------------|-------|----------|---------| 482 | | 1 | `code` | `httpRequest` | 1,646 | 686 | 10.4 | Data transformation + API | 483 | | 2 | `webhook` | `respondToWebhook` | 1,043 | 452 | 8.8 | Standard webhook pattern | 484 | | 3 | `code` | `webhook` | 1,008 | 442 | 9.6 | Custom webhook logic | 485 | | 4 | `code` | `if` | 894 | 437 | 12.9 | Conditional with custom logic | 486 | | 5 | `httpRequest` | `webhook` | 845 | 404 | 10.5 | Webhook-triggered API calls | 487 | | 6 | `httpRequest` | `set` | 841 | 431 | 10.7 | API response processing | 488 | | 7 | `httpRequest` | `if` | 815 | 420 | 13.4 | Conditional API logic | 489 | | 8 | `code` | `manualTrigger` | 731 | 321 | 9.7 | Manual testing workflows | 490 | | 9 | `httpRequest` | `manualTrigger` | 706 | 328 | 9.1 | Manual API testing | 491 | | 10 | `code` | `set` | 677 | 339 | 11.6 | Data manipulation | 492 | | 11 | `code` | `respondToWebhook` | 617 | 285 | 9.8 | Webhook responses | 493 | | 12 | `code` | `scheduleTrigger` | 585 | 290 | 10.3 | Scheduled automation | 494 | | 13 | `manualTrigger` | `set` | 569 | 290 | 8.6 | Simple manual workflows | 495 | | 14 | `if` | `set` | 545 | 291 | 13.0 | Conditional data setting | 496 | | 15 | `httpRequest` | `respondToWebhook` | 534 | 263 | 10.0 | API-based webhooks | 497 | | 16 | `webhook` | `set` | 516 | 289 | 9.9 | Webhook data processing | 498 | | 17 | `httpRequest` | `scheduleTrigger` | 511 | 270 | 10.4 | Scheduled API calls | 499 | | 18 | `webhook` | `if` | 477 | 277 | 12.7 | Conditional webhooks | 500 | | 19 | `code` | `googleSheets` | 475 | 212 | 11.4 | Sheets data processing | 501 | | 20 | `agent` | `lmChatOpenAi` | 475 | 222 | 10.1 | AI agent workflows | 502 | 503 | ### **5.6 Pattern Recognition** 504 | 505 | **Pattern A: The "Transform-and-Call" Pattern** 506 | - `code + httpRequest` (1,646 workflows) 507 | - **Flow**: Prepare data → Call API → Process response 508 | - **Use cases**: Integration automation, data synchronization 509 | 510 | **Pattern B: The "Webhook Handler" Pattern** 511 | - `webhook + respondToWebhook` (1,043 workflows) 512 | - **Flow**: Receive webhook → Process → Respond 513 | - **Use cases**: Event-driven automation, API endpoints 514 | 515 | **Pattern C: The "Conditional Integration" Pattern** 516 | - `httpRequest + if + set` (combined ~2,000 workflows) 517 | - **Flow**: Call API → Check response → Transform data 518 | - **Use cases**: Smart integrations, error handling 519 | 520 | **Pattern D: The "AI Agent" Pattern** 521 | - `agent + lmChatOpenAi + memoryBufferWindow` (475 workflows) 522 | - **Flow**: AI agent with memory and LLM 523 | - **Use cases**: Conversational AI, intelligent automation 524 | 525 | ### **5.7 Node Usage in Complex Workflows** 526 | 527 | **Top nodes in complex workflows (21+ nodes):** 528 | 529 | | Rank | Node Type | Count | Avg Size | 530 | |------|-----------|-------|----------| 531 | | 1 | `code` | 139 | 26.7 | 532 | | 2 | `httpRequest` | 125 | 27.6 | 533 | | 3 | `if` | 111 | 27.4 | 534 | | 4 | `set` | 87 | 28.0 | 535 | | 5 | `switch` | 74 | 27.9 | 536 | | 6 | `webhook` | 68 | 29.5 | 537 | | 7 | `merge` | 62 | 27.1 | 538 | | 8 | `splitInBatches` | 50 | 25.6 | 539 | 540 | **Insight**: Complex workflows use the same core nodes, just more of them 541 | - Control flow (if/switch) for complex logic 542 | - Data manipulation (code/set) for transformations 543 | - Integration (httpRequest) for external systems 544 | 545 | --- 546 | 547 | ## **6. PLATFORM & VERSION DISTRIBUTION** 548 | 549 | ### **6.1 Platform Breakdown** 550 | 551 | **Top 20 Configurations:** 552 | 553 | | Platform | Arch | Version | Sessions | Users | % | 554 | |----------|------|---------|----------|-------|---| 555 | | **Linux** | x64 | 2.14.0 | 1,488 | 242 | 34% | 556 | | Linux | arm64 | 2.14.0 | 190 | 135 | 4% | 557 | | **Windows** | x64 | 2.14.1 | 115 | 79 | 3% | 558 | | Windows | x64 | 2.14.1 | 95 | 53 | 2% | 559 | | **macOS** | arm64 | 2.14.1 | 77 | 51 | 2% | 560 | | Windows | x64 | 2.14.1 | 70 | 41 | 2% | 561 | | macOS | arm64 | 2.14.1 | 68 | 43 | 2% | 562 | | Windows | x64 | 2.14.1 | 60 | 46 | 1% | 563 | | Linux | x64 | 2.14.5 | 54 | 30 | 1% | 564 | | macOS | arm64 | 2.14.1 | 51 | 26 | 1% | 565 | 566 | **Aggregated by Platform:** 567 | - **Linux**: ~40% (1,678 sessions) - Docker, cloud VMs, CI/CD 568 | - **Windows**: ~25% (multiple versions) 569 | - **macOS**: ~15% (mostly M1/M2 Macs) 570 | - **Other/Unknown**: ~20% 571 | 572 | ### **6.2 Version Distribution** 573 | 574 | | Version | Total Sessions | Estimated Users | Release Date | 575 | |---------|----------------|-----------------|--------------| 576 | | 2.14.0 | 1,678 | 377 | Sep 26 | 577 | | 2.14.1 | 780+ | 500+ | Sep 26 | 578 | | 2.14.2 | ~50 | ~40 | Sep 29 | 579 | | 2.14.3 | ~30 | ~25 | Sep 29 | 580 | | 2.14.4 | 29 | 27 | Sep 30 | 581 | | 2.14.5 | 54 | 30 | Sep 30 | 582 | | 2.14.6 | 74 | 56 | Oct 1 | 583 | 584 | ### **6.3 Critical Findings** 585 | 586 | **FINDING #1: Majority stuck on 2.14.0 (37% of sessions)** 587 | - 1,678 sessions on v2.14.0 588 | - **Problem**: This version has known issues: 589 | - TypeError fixes incomplete (CHANGELOG 2.14.0) 590 | - Validation false positives (fixed in 2.14.2) 591 | - Template sanitization issues (fixed in 2.14.3) 592 | 593 | **FINDING #2: Slow version adoption** 594 | - Only 74 sessions on latest v2.14.6 (Oct 1 release) 595 | - Only 54 sessions on v2.14.5 (Sep 30 release) 596 | - **Gap**: Users not upgrading despite bug fixes 597 | 598 | **FINDING #3: Linux dominates (40% of sessions)** 599 | - Likely Docker deployments 600 | - CI/CD integration 601 | - Cloud VMs (AWS, GCP, Azure) 602 | - **Implication**: Containerization is working well 603 | 604 | **FINDING #4: Node.js version fragmentation** 605 | - v22.20.0: Most common 606 | - v22.19.0: Second most common 607 | - v22.18.0, v22.17.0, v22.14.0: Long tail 608 | - **No compatibility issues reported** (good) 609 | 610 | ### **6.4 Recommendations** 611 | 612 | **R1: Version update notifications** 613 | - Add update checker to MCP server 614 | - Notify users of critical fixes 615 | - Show CHANGELOG diff between versions 616 | 617 | **R2: Docker image optimization** 618 | - Pre-build for Linux x64 + arm64 619 | - Multi-stage builds for smaller images 620 | - Automatic version pinning 621 | 622 | **R3: Breaking change policy** 623 | - Clear migration guides for version updates 624 | - Deprecation warnings before breaking changes 625 | - Backward compatibility period (2 releases minimum) 626 | 627 | --- 628 | 629 | ## **7. ERROR PATTERNS & ROOT CAUSES** 630 | 631 | ### **7.1 TypeError Cascade in Node Tools** 632 | 633 | **Error Distribution (from error logs):** 634 | 635 | | Tool | TypeError Count | Affected Users | % Failure | 636 | |------|-----------------|----------------|-----------| 637 | | `get_node_essentials` | ~350 | 10+ | 9.8% | 638 | | `get_node_info` | ~250 | 12+ | 17.7% | 639 | | `get_node_documentation` | ~100 | 8+ | 7.1% | 640 | | **TOTAL** | ~700 | ~30 | **varies** | 641 | 642 | **From CHANGELOG 2.14.0:** 643 | > "Fixed TypeErrors in `get_node_info`, `get_node_essentials`, and `get_node_documentation` tools that were affecting 50% of calls" 644 | > "Added null safety checks for undefined node properties" 645 | 646 | **Analysis:** 647 | - Fix in 2.14.0 reduced failures from **50% → 10-18%** 648 | - **Residual issues remain** (700+ errors in 6 days) 649 | - **Root causes**: 650 | 1. Incomplete null safety for edge cases 651 | 2. Nodes with unusual/legacy structure 652 | 3. Missing properties in database 653 | 4. Nested property access without guards 654 | 655 | **Example Error Pattern:** 656 | ```javascript 657 | // Fails when node.properties.description is undefined 658 | const description = node.properties.description.text; 659 | 660 | // Should be: 661 | const description = node?.properties?.description?.text ?? 'No description'; 662 | ``` 663 | 664 | ### **7.2 ValidationError in Workflow Creation** 665 | 666 | **Pattern:** 667 | - `n8n_create_workflow`: 237 failures (3.9% failure rate) 668 | - Error type: `ValidationError` 669 | - Context: `tool_execution` 670 | 671 | **Root causes (from validation analysis):** 672 | 1. **Node type prefix errors** (80% of validation errors) 673 | - `nodes-base.X` vs `n8n-nodes-base.X` 674 | - See Section 2 for full analysis 675 | 676 | 2. **Missing connections** (10% of validation errors) 677 | - "Multi-node workflow has no connections" 678 | - Valid error, but could provide better guidance 679 | 680 | 3. **Duplicate node IDs** (5% of validation errors) 681 | - "Duplicate node ID: 'undefined'" 682 | - Likely bug in node generation 683 | 684 | 4. **Other validation issues** (5%) 685 | - Missing required properties 686 | - Invalid property values 687 | - Connection reference errors 688 | 689 | ### **7.3 Task Discovery Failures** 690 | 691 | **Pattern:** 692 | - `get_node_for_task`: 109 failures (27.8% failure rate) 693 | - Error type: Not specified (likely "No matching task found") 694 | - **Highest failure rate of any tool** 695 | 696 | **Probable causes:** 697 | 1. **Limited task library** - Only ~30-40 predefined tasks 698 | 2. **No fuzzy matching** - Exact task name required 699 | 3. **Poor task descriptions** - AI agents can't guess correct task name 700 | 4. **Missing fallback** - Doesn't suggest alternatives 701 | 702 | **Example failing queries (inferred):** 703 | - "send email notification" (might need "email_send" task) 704 | - "process json data" (might need "json_parse" task) 705 | - "schedule workflow" (might need "cron_trigger" task) 706 | 707 | ### **7.4 Other Error Patterns** 708 | 709 | **Low-frequency errors (<10 occurrences):** 710 | - Tool not found errors (misspelled tool names) 711 | - Invalid parameters (wrong types, missing required fields) 712 | - Network timeouts (n8n API unavailable) 713 | - Database errors (SQLite lock issues) 714 | 715 | **These are expected in production and handled gracefully** 716 | 717 | --- 718 | 719 | ## **8. PRIORITIZED REFACTORING RECOMMENDATIONS** 720 | 721 | ### **P0 - CRITICAL (Fix Immediately)** 722 | 723 | --- 724 | 725 | #### **P0-R1: Auto-normalize node type prefixes** 726 | 727 | **Problem**: 4,800+ validation errors from `nodes-base.X` vs `n8n-nodes-base.X` 728 | 729 | **Impact**: 730 | - Eliminates **80% of all validation errors** 731 | - Improves AI agent experience significantly 732 | - Reduces token waste from retry attempts 733 | - Unblocks hundreds of users 734 | 735 | **Solution**: 736 | ```typescript 737 | // src/services/workflow-validator.ts 738 | 739 | export function normalizeNodeTypes(workflow: any): any { 740 | const normalized = { ...workflow }; 741 | 742 | if (normalized.nodes) { 743 | normalized.nodes = normalized.nodes.map((node: any) => ({ 744 | ...node, 745 | type: normalizeNodeType(node.type) 746 | })); 747 | } 748 | 749 | return normalized; 750 | } 751 | 752 | function normalizeNodeType(type: string): string { 753 | // Fix common AI-generated abbreviations 754 | const prefixMap: Record<string, string> = { 755 | 'nodes-base.': 'n8n-nodes-base.', 756 | 'nodes-langchain.': '@n8n/n8n-nodes-langchain.', 757 | 'n8n-nodes-langchain.': '@n8n/n8n-nodes-langchain.' 758 | }; 759 | 760 | for (const [short, full] of Object.entries(prefixMap)) { 761 | if (type.startsWith(short)) { 762 | return type.replace(short, full); 763 | } 764 | } 765 | 766 | return type; 767 | } 768 | ``` 769 | 770 | **Apply in handlers**: 771 | ```typescript 772 | // src/mcp/handlers-n8n-manager.ts 773 | 774 | export async function handleCreateWorkflow(params: any): Promise<McpToolResponse> { 775 | // Normalize before validation 776 | const normalizedWorkflow = normalizeNodeTypes(params); 777 | 778 | const validation = await validateWorkflow(normalizedWorkflow); 779 | if (!validation.valid) { 780 | return { success: false, error: validation.errors }; 781 | } 782 | 783 | // Use normalized workflow 784 | return await createWorkflow(normalizedWorkflow); 785 | } 786 | 787 | export async function handleUpdateFullWorkflow(params: any): Promise<McpToolResponse> { 788 | const normalizedWorkflow = normalizeNodeTypes(params); 789 | // ... rest of handler 790 | } 791 | ``` 792 | 793 | **Testing**: 794 | ```typescript 795 | // tests/unit/services/workflow-normalizer.test.ts 796 | 797 | describe('normalizeNodeTypes', () => { 798 | it('should normalize nodes-base prefix', () => { 799 | const workflow = { 800 | nodes: [ 801 | { id: '1', type: 'nodes-base.set', parameters: {} } 802 | ] 803 | }; 804 | 805 | const result = normalizeNodeTypes(workflow); 806 | expect(result.nodes[0].type).toBe('n8n-nodes-base.set'); 807 | }); 808 | 809 | it('should handle already-normalized types', () => { 810 | const workflow = { 811 | nodes: [ 812 | { id: '1', type: 'n8n-nodes-base.set', parameters: {} } 813 | ] 814 | }; 815 | 816 | const result = normalizeNodeTypes(workflow); 817 | expect(result.nodes[0].type).toBe('n8n-nodes-base.set'); 818 | }); 819 | 820 | it('should normalize langchain nodes', () => { 821 | const workflow = { 822 | nodes: [ 823 | { id: '1', type: 'nodes-langchain.agent', parameters: {} } 824 | ] 825 | }; 826 | 827 | const result = normalizeNodeTypes(workflow); 828 | expect(result.nodes[0].type).toBe('@n8n/n8n-nodes-langchain.agent'); 829 | }); 830 | }); 831 | ``` 832 | 833 | **Effort**: 2-4 hours 834 | **Risk**: Low (only adds normalization, doesn't change validation logic) 835 | **Files**: 836 | - `src/services/workflow-validator.ts` (new helper) 837 | - `src/mcp/handlers-n8n-manager.ts` (apply in handlers) 838 | - `tests/unit/services/workflow-normalizer.test.ts` (new tests) 839 | 840 | --- 841 | 842 | #### **P0-R2: Complete null-safety audit of node information tools** 843 | 844 | **Problem**: 10-18% failure rate for `get_node_essentials`, `get_node_info`, `get_node_documentation` 845 | 846 | **Impact**: 847 | - Reduce TypeError failures from **10-18% → <1%** 848 | - Improve reliability of most-used tools 849 | - Prevent AI agent blocking on node discovery 850 | 851 | **Solution**: Comprehensive null-safety refactor 852 | 853 | **Step 1: Update repository methods** 854 | ```typescript 855 | // src/database/node-repository.ts 856 | 857 | export class NodeRepository { 858 | getNodeEssentials(nodeType: string): NodeEssentials | null { 859 | try { 860 | const node = this.db.prepare(` 861 | SELECT * FROM nodes WHERE type = ? 862 | `).get(nodeType); 863 | 864 | if (!node) { 865 | return null; 866 | } 867 | 868 | // Safe property access with defaults 869 | return { 870 | type: node.type ?? 'unknown', 871 | displayName: node.displayName ?? node.name ?? 'Unknown Node', 872 | description: this.extractDescription(node), 873 | category: node.category ?? 'Uncategorized', 874 | icon: node.icon ?? 'fa:question', 875 | inputs: this.parseJSON(node.inputs, []), 876 | outputs: this.parseJSON(node.outputs, []), 877 | properties: this.extractEssentialProperties(node) 878 | }; 879 | } catch (error) { 880 | this.logger.error('Error getting node essentials', { nodeType, error }); 881 | return null; 882 | } 883 | } 884 | 885 | private extractDescription(node: any): string { 886 | // Try multiple possible locations for description 887 | if (node.description) return node.description; 888 | if (node.properties?.description?.text) return node.properties.description.text; 889 | if (node.properties?.description) return node.properties.description; 890 | if (node.subtitle) return node.subtitle; 891 | return 'No description available'; 892 | } 893 | 894 | private parseJSON<T>(value: any, defaultValue: T): T { 895 | if (!value) return defaultValue; 896 | try { 897 | return typeof value === 'string' ? JSON.parse(value) : value; 898 | } catch { 899 | return defaultValue; 900 | } 901 | } 902 | 903 | private extractEssentialProperties(node: any): any[] { 904 | try { 905 | const props = this.parseJSON(node.properties, []); 906 | return props.map((prop: any) => ({ 907 | name: prop.name ?? 'unknown', 908 | displayName: prop.displayName ?? prop.name ?? 'Unknown', 909 | type: prop.type ?? 'string', 910 | required: prop.required ?? false, 911 | default: prop.default ?? null, 912 | description: prop.description ?? '' 913 | })); 914 | } catch { 915 | return []; 916 | } 917 | } 918 | } 919 | ``` 920 | 921 | **Step 2: Update handlers with error handling** 922 | ```typescript 923 | // src/mcp/handlers.ts 924 | 925 | export async function handleGetNodeEssentials(params: { nodeType: string }): Promise<McpToolResponse> { 926 | const { nodeType } = params; 927 | 928 | // Validate input 929 | if (!nodeType || typeof nodeType !== 'string') { 930 | return { 931 | success: false, 932 | error: 'Invalid nodeType parameter' 933 | }; 934 | } 935 | 936 | try { 937 | const essentials = await nodeRepository.getNodeEssentials(nodeType); 938 | 939 | if (!essentials) { 940 | return { 941 | success: false, 942 | error: `Node type "${nodeType}" not found. Use search_nodes to find available nodes.` 943 | }; 944 | } 945 | 946 | return { 947 | success: true, 948 | data: essentials 949 | }; 950 | } catch (error) { 951 | return { 952 | success: false, 953 | error: `Failed to get node essentials: ${error.message}` 954 | }; 955 | } 956 | } 957 | ``` 958 | 959 | **Step 3: Add comprehensive tests** 960 | ```typescript 961 | // tests/unit/database/node-repository.test.ts 962 | 963 | describe('NodeRepository - Null Safety', () => { 964 | it('should handle node with missing description', () => { 965 | const node = { type: 'test.node', name: 'Test' }; 966 | db.prepare('INSERT INTO nodes VALUES (?, ?, NULL)').run(node.type, node.name); 967 | 968 | const result = repository.getNodeEssentials('test.node'); 969 | expect(result).not.toBeNull(); 970 | expect(result.description).toBe('No description available'); 971 | }); 972 | 973 | it('should handle node with malformed JSON properties', () => { 974 | const node = { type: 'test.node', properties: 'invalid json' }; 975 | db.prepare('INSERT INTO nodes VALUES (?, ?, ?)').run(node.type, node.name, node.properties); 976 | 977 | const result = repository.getNodeEssentials('test.node'); 978 | expect(result).not.toBeNull(); 979 | expect(result.properties).toEqual([]); 980 | }); 981 | 982 | it('should return null for non-existent node', () => { 983 | const result = repository.getNodeEssentials('non.existent'); 984 | expect(result).toBeNull(); 985 | }); 986 | }); 987 | ``` 988 | 989 | **Effort**: 1 day (8 hours) 990 | **Risk**: Medium (changes core repository methods, needs thorough testing) 991 | **Files**: 992 | - `src/database/node-repository.ts` (refactor) 993 | - `src/mcp/handlers.ts` (update error handling) 994 | - `tests/unit/database/node-repository.test.ts` (comprehensive tests) 995 | - `tests/unit/mcp/handlers.test.ts` (update tests) 996 | 997 | **Success Criteria**: 998 | - `get_node_essentials` failure rate: 10% → <1% 999 | - `get_node_info` failure rate: 18% → <1% 1000 | - `get_node_documentation` failure rate: 7% → <1% 1001 | - 100% test coverage for null cases 1002 | 1003 | --- 1004 | 1005 | #### **P0-R3: Improve `get_node_for_task` success rate** 1006 | 1007 | **Problem**: 27.8% failure rate (worst-performing tool) 1008 | 1009 | **Impact**: 1010 | - Improve from **72% → 95%+ success rate** 1011 | - Enable AI agents to discover nodes by task description 1012 | - Reduce frustration when agents don't know exact node names 1013 | 1014 | **Solution**: Multi-pronged enhancement 1015 | 1016 | **Step 1: Expand task library** 1017 | ```typescript 1018 | // src/services/task-templates.ts 1019 | 1020 | export const TASK_LIBRARY = { 1021 | // HTTP & API 1022 | 'http_request': { node: 'n8n-nodes-base.httpRequest', priority: 1 }, 1023 | 'api_call': { node: 'n8n-nodes-base.httpRequest', priority: 1 }, 1024 | 'make_http_request': { node: 'n8n-nodes-base.httpRequest', priority: 1 }, 1025 | 'fetch_data': { node: 'n8n-nodes-base.httpRequest', priority: 2 }, 1026 | 1027 | // Data transformation 1028 | 'transform_data': { node: 'n8n-nodes-base.code', priority: 1 }, 1029 | 'process_json': { node: 'n8n-nodes-base.code', priority: 1 }, 1030 | 'manipulate_data': { node: 'n8n-nodes-base.set', priority: 2 }, 1031 | 'set_values': { node: 'n8n-nodes-base.set', priority: 1 }, 1032 | 1033 | // Email 1034 | 'send_email': { node: 'n8n-nodes-base.emailSend', priority: 1 }, 1035 | 'email_notification': { node: 'n8n-nodes-base.emailSend', priority: 1 }, 1036 | 'receive_email': { node: 'n8n-nodes-base.emailReadImap', priority: 1 }, 1037 | 1038 | // Webhooks 1039 | 'webhook': { node: 'n8n-nodes-base.webhook', priority: 1 }, 1040 | 'receive_webhook': { node: 'n8n-nodes-base.webhook', priority: 1 }, 1041 | 'respond_to_webhook': { node: 'n8n-nodes-base.respondToWebhook', priority: 1 }, 1042 | 1043 | // ... expand to 100+ tasks 1044 | }; 1045 | ``` 1046 | 1047 | **Step 2: Add fuzzy matching** 1048 | ```typescript 1049 | // src/services/discovery-service.ts 1050 | 1051 | import Fuse from 'fuse.js'; 1052 | 1053 | export class DiscoveryService { 1054 | private taskIndex: Fuse<TaskDefinition>; 1055 | 1056 | constructor() { 1057 | // Build fuzzy search index 1058 | this.taskIndex = new Fuse(Object.entries(TASK_LIBRARY), { 1059 | keys: ['0'], // Task name 1060 | threshold: 0.4, // Allow some typos 1061 | distance: 100 1062 | }); 1063 | } 1064 | 1065 | getNodeForTask(taskDescription: string): TaskMatch[] { 1066 | // 1. Try exact match 1067 | const exactMatch = TASK_LIBRARY[taskDescription.toLowerCase()]; 1068 | if (exactMatch) { 1069 | return [{ 1070 | node: exactMatch.node, 1071 | confidence: 1.0, 1072 | reason: 'Exact task match' 1073 | }]; 1074 | } 1075 | 1076 | // 2. Try fuzzy match 1077 | const fuzzyMatches = this.taskIndex.search(taskDescription); 1078 | if (fuzzyMatches.length > 0) { 1079 | return fuzzyMatches.slice(0, 3).map(match => ({ 1080 | node: match.item[1].node, 1081 | confidence: 1 - match.score, 1082 | reason: `Similar to "${match.item[0]}"` 1083 | })); 1084 | } 1085 | 1086 | // 3. Fallback to keyword search in node descriptions 1087 | return this.searchNodesByKeywords(taskDescription); 1088 | } 1089 | 1090 | private searchNodesByKeywords(query: string): TaskMatch[] { 1091 | // Use existing search_nodes functionality 1092 | const results = nodeRepository.searchNodes(query, { limit: 3 }); 1093 | return results.map(node => ({ 1094 | node: node.type, 1095 | confidence: 0.5, 1096 | reason: `Found by keyword search: "${query}"` 1097 | })); 1098 | } 1099 | } 1100 | ``` 1101 | 1102 | **Step 3: Return multiple suggestions** 1103 | ```typescript 1104 | // src/mcp/handlers.ts 1105 | 1106 | export async function handleGetNodeForTask(params: { task: string }): Promise<McpToolResponse> { 1107 | const { task } = params; 1108 | 1109 | try { 1110 | const matches = discoveryService.getNodeForTask(task); 1111 | 1112 | if (matches.length === 0) { 1113 | return { 1114 | success: false, 1115 | error: `No node found for task "${task}". Try search_nodes with keywords instead.`, 1116 | suggestions: [ 1117 | 'Use search_nodes to explore available nodes', 1118 | 'Check list_tasks to see predefined task names' 1119 | ] 1120 | }; 1121 | } 1122 | 1123 | return { 1124 | success: true, 1125 | data: { 1126 | primaryMatch: matches[0], 1127 | alternativeMatches: matches.slice(1), 1128 | totalMatches: matches.length 1129 | } 1130 | }; 1131 | } catch (error) { 1132 | return { 1133 | success: false, 1134 | error: `Failed to find node for task: ${error.message}` 1135 | }; 1136 | } 1137 | } 1138 | ``` 1139 | 1140 | **Step 4: Testing** 1141 | ```typescript 1142 | // tests/unit/services/discovery-service.test.ts 1143 | 1144 | describe('DiscoveryService - Task Matching', () => { 1145 | it('should find exact task match', () => { 1146 | const result = service.getNodeForTask('send_email'); 1147 | expect(result[0].node).toBe('n8n-nodes-base.emailSend'); 1148 | expect(result[0].confidence).toBe(1.0); 1149 | }); 1150 | 1151 | it('should handle typos with fuzzy matching', () => { 1152 | const result = service.getNodeForTask('send emial'); // typo 1153 | expect(result[0].node).toBe('n8n-nodes-base.emailSend'); 1154 | expect(result[0].confidence).toBeGreaterThan(0.7); 1155 | }); 1156 | 1157 | it('should return multiple suggestions', () => { 1158 | const result = service.getNodeForTask('process data'); 1159 | expect(result.length).toBeGreaterThan(1); 1160 | expect(result).toContainEqual( 1161 | expect.objectContaining({ node: 'n8n-nodes-base.code' }) 1162 | ); 1163 | }); 1164 | 1165 | it('should fallback to keyword search', () => { 1166 | const result = service.getNodeForTask('sheets manipulation'); 1167 | expect(result.some(r => r.node.includes('googleSheets'))).toBe(true); 1168 | }); 1169 | }); 1170 | ``` 1171 | 1172 | **Effort**: 3 days (24 hours) 1173 | - Day 1: Expand task library (100+ tasks) 1174 | - Day 2: Implement fuzzy matching 1175 | - Day 3: Testing and refinement 1176 | 1177 | **Risk**: Low (enhances existing functionality) 1178 | **Dependencies**: `fuse.js` (fuzzy search library) 1179 | **Files**: 1180 | - `src/services/task-templates.ts` (expand library) 1181 | - `src/services/discovery-service.ts` (new service) 1182 | - `src/mcp/handlers.ts` (update handler) 1183 | - `tests/unit/services/discovery-service.test.ts` (comprehensive tests) 1184 | 1185 | **Success Criteria**: 1186 | - `get_node_for_task` success rate: 72% → 95% 1187 | - Average confidence score: >0.8 1188 | - Multiple suggestions returned for ambiguous queries 1189 | 1190 | ### **Immediate Actions (This Week) - P0** 1191 | 1192 | **1. Auto-normalize node type prefixes (P0-R1)** 1193 | - **Impact**: Eliminate 4,800 validation errors (80% of all errors) 1194 | - **Effort**: 2-4 hours 1195 | - **Files**: `workflow-validator.ts`, `handlers-n8n-manager.ts` 1196 | - **ROI**: ⭐⭐⭐⭐⭐ (Massive impact, minimal effort) 1197 | 1198 | **2. Complete null-safety audit (P0-R2)** 1199 | - **Impact**: Fix 10-18% TypeError failures 1200 | - **Effort**: 1 day (8 hours) 1201 | - **Files**: `node-repository.ts`, `handlers.ts` 1202 | - **ROI**: ⭐⭐⭐⭐⭐ (Critical reliability improvement) 1203 | 1204 | **3. Expand task discovery library (P0-R3)** 1205 | - **Impact**: Improve 72% → 95% success rate 1206 | - **Effort**: 3 days (24 hours) 1207 | - **Files**: `task-templates.ts`, `discovery-service.ts` 1208 | - **ROI**: ⭐⭐⭐⭐ (High value for task-based workflows) 1209 | 1210 | **Expected Overall Impact**: 1211 | - Error rate: 5-10% → <2% 1212 | - User satisfaction: Significant improvement 1213 | - Support burden: Reduced by 50% 1214 | ```