n8n-atom-mcp 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +220 -0
- package/LICENSE +21 -0
- package/README.md +78 -0
- package/data/nodes.db +0 -0
- package/dist/community/community-node-fetcher.d.ts +118 -0
- package/dist/community/community-node-fetcher.d.ts.map +1 -0
- package/dist/community/community-node-fetcher.js +239 -0
- package/dist/community/community-node-fetcher.js.map +1 -0
- package/dist/community/community-node-service.d.ts +42 -0
- package/dist/community/community-node-service.d.ts.map +1 -0
- package/dist/community/community-node-service.js +210 -0
- package/dist/community/community-node-service.js.map +1 -0
- package/dist/community/documentation-batch-processor.d.ts +33 -0
- package/dist/community/documentation-batch-processor.d.ts.map +1 -0
- package/dist/community/documentation-batch-processor.js +156 -0
- package/dist/community/documentation-batch-processor.js.map +1 -0
- package/dist/community/documentation-generator.d.ts +65 -0
- package/dist/community/documentation-generator.d.ts.map +1 -0
- package/dist/community/documentation-generator.js +220 -0
- package/dist/community/documentation-generator.js.map +1 -0
- package/dist/community/index.d.ts +5 -0
- package/dist/community/index.d.ts.map +1 -0
- package/dist/community/index.js +14 -0
- package/dist/community/index.js.map +1 -0
- package/dist/config/n8n-api.d.ts +15 -0
- package/dist/config/n8n-api.d.ts.map +1 -0
- package/dist/config/n8n-api.js +53 -0
- package/dist/config/n8n-api.js.map +1 -0
- package/dist/constants/type-structures.d.ts +123 -0
- package/dist/constants/type-structures.d.ts.map +1 -0
- package/dist/constants/type-structures.js +654 -0
- package/dist/constants/type-structures.js.map +1 -0
- package/dist/database/database-adapter.d.ts +33 -0
- package/dist/database/database-adapter.d.ts.map +1 -0
- package/dist/database/database-adapter.js +420 -0
- package/dist/database/database-adapter.js.map +1 -0
- package/dist/database/node-repository.d.ts +152 -0
- package/dist/database/node-repository.d.ts.map +1 -0
- package/dist/database/node-repository.js +791 -0
- package/dist/database/node-repository.js.map +1 -0
- package/dist/errors/validation-service-error.d.ts +10 -0
- package/dist/errors/validation-service-error.d.ts.map +1 -0
- package/dist/errors/validation-service-error.js +26 -0
- package/dist/errors/validation-service-error.js.map +1 -0
- package/dist/http-server-single-session.d.ts +52 -0
- package/dist/http-server-single-session.d.ts.map +1 -0
- package/dist/http-server-single-session.js +1180 -0
- package/dist/http-server-single-session.js.map +1 -0
- package/dist/http-server.d.ts +9 -0
- package/dist/http-server.d.ts.map +1 -0
- package/dist/http-server.js +481 -0
- package/dist/http-server.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/loaders/node-loader.d.ts +11 -0
- package/dist/loaders/node-loader.d.ts.map +1 -0
- package/dist/loaders/node-loader.js +79 -0
- package/dist/loaders/node-loader.js.map +1 -0
- package/dist/mappers/docs-mapper.d.ts +7 -0
- package/dist/mappers/docs-mapper.d.ts.map +1 -0
- package/dist/mappers/docs-mapper.js +106 -0
- package/dist/mappers/docs-mapper.js.map +1 -0
- package/dist/mcp/handlers-n8n-manager.d.ts +58 -0
- package/dist/mcp/handlers-n8n-manager.d.ts.map +1 -0
- package/dist/mcp/handlers-n8n-manager.js +3299 -0
- package/dist/mcp/handlers-n8n-manager.js.map +1 -0
- package/dist/mcp/handlers-workflow-diff.d.ts +5 -0
- package/dist/mcp/handlers-workflow-diff.d.ts.map +1 -0
- package/dist/mcp/handlers-workflow-diff.js +461 -0
- package/dist/mcp/handlers-workflow-diff.js.map +1 -0
- package/dist/mcp/index.d.ts +3 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +228 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/server.d.ts +82 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +3006 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/stdio-wrapper.d.ts +3 -0
- package/dist/mcp/stdio-wrapper.d.ts.map +1 -0
- package/dist/mcp/stdio-wrapper.js +81 -0
- package/dist/mcp/stdio-wrapper.js.map +1 -0
- package/dist/mcp/tool-docs/configuration/get-node.d.ts +3 -0
- package/dist/mcp/tool-docs/configuration/get-node.d.ts.map +1 -0
- package/dist/mcp/tool-docs/configuration/get-node.js +90 -0
- package/dist/mcp/tool-docs/configuration/get-node.js.map +1 -0
- package/dist/mcp/tool-docs/configuration/index.d.ts +2 -0
- package/dist/mcp/tool-docs/configuration/index.d.ts.map +1 -0
- package/dist/mcp/tool-docs/configuration/index.js +6 -0
- package/dist/mcp/tool-docs/configuration/index.js.map +1 -0
- package/dist/mcp/tool-docs/discovery/index.d.ts +2 -0
- package/dist/mcp/tool-docs/discovery/index.d.ts.map +1 -0
- package/dist/mcp/tool-docs/discovery/index.js +6 -0
- package/dist/mcp/tool-docs/discovery/index.js.map +1 -0
- package/dist/mcp/tool-docs/discovery/search-nodes.d.ts +3 -0
- package/dist/mcp/tool-docs/discovery/search-nodes.d.ts.map +1 -0
- package/dist/mcp/tool-docs/discovery/search-nodes.js +70 -0
- package/dist/mcp/tool-docs/discovery/search-nodes.js.map +1 -0
- package/dist/mcp/tool-docs/guides/ai-agents-guide.d.ts +3 -0
- package/dist/mcp/tool-docs/guides/ai-agents-guide.d.ts.map +1 -0
- package/dist/mcp/tool-docs/guides/ai-agents-guide.js +739 -0
- package/dist/mcp/tool-docs/guides/ai-agents-guide.js.map +1 -0
- package/dist/mcp/tool-docs/guides/index.d.ts +2 -0
- package/dist/mcp/tool-docs/guides/index.d.ts.map +1 -0
- package/dist/mcp/tool-docs/guides/index.js +6 -0
- package/dist/mcp/tool-docs/guides/index.js.map +1 -0
- package/dist/mcp/tool-docs/index.d.ts +4 -0
- package/dist/mcp/tool-docs/index.d.ts.map +1 -0
- package/dist/mcp/tool-docs/index.js +34 -0
- package/dist/mcp/tool-docs/index.js.map +1 -0
- package/dist/mcp/tool-docs/system/index.d.ts +3 -0
- package/dist/mcp/tool-docs/system/index.d.ts.map +1 -0
- package/dist/mcp/tool-docs/system/index.js +8 -0
- package/dist/mcp/tool-docs/system/index.js.map +1 -0
- package/dist/mcp/tool-docs/system/n8n-diagnostic.d.ts +3 -0
- package/dist/mcp/tool-docs/system/n8n-diagnostic.d.ts.map +1 -0
- package/dist/mcp/tool-docs/system/n8n-diagnostic.js +99 -0
- package/dist/mcp/tool-docs/system/n8n-diagnostic.js.map +1 -0
- package/dist/mcp/tool-docs/system/n8n-health-check.d.ts +3 -0
- package/dist/mcp/tool-docs/system/n8n-health-check.d.ts.map +1 -0
- package/dist/mcp/tool-docs/system/n8n-health-check.js +102 -0
- package/dist/mcp/tool-docs/system/n8n-health-check.js.map +1 -0
- package/dist/mcp/tool-docs/system/n8n-list-available-tools.d.ts +3 -0
- package/dist/mcp/tool-docs/system/n8n-list-available-tools.d.ts.map +1 -0
- package/dist/mcp/tool-docs/system/n8n-list-available-tools.js +75 -0
- package/dist/mcp/tool-docs/system/n8n-list-available-tools.js.map +1 -0
- package/dist/mcp/tool-docs/system/tools-documentation.d.ts +3 -0
- package/dist/mcp/tool-docs/system/tools-documentation.d.ts.map +1 -0
- package/dist/mcp/tool-docs/system/tools-documentation.js +65 -0
- package/dist/mcp/tool-docs/system/tools-documentation.js.map +1 -0
- package/dist/mcp/tool-docs/templates/get-template.d.ts +3 -0
- package/dist/mcp/tool-docs/templates/get-template.d.ts.map +1 -0
- package/dist/mcp/tool-docs/templates/get-template.js +84 -0
- package/dist/mcp/tool-docs/templates/get-template.js.map +1 -0
- package/dist/mcp/tool-docs/templates/index.d.ts +3 -0
- package/dist/mcp/tool-docs/templates/index.d.ts.map +1 -0
- package/dist/mcp/tool-docs/templates/index.js +8 -0
- package/dist/mcp/tool-docs/templates/index.js.map +1 -0
- package/dist/mcp/tool-docs/templates/search-templates.d.ts +3 -0
- package/dist/mcp/tool-docs/templates/search-templates.d.ts.map +1 -0
- package/dist/mcp/tool-docs/templates/search-templates.js +143 -0
- package/dist/mcp/tool-docs/templates/search-templates.js.map +1 -0
- package/dist/mcp/tool-docs/types.d.ts +32 -0
- package/dist/mcp/tool-docs/types.d.ts.map +1 -0
- package/dist/mcp/tool-docs/types.js +3 -0
- package/dist/mcp/tool-docs/types.js.map +1 -0
- package/dist/mcp/tool-docs/validation/index.d.ts +3 -0
- package/dist/mcp/tool-docs/validation/index.d.ts.map +1 -0
- package/dist/mcp/tool-docs/validation/index.js +8 -0
- package/dist/mcp/tool-docs/validation/index.js.map +1 -0
- package/dist/mcp/tool-docs/validation/validate-node.d.ts +3 -0
- package/dist/mcp/tool-docs/validation/validate-node.d.ts.map +1 -0
- package/dist/mcp/tool-docs/validation/validate-node.js +82 -0
- package/dist/mcp/tool-docs/validation/validate-node.js.map +1 -0
- package/dist/mcp/tool-docs/validation/validate-workflow.d.ts +3 -0
- package/dist/mcp/tool-docs/validation/validate-workflow.d.ts.map +1 -0
- package/dist/mcp/tool-docs/validation/validate-workflow.js +86 -0
- package/dist/mcp/tool-docs/validation/validate-workflow.js.map +1 -0
- package/dist/mcp/tool-docs/workflow_management/index.d.ts +13 -0
- package/dist/mcp/tool-docs/workflow_management/index.d.ts.map +1 -0
- package/dist/mcp/tool-docs/workflow_management/index.js +28 -0
- package/dist/mcp/tool-docs/workflow_management/index.js.map +1 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-autofix-workflow.d.ts +3 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-autofix-workflow.d.ts.map +1 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-autofix-workflow.js +162 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-autofix-workflow.js.map +1 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-create-workflow.d.ts +3 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-create-workflow.d.ts.map +1 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-create-workflow.js +102 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-create-workflow.js.map +1 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-delete-workflow.d.ts +3 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-delete-workflow.d.ts.map +1 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-delete-workflow.js +52 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-delete-workflow.js.map +1 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-deploy-template.d.ts +3 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-deploy-template.d.ts.map +1 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-deploy-template.js +73 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-deploy-template.js.map +1 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-executions.d.ts +3 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-executions.d.ts.map +1 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-executions.js +109 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-executions.js.map +1 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-get-workflow.d.ts +3 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-get-workflow.d.ts.map +1 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-get-workflow.js +68 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-get-workflow.js.map +1 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-list-workflows.d.ts +3 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-list-workflows.d.ts.map +1 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-list-workflows.js +57 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-list-workflows.js.map +1 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-test-workflow.d.ts +3 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-test-workflow.d.ts.map +1 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-test-workflow.js +140 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-test-workflow.js.map +1 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-update-full-workflow.d.ts +3 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-update-full-workflow.d.ts.map +1 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-update-full-workflow.js +61 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-update-full-workflow.js.map +1 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-update-partial-workflow.d.ts +3 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-update-partial-workflow.d.ts.map +1 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-update-partial-workflow.js +420 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-update-partial-workflow.js.map +1 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-validate-workflow.d.ts +3 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-validate-workflow.d.ts.map +1 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-validate-workflow.js +73 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-validate-workflow.js.map +1 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-workflow-versions.d.ts +3 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-workflow-versions.d.ts.map +1 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-workflow-versions.js +170 -0
- package/dist/mcp/tool-docs/workflow_management/n8n-workflow-versions.js.map +1 -0
- package/dist/mcp/tools-documentation.d.ts +6 -0
- package/dist/mcp/tools-documentation.d.ts.map +1 -0
- package/dist/mcp/tools-documentation.js +682 -0
- package/dist/mcp/tools-documentation.js.map +1 -0
- package/dist/mcp/tools-file-operations.d.ts +154 -0
- package/dist/mcp/tools-file-operations.d.ts.map +1 -0
- package/dist/mcp/tools-file-operations.js +213 -0
- package/dist/mcp/tools-file-operations.js.map +1 -0
- package/dist/mcp/tools-n8n-friendly.d.ts +6 -0
- package/dist/mcp/tools-n8n-friendly.d.ts.map +1 -0
- package/dist/mcp/tools-n8n-friendly.js +89 -0
- package/dist/mcp/tools-n8n-friendly.js.map +1 -0
- package/dist/mcp/tools-n8n-manager.d.ts +3 -0
- package/dist/mcp/tools-n8n-manager.d.ts.map +1 -0
- package/dist/mcp/tools-n8n-manager.js +1320 -0
- package/dist/mcp/tools-n8n-manager.js.map +1 -0
- package/dist/mcp/tools.d.ts +3 -0
- package/dist/mcp/tools.d.ts.map +1 -0
- package/dist/mcp/tools.js +424 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/mcp/workflow-examples.d.ts +76 -0
- package/dist/mcp/workflow-examples.d.ts.map +1 -0
- package/dist/mcp/workflow-examples.js +111 -0
- package/dist/mcp/workflow-examples.js.map +1 -0
- package/dist/mcp-engine.d.ts +36 -0
- package/dist/mcp-engine.d.ts.map +1 -0
- package/dist/mcp-engine.js +77 -0
- package/dist/mcp-engine.js.map +1 -0
- package/dist/mcp-tools-engine.d.ts +47 -0
- package/dist/mcp-tools-engine.d.ts.map +1 -0
- package/dist/mcp-tools-engine.js +89 -0
- package/dist/mcp-tools-engine.js.map +1 -0
- package/dist/n8n/MCPApi.credentials.d.ts +8 -0
- package/dist/n8n/MCPApi.credentials.d.ts.map +1 -0
- package/dist/n8n/MCPApi.credentials.js +53 -0
- package/dist/n8n/MCPApi.credentials.js.map +1 -0
- package/dist/n8n/MCPNode.node.d.ts +13 -0
- package/dist/n8n/MCPNode.node.d.ts.map +1 -0
- package/dist/n8n/MCPNode.node.js +260 -0
- package/dist/n8n/MCPNode.node.js.map +1 -0
- package/dist/parsers/node-parser.d.ts +38 -0
- package/dist/parsers/node-parser.d.ts.map +1 -0
- package/dist/parsers/node-parser.js +250 -0
- package/dist/parsers/node-parser.js.map +1 -0
- package/dist/parsers/property-extractor.d.ts +11 -0
- package/dist/parsers/property-extractor.d.ts.map +1 -0
- package/dist/parsers/property-extractor.js +172 -0
- package/dist/parsers/property-extractor.js.map +1 -0
- package/dist/parsers/simple-parser.d.ts +25 -0
- package/dist/parsers/simple-parser.d.ts.map +1 -0
- package/dist/parsers/simple-parser.js +212 -0
- package/dist/parsers/simple-parser.js.map +1 -0
- package/dist/scripts/debug-http-search.d.ts +3 -0
- package/dist/scripts/debug-http-search.d.ts.map +1 -0
- package/dist/scripts/debug-http-search.js +57 -0
- package/dist/scripts/debug-http-search.js.map +1 -0
- package/dist/scripts/extract-from-docker.d.ts +3 -0
- package/dist/scripts/extract-from-docker.d.ts.map +1 -0
- package/dist/scripts/extract-from-docker.js +210 -0
- package/dist/scripts/extract-from-docker.js.map +1 -0
- package/dist/scripts/fetch-community-nodes.d.ts +3 -0
- package/dist/scripts/fetch-community-nodes.d.ts.map +1 -0
- package/dist/scripts/fetch-community-nodes.js +114 -0
- package/dist/scripts/fetch-community-nodes.js.map +1 -0
- package/dist/scripts/fetch-templates-robust.d.ts +4 -0
- package/dist/scripts/fetch-templates-robust.d.ts.map +1 -0
- package/dist/scripts/fetch-templates-robust.js +132 -0
- package/dist/scripts/fetch-templates-robust.js.map +1 -0
- package/dist/scripts/fetch-templates.d.ts +4 -0
- package/dist/scripts/fetch-templates.d.ts.map +1 -0
- package/dist/scripts/fetch-templates.js +411 -0
- package/dist/scripts/fetch-templates.js.map +1 -0
- package/dist/scripts/generate-community-docs.d.ts +3 -0
- package/dist/scripts/generate-community-docs.d.ts.map +1 -0
- package/dist/scripts/generate-community-docs.js +183 -0
- package/dist/scripts/generate-community-docs.js.map +1 -0
- package/dist/scripts/migrate-readme-columns.d.ts +2 -0
- package/dist/scripts/migrate-readme-columns.d.ts.map +1 -0
- package/dist/scripts/migrate-readme-columns.js +62 -0
- package/dist/scripts/migrate-readme-columns.js.map +1 -0
- package/dist/scripts/rebuild-database.d.ts +4 -0
- package/dist/scripts/rebuild-database.d.ts.map +1 -0
- package/dist/scripts/rebuild-database.js +95 -0
- package/dist/scripts/rebuild-database.js.map +1 -0
- package/dist/scripts/rebuild-optimized.d.ts +3 -0
- package/dist/scripts/rebuild-optimized.d.ts.map +1 -0
- package/dist/scripts/rebuild-optimized.js +198 -0
- package/dist/scripts/rebuild-optimized.js.map +1 -0
- package/dist/scripts/rebuild.d.ts +3 -0
- package/dist/scripts/rebuild.d.ts.map +1 -0
- package/dist/scripts/rebuild.js +258 -0
- package/dist/scripts/rebuild.js.map +1 -0
- package/dist/scripts/sanitize-templates.d.ts +3 -0
- package/dist/scripts/sanitize-templates.d.ts.map +1 -0
- package/dist/scripts/sanitize-templates.js +88 -0
- package/dist/scripts/sanitize-templates.js.map +1 -0
- package/dist/scripts/seed-canonical-ai-examples.d.ts +4 -0
- package/dist/scripts/seed-canonical-ai-examples.d.ts.map +1 -0
- package/dist/scripts/seed-canonical-ai-examples.js +121 -0
- package/dist/scripts/seed-canonical-ai-examples.js.map +1 -0
- package/dist/scripts/test-autofix-documentation.d.ts +3 -0
- package/dist/scripts/test-autofix-documentation.d.ts.map +1 -0
- package/dist/scripts/test-autofix-documentation.js +103 -0
- package/dist/scripts/test-autofix-documentation.js.map +1 -0
- package/dist/scripts/test-autofix-workflow.d.ts +2 -0
- package/dist/scripts/test-autofix-workflow.d.ts.map +1 -0
- package/dist/scripts/test-autofix-workflow.js +223 -0
- package/dist/scripts/test-autofix-workflow.js.map +1 -0
- package/dist/scripts/test-execution-filtering.d.ts +3 -0
- package/dist/scripts/test-execution-filtering.d.ts.map +1 -0
- package/dist/scripts/test-execution-filtering.js +206 -0
- package/dist/scripts/test-execution-filtering.js.map +1 -0
- package/dist/scripts/test-node-suggestions.d.ts +3 -0
- package/dist/scripts/test-node-suggestions.d.ts.map +1 -0
- package/dist/scripts/test-node-suggestions.js +165 -0
- package/dist/scripts/test-node-suggestions.js.map +1 -0
- package/dist/scripts/test-protocol-negotiation.d.ts +3 -0
- package/dist/scripts/test-protocol-negotiation.d.ts.map +1 -0
- package/dist/scripts/test-protocol-negotiation.js +154 -0
- package/dist/scripts/test-protocol-negotiation.js.map +1 -0
- package/dist/scripts/test-summary.d.ts +3 -0
- package/dist/scripts/test-summary.d.ts.map +1 -0
- package/dist/scripts/test-summary.js +77 -0
- package/dist/scripts/test-summary.js.map +1 -0
- package/dist/scripts/test-telemetry-mutations-verbose.d.ts +2 -0
- package/dist/scripts/test-telemetry-mutations-verbose.d.ts.map +1 -0
- package/dist/scripts/test-telemetry-mutations-verbose.js +133 -0
- package/dist/scripts/test-telemetry-mutations-verbose.js.map +1 -0
- package/dist/scripts/test-telemetry-mutations.d.ts +2 -0
- package/dist/scripts/test-telemetry-mutations.d.ts.map +1 -0
- package/dist/scripts/test-telemetry-mutations.js +129 -0
- package/dist/scripts/test-telemetry-mutations.js.map +1 -0
- package/dist/scripts/test-webhook-autofix.d.ts +3 -0
- package/dist/scripts/test-webhook-autofix.d.ts.map +1 -0
- package/dist/scripts/test-webhook-autofix.js +117 -0
- package/dist/scripts/test-webhook-autofix.js.map +1 -0
- package/dist/scripts/validate.d.ts +3 -0
- package/dist/scripts/validate.d.ts.map +1 -0
- package/dist/scripts/validate.js +121 -0
- package/dist/scripts/validate.js.map +1 -0
- package/dist/scripts/validation-summary.d.ts +3 -0
- package/dist/scripts/validation-summary.d.ts.map +1 -0
- package/dist/scripts/validation-summary.js +135 -0
- package/dist/scripts/validation-summary.js.map +1 -0
- package/dist/services/ai-node-validator.d.ts +12 -0
- package/dist/services/ai-node-validator.d.ts.map +1 -0
- package/dist/services/ai-node-validator.js +429 -0
- package/dist/services/ai-node-validator.js.map +1 -0
- package/dist/services/ai-tool-validators.d.ts +58 -0
- package/dist/services/ai-tool-validators.d.ts.map +1 -0
- package/dist/services/ai-tool-validators.js +438 -0
- package/dist/services/ai-tool-validators.js.map +1 -0
- package/dist/services/breaking-change-detector.d.ts +38 -0
- package/dist/services/breaking-change-detector.d.ts.map +1 -0
- package/dist/services/breaking-change-detector.js +184 -0
- package/dist/services/breaking-change-detector.js.map +1 -0
- package/dist/services/breaking-changes-registry.d.ts +28 -0
- package/dist/services/breaking-changes-registry.d.ts.map +1 -0
- package/dist/services/breaking-changes-registry.js +200 -0
- package/dist/services/breaking-changes-registry.js.map +1 -0
- package/dist/services/community-package-service.d.ts +69 -0
- package/dist/services/community-package-service.d.ts.map +1 -0
- package/dist/services/community-package-service.js +202 -0
- package/dist/services/community-package-service.js.map +1 -0
- package/dist/services/confidence-scorer.d.ts +24 -0
- package/dist/services/confidence-scorer.d.ts.map +1 -0
- package/dist/services/confidence-scorer.js +139 -0
- package/dist/services/confidence-scorer.js.map +1 -0
- package/dist/services/config-validator.d.ts +49 -0
- package/dist/services/config-validator.d.ts.map +1 -0
- package/dist/services/config-validator.js +724 -0
- package/dist/services/config-validator.js.map +1 -0
- package/dist/services/enhanced-config-validator.d.ts +54 -0
- package/dist/services/enhanced-config-validator.d.ts.map +1 -0
- package/dist/services/enhanced-config-validator.js +789 -0
- package/dist/services/enhanced-config-validator.js.map +1 -0
- package/dist/services/error-execution-processor.d.ts +9 -0
- package/dist/services/error-execution-processor.d.ts.map +1 -0
- package/dist/services/error-execution-processor.js +380 -0
- package/dist/services/error-execution-processor.js.map +1 -0
- package/dist/services/example-generator.d.ts +14 -0
- package/dist/services/example-generator.d.ts.map +1 -0
- package/dist/services/example-generator.js +970 -0
- package/dist/services/example-generator.js.map +1 -0
- package/dist/services/execution-processor.d.ts +8 -0
- package/dist/services/execution-processor.d.ts.map +1 -0
- package/dist/services/execution-processor.js +381 -0
- package/dist/services/execution-processor.js.map +1 -0
- package/dist/services/expression-format-validator.d.ts +33 -0
- package/dist/services/expression-format-validator.d.ts.map +1 -0
- package/dist/services/expression-format-validator.js +209 -0
- package/dist/services/expression-format-validator.js.map +1 -0
- package/dist/services/expression-validator.d.ts +27 -0
- package/dist/services/expression-validator.d.ts.map +1 -0
- package/dist/services/expression-validator.js +187 -0
- package/dist/services/expression-validator.js.map +1 -0
- package/dist/services/installed-node-sync-service.d.ts +32 -0
- package/dist/services/installed-node-sync-service.d.ts.map +1 -0
- package/dist/services/installed-node-sync-service.js +190 -0
- package/dist/services/installed-node-sync-service.js.map +1 -0
- package/dist/services/n8n-api-client.d.ts +48 -0
- package/dist/services/n8n-api-client.d.ts.map +1 -0
- package/dist/services/n8n-api-client.js +458 -0
- package/dist/services/n8n-api-client.js.map +1 -0
- package/dist/services/n8n-file-format.d.ts +58 -0
- package/dist/services/n8n-file-format.d.ts.map +1 -0
- package/dist/services/n8n-file-format.js +203 -0
- package/dist/services/n8n-file-format.js.map +1 -0
- package/dist/services/n8n-validation.d.ts +273 -0
- package/dist/services/n8n-validation.d.ts.map +1 -0
- package/dist/services/n8n-validation.js +491 -0
- package/dist/services/n8n-validation.js.map +1 -0
- package/dist/services/n8n-version.d.ts +23 -0
- package/dist/services/n8n-version.d.ts.map +1 -0
- package/dist/services/n8n-version.js +151 -0
- package/dist/services/n8n-version.js.map +1 -0
- package/dist/services/node-documentation-service.d.ts +70 -0
- package/dist/services/node-documentation-service.d.ts.map +1 -0
- package/dist/services/node-documentation-service.js +518 -0
- package/dist/services/node-documentation-service.js.map +1 -0
- package/dist/services/node-migration-service.d.ts +44 -0
- package/dist/services/node-migration-service.d.ts.map +1 -0
- package/dist/services/node-migration-service.js +231 -0
- package/dist/services/node-migration-service.js.map +1 -0
- package/dist/services/node-sanitizer.d.ts +5 -0
- package/dist/services/node-sanitizer.d.ts.map +1 -0
- package/dist/services/node-sanitizer.js +225 -0
- package/dist/services/node-sanitizer.js.map +1 -0
- package/dist/services/node-similarity-service.d.ts +51 -0
- package/dist/services/node-similarity-service.d.ts.map +1 -0
- package/dist/services/node-similarity-service.js +352 -0
- package/dist/services/node-similarity-service.js.map +1 -0
- package/dist/services/node-specific-validators.d.ts +37 -0
- package/dist/services/node-specific-validators.d.ts.map +1 -0
- package/dist/services/node-specific-validators.js +1331 -0
- package/dist/services/node-specific-validators.js.map +1 -0
- package/dist/services/node-version-service.d.ts +63 -0
- package/dist/services/node-version-service.d.ts.map +1 -0
- package/dist/services/node-version-service.js +215 -0
- package/dist/services/node-version-service.js.map +1 -0
- package/dist/services/operation-similarity-service.d.ts +32 -0
- package/dist/services/operation-similarity-service.d.ts.map +1 -0
- package/dist/services/operation-similarity-service.js +341 -0
- package/dist/services/operation-similarity-service.js.map +1 -0
- package/dist/services/post-update-validator.d.ts +59 -0
- package/dist/services/post-update-validator.d.ts.map +1 -0
- package/dist/services/post-update-validator.js +231 -0
- package/dist/services/post-update-validator.js.map +1 -0
- package/dist/services/property-dependencies.d.ts +36 -0
- package/dist/services/property-dependencies.d.ts.map +1 -0
- package/dist/services/property-dependencies.js +168 -0
- package/dist/services/property-dependencies.js.map +1 -0
- package/dist/services/property-filter.d.ts +44 -0
- package/dist/services/property-filter.d.ts.map +1 -0
- package/dist/services/property-filter.js +395 -0
- package/dist/services/property-filter.js.map +1 -0
- package/dist/services/resource-similarity-service.d.ts +33 -0
- package/dist/services/resource-similarity-service.d.ts.map +1 -0
- package/dist/services/resource-similarity-service.js +358 -0
- package/dist/services/resource-similarity-service.js.map +1 -0
- package/dist/services/sqlite-storage-service.d.ts +11 -0
- package/dist/services/sqlite-storage-service.d.ts.map +1 -0
- package/dist/services/sqlite-storage-service.js +74 -0
- package/dist/services/sqlite-storage-service.js.map +1 -0
- package/dist/services/task-templates.d.ts +27 -0
- package/dist/services/task-templates.d.ts.map +1 -0
- package/dist/services/task-templates.js +1397 -0
- package/dist/services/task-templates.js.map +1 -0
- package/dist/services/tool-variant-generator.d.ts +10 -0
- package/dist/services/tool-variant-generator.d.ts.map +1 -0
- package/dist/services/tool-variant-generator.js +93 -0
- package/dist/services/tool-variant-generator.js.map +1 -0
- package/dist/services/type-structure-service.d.ts +23 -0
- package/dist/services/type-structure-service.d.ts.map +1 -0
- package/dist/services/type-structure-service.js +109 -0
- package/dist/services/type-structure-service.js.map +1 -0
- package/dist/services/universal-expression-validator.d.ts +20 -0
- package/dist/services/universal-expression-validator.d.ts.map +1 -0
- package/dist/services/universal-expression-validator.js +192 -0
- package/dist/services/universal-expression-validator.js.map +1 -0
- package/dist/services/workflow-analytics-service.d.ts +34 -0
- package/dist/services/workflow-analytics-service.d.ts.map +1 -0
- package/dist/services/workflow-analytics-service.js +144 -0
- package/dist/services/workflow-analytics-service.js.map +1 -0
- package/dist/services/workflow-auto-fixer.d.ts +74 -0
- package/dist/services/workflow-auto-fixer.d.ts.map +1 -0
- package/dist/services/workflow-auto-fixer.js +557 -0
- package/dist/services/workflow-auto-fixer.js.map +1 -0
- package/dist/services/workflow-comparison.d.ts +13 -0
- package/dist/services/workflow-comparison.d.ts.map +1 -0
- package/dist/services/workflow-comparison.js +306 -0
- package/dist/services/workflow-comparison.js.map +1 -0
- package/dist/services/workflow-debugging-service.d.ts +62 -0
- package/dist/services/workflow-debugging-service.d.ts.map +1 -0
- package/dist/services/workflow-debugging-service.js +153 -0
- package/dist/services/workflow-debugging-service.js.map +1 -0
- package/dist/services/workflow-diff-engine.d.ts +45 -0
- package/dist/services/workflow-diff-engine.d.ts.map +1 -0
- package/dist/services/workflow-diff-engine.js +830 -0
- package/dist/services/workflow-diff-engine.js.map +1 -0
- package/dist/services/workflow-validator.d.ts +112 -0
- package/dist/services/workflow-validator.d.ts.map +1 -0
- package/dist/services/workflow-validator.js +1328 -0
- package/dist/services/workflow-validator.js.map +1 -0
- package/dist/services/workflow-versioning-service.d.ts +102 -0
- package/dist/services/workflow-versioning-service.d.ts.map +1 -0
- package/dist/services/workflow-versioning-service.js +264 -0
- package/dist/services/workflow-versioning-service.js.map +1 -0
- package/dist/telemetry/batch-processor.d.ts +34 -0
- package/dist/telemetry/batch-processor.d.ts.map +1 -0
- package/dist/telemetry/batch-processor.js +337 -0
- package/dist/telemetry/batch-processor.js.map +1 -0
- package/dist/telemetry/config-manager.d.ts +32 -0
- package/dist/telemetry/config-manager.d.ts.map +1 -0
- package/dist/telemetry/config-manager.js +289 -0
- package/dist/telemetry/config-manager.js.map +1 -0
- package/dist/telemetry/early-error-logger.d.ts +26 -0
- package/dist/telemetry/early-error-logger.d.ts.map +1 -0
- package/dist/telemetry/early-error-logger.js +187 -0
- package/dist/telemetry/early-error-logger.js.map +1 -0
- package/dist/telemetry/error-sanitization-utils.d.ts +2 -0
- package/dist/telemetry/error-sanitization-utils.d.ts.map +1 -0
- package/dist/telemetry/error-sanitization-utils.js +37 -0
- package/dist/telemetry/error-sanitization-utils.js.map +1 -0
- package/dist/telemetry/error-sanitizer.d.ts +4 -0
- package/dist/telemetry/error-sanitizer.d.ts.map +1 -0
- package/dist/telemetry/error-sanitizer.js +45 -0
- package/dist/telemetry/error-sanitizer.js.map +1 -0
- package/dist/telemetry/event-tracker.d.ts +71 -0
- package/dist/telemetry/event-tracker.d.ts.map +1 -0
- package/dist/telemetry/event-tracker.js +356 -0
- package/dist/telemetry/event-tracker.js.map +1 -0
- package/dist/telemetry/event-validator.d.ts +78 -0
- package/dist/telemetry/event-validator.d.ts.map +1 -0
- package/dist/telemetry/event-validator.js +227 -0
- package/dist/telemetry/event-validator.js.map +1 -0
- package/dist/telemetry/index.d.ts +5 -0
- package/dist/telemetry/index.d.ts.map +1 -0
- package/dist/telemetry/index.js +11 -0
- package/dist/telemetry/index.js.map +1 -0
- package/dist/telemetry/intent-classifier.d.ts +11 -0
- package/dist/telemetry/intent-classifier.d.ts.map +1 -0
- package/dist/telemetry/intent-classifier.js +141 -0
- package/dist/telemetry/intent-classifier.js.map +1 -0
- package/dist/telemetry/intent-sanitizer.d.ts +9 -0
- package/dist/telemetry/intent-sanitizer.d.ts.map +1 -0
- package/dist/telemetry/intent-sanitizer.js +103 -0
- package/dist/telemetry/intent-sanitizer.js.map +1 -0
- package/dist/telemetry/mutation-tracker.d.ts +15 -0
- package/dist/telemetry/mutation-tracker.d.ts.map +1 -0
- package/dist/telemetry/mutation-tracker.js +177 -0
- package/dist/telemetry/mutation-tracker.js.map +1 -0
- package/dist/telemetry/mutation-types.d.ts +106 -0
- package/dist/telemetry/mutation-types.d.ts.map +1 -0
- package/dist/telemetry/mutation-types.js +18 -0
- package/dist/telemetry/mutation-types.js.map +1 -0
- package/dist/telemetry/mutation-validator.d.ts +20 -0
- package/dist/telemetry/mutation-validator.d.ts.map +1 -0
- package/dist/telemetry/mutation-validator.js +144 -0
- package/dist/telemetry/mutation-validator.js.map +1 -0
- package/dist/telemetry/performance-monitor.d.ts +113 -0
- package/dist/telemetry/performance-monitor.d.ts.map +1 -0
- package/dist/telemetry/performance-monitor.js +208 -0
- package/dist/telemetry/performance-monitor.js.map +1 -0
- package/dist/telemetry/rate-limiter.d.ts +30 -0
- package/dist/telemetry/rate-limiter.d.ts.map +1 -0
- package/dist/telemetry/rate-limiter.js +103 -0
- package/dist/telemetry/rate-limiter.js.map +1 -0
- package/dist/telemetry/startup-checkpoints.d.ts +26 -0
- package/dist/telemetry/startup-checkpoints.d.ts.map +1 -0
- package/dist/telemetry/startup-checkpoints.js +65 -0
- package/dist/telemetry/startup-checkpoints.js.map +1 -0
- package/dist/telemetry/telemetry-error.d.ts +44 -0
- package/dist/telemetry/telemetry-error.d.ts.map +1 -0
- package/dist/telemetry/telemetry-error.js +153 -0
- package/dist/telemetry/telemetry-error.js.map +1 -0
- package/dist/telemetry/telemetry-manager.d.ts +130 -0
- package/dist/telemetry/telemetry-manager.d.ts.map +1 -0
- package/dist/telemetry/telemetry-manager.js +257 -0
- package/dist/telemetry/telemetry-manager.js.map +1 -0
- package/dist/telemetry/telemetry-types.d.ts +103 -0
- package/dist/telemetry/telemetry-types.d.ts.map +1 -0
- package/dist/telemetry/telemetry-types.js +29 -0
- package/dist/telemetry/telemetry-types.js.map +1 -0
- package/dist/telemetry/workflow-sanitizer.d.ts +34 -0
- package/dist/telemetry/workflow-sanitizer.d.ts.map +1 -0
- package/dist/telemetry/workflow-sanitizer.js +242 -0
- package/dist/telemetry/workflow-sanitizer.js.map +1 -0
- package/dist/templates/batch-processor.d.ts +35 -0
- package/dist/templates/batch-processor.d.ts.map +1 -0
- package/dist/templates/batch-processor.js +320 -0
- package/dist/templates/batch-processor.js.map +1 -0
- package/dist/templates/metadata-generator.d.ts +52 -0
- package/dist/templates/metadata-generator.d.ts.map +1 -0
- package/dist/templates/metadata-generator.js +252 -0
- package/dist/templates/metadata-generator.js.map +1 -0
- package/dist/templates/template-fetcher.d.ts +45 -0
- package/dist/templates/template-fetcher.d.ts.map +1 -0
- package/dist/templates/template-fetcher.js +122 -0
- package/dist/templates/template-fetcher.js.map +1 -0
- package/dist/templates/template-repository.d.ts +93 -0
- package/dist/templates/template-repository.d.ts.map +1 -0
- package/dist/templates/template-repository.js +644 -0
- package/dist/templates/template-repository.js.map +1 -0
- package/dist/templates/template-service.d.ts +79 -0
- package/dist/templates/template-service.d.ts.map +1 -0
- package/dist/templates/template-service.js +300 -0
- package/dist/templates/template-service.js.map +1 -0
- package/dist/triggers/handlers/base-handler.d.ts +21 -0
- package/dist/triggers/handlers/base-handler.d.ts.map +1 -0
- package/dist/triggers/handlers/base-handler.js +60 -0
- package/dist/triggers/handlers/base-handler.js.map +1 -0
- package/dist/triggers/handlers/chat-handler.d.ts +38 -0
- package/dist/triggers/handlers/chat-handler.d.ts.map +1 -0
- package/dist/triggers/handlers/chat-handler.js +129 -0
- package/dist/triggers/handlers/chat-handler.js.map +1 -0
- package/dist/triggers/handlers/form-handler.d.ts +35 -0
- package/dist/triggers/handlers/form-handler.d.ts.map +1 -0
- package/dist/triggers/handlers/form-handler.js +362 -0
- package/dist/triggers/handlers/form-handler.js.map +1 -0
- package/dist/triggers/handlers/webhook-handler.d.ts +38 -0
- package/dist/triggers/handlers/webhook-handler.d.ts.map +1 -0
- package/dist/triggers/handlers/webhook-handler.js +115 -0
- package/dist/triggers/handlers/webhook-handler.js.map +1 -0
- package/dist/triggers/index.d.ts +5 -0
- package/dist/triggers/index.d.ts.map +1 -0
- package/dist/triggers/index.js +14 -0
- package/dist/triggers/index.js.map +1 -0
- package/dist/triggers/trigger-detector.d.ts +6 -0
- package/dist/triggers/trigger-detector.d.ts.map +1 -0
- package/dist/triggers/trigger-detector.js +201 -0
- package/dist/triggers/trigger-detector.js.map +1 -0
- package/dist/triggers/trigger-registry.d.ts +18 -0
- package/dist/triggers/trigger-registry.d.ts.map +1 -0
- package/dist/triggers/trigger-registry.js +87 -0
- package/dist/triggers/trigger-registry.js.map +1 -0
- package/dist/triggers/types.d.ts +76 -0
- package/dist/triggers/types.d.ts.map +1 -0
- package/dist/triggers/types.js +3 -0
- package/dist/triggers/types.js.map +1 -0
- package/dist/types/index.d.ts +49 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +21 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/instance-context.d.ts +20 -0
- package/dist/types/instance-context.d.ts.map +1 -0
- package/dist/types/instance-context.js +153 -0
- package/dist/types/instance-context.js.map +1 -0
- package/dist/types/n8n-api.d.ts +376 -0
- package/dist/types/n8n-api.d.ts.map +1 -0
- package/dist/types/n8n-api.js +10 -0
- package/dist/types/n8n-api.js.map +1 -0
- package/dist/types/node-types.d.ts +19 -0
- package/dist/types/node-types.d.ts.map +1 -0
- package/dist/types/node-types.js +62 -0
- package/dist/types/node-types.js.map +1 -0
- package/dist/types/session-state.d.ts +15 -0
- package/dist/types/session-state.d.ts.map +1 -0
- package/dist/types/session-state.js +3 -0
- package/dist/types/session-state.js.map +1 -0
- package/dist/types/type-structures.d.ts +42 -0
- package/dist/types/type-structures.d.ts.map +1 -0
- package/dist/types/type-structures.js +32 -0
- package/dist/types/type-structures.js.map +1 -0
- package/dist/types/workflow-comparison.d.ts +51 -0
- package/dist/types/workflow-comparison.d.ts.map +1 -0
- package/dist/types/workflow-comparison.js +3 -0
- package/dist/types/workflow-comparison.js.map +1 -0
- package/dist/types/workflow-diff.d.ts +148 -0
- package/dist/types/workflow-diff.d.ts.map +1 -0
- package/dist/types/workflow-diff.js +15 -0
- package/dist/types/workflow-diff.js.map +1 -0
- package/dist/utils/auth.d.ts +13 -0
- package/dist/utils/auth.d.ts.map +1 -0
- package/dist/utils/auth.js +82 -0
- package/dist/utils/auth.js.map +1 -0
- package/dist/utils/bridge.d.ts +12 -0
- package/dist/utils/bridge.d.ts.map +1 -0
- package/dist/utils/bridge.js +127 -0
- package/dist/utils/bridge.js.map +1 -0
- package/dist/utils/cache-utils.d.ts +58 -0
- package/dist/utils/cache-utils.d.ts.map +1 -0
- package/dist/utils/cache-utils.js +243 -0
- package/dist/utils/cache-utils.js.map +1 -0
- package/dist/utils/console-manager.d.ts +10 -0
- package/dist/utils/console-manager.d.ts.map +1 -0
- package/dist/utils/console-manager.js +63 -0
- package/dist/utils/console-manager.js.map +1 -0
- package/dist/utils/documentation-fetcher.d.ts +2 -0
- package/dist/utils/documentation-fetcher.d.ts.map +1 -0
- package/dist/utils/documentation-fetcher.js +18 -0
- package/dist/utils/documentation-fetcher.js.map +1 -0
- package/dist/utils/enhanced-documentation-fetcher.d.ts +74 -0
- package/dist/utils/enhanced-documentation-fetcher.d.ts.map +1 -0
- package/dist/utils/enhanced-documentation-fetcher.js +521 -0
- package/dist/utils/enhanced-documentation-fetcher.js.map +1 -0
- package/dist/utils/error-handler.d.ts +24 -0
- package/dist/utils/error-handler.d.ts.map +1 -0
- package/dist/utils/error-handler.js +84 -0
- package/dist/utils/error-handler.js.map +1 -0
- package/dist/utils/example-generator.d.ts +8 -0
- package/dist/utils/example-generator.d.ts.map +1 -0
- package/dist/utils/example-generator.js +106 -0
- package/dist/utils/example-generator.js.map +1 -0
- package/dist/utils/expression-utils.d.ts +6 -0
- package/dist/utils/expression-utils.d.ts.map +1 -0
- package/dist/utils/expression-utils.js +47 -0
- package/dist/utils/expression-utils.js.map +1 -0
- package/dist/utils/fixed-collection-validator.d.ts +35 -0
- package/dist/utils/fixed-collection-validator.d.ts.map +1 -0
- package/dist/utils/fixed-collection-validator.js +358 -0
- package/dist/utils/fixed-collection-validator.js.map +1 -0
- package/dist/utils/logger.d.ts +33 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +101 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/mcp-client.d.ts +21 -0
- package/dist/utils/mcp-client.d.ts.map +1 -0
- package/dist/utils/mcp-client.js +96 -0
- package/dist/utils/mcp-client.js.map +1 -0
- package/dist/utils/n8n-errors.d.ts +27 -0
- package/dist/utils/n8n-errors.d.ts.map +1 -0
- package/dist/utils/n8n-errors.js +138 -0
- package/dist/utils/n8n-errors.js.map +1 -0
- package/dist/utils/node-classification.d.ts +5 -0
- package/dist/utils/node-classification.d.ts.map +1 -0
- package/dist/utils/node-classification.js +31 -0
- package/dist/utils/node-classification.js.map +1 -0
- package/dist/utils/node-source-extractor.d.ts +21 -0
- package/dist/utils/node-source-extractor.d.ts.map +1 -0
- package/dist/utils/node-source-extractor.js +377 -0
- package/dist/utils/node-source-extractor.js.map +1 -0
- package/dist/utils/node-type-normalizer.d.ts +17 -0
- package/dist/utils/node-type-normalizer.d.ts.map +1 -0
- package/dist/utils/node-type-normalizer.js +87 -0
- package/dist/utils/node-type-normalizer.js.map +1 -0
- package/dist/utils/node-type-utils.d.ts +12 -0
- package/dist/utils/node-type-utils.d.ts.map +1 -0
- package/dist/utils/node-type-utils.js +126 -0
- package/dist/utils/node-type-utils.js.map +1 -0
- package/dist/utils/node-utils.d.ts +4 -0
- package/dist/utils/node-utils.d.ts.map +1 -0
- package/dist/utils/node-utils.js +81 -0
- package/dist/utils/node-utils.js.map +1 -0
- package/dist/utils/npm-version-checker.d.ts +14 -0
- package/dist/utils/npm-version-checker.d.ts.map +1 -0
- package/dist/utils/npm-version-checker.js +125 -0
- package/dist/utils/npm-version-checker.js.map +1 -0
- package/dist/utils/protocol-version.d.ts +19 -0
- package/dist/utils/protocol-version.d.ts.map +1 -0
- package/dist/utils/protocol-version.js +95 -0
- package/dist/utils/protocol-version.js.map +1 -0
- package/dist/utils/simple-cache.d.ts +10 -0
- package/dist/utils/simple-cache.d.ts.map +1 -0
- package/dist/utils/simple-cache.js +42 -0
- package/dist/utils/simple-cache.js.map +1 -0
- package/dist/utils/ssrf-protection.d.ts +7 -0
- package/dist/utils/ssrf-protection.d.ts.map +1 -0
- package/dist/utils/ssrf-protection.js +118 -0
- package/dist/utils/ssrf-protection.js.map +1 -0
- package/dist/utils/template-node-resolver.d.ts +2 -0
- package/dist/utils/template-node-resolver.d.ts.map +1 -0
- package/dist/utils/template-node-resolver.js +161 -0
- package/dist/utils/template-node-resolver.js.map +1 -0
- package/dist/utils/template-sanitizer.d.ts +21 -0
- package/dist/utils/template-sanitizer.d.ts.map +1 -0
- package/dist/utils/template-sanitizer.js +126 -0
- package/dist/utils/template-sanitizer.js.map +1 -0
- package/dist/utils/url-detector.d.ts +9 -0
- package/dist/utils/url-detector.d.ts.map +1 -0
- package/dist/utils/url-detector.js +79 -0
- package/dist/utils/url-detector.js.map +1 -0
- package/dist/utils/validation-schemas.d.ts +32 -0
- package/dist/utils/validation-schemas.d.ts.map +1 -0
- package/dist/utils/validation-schemas.js +219 -0
- package/dist/utils/validation-schemas.js.map +1 -0
- package/dist/utils/version.d.ts +2 -0
- package/dist/utils/version.d.ts.map +1 -0
- package/dist/utils/version.js +18 -0
- package/dist/utils/version.js.map +1 -0
- package/package.json +178 -0
- package/package.runtime.json +24 -0
|
@@ -0,0 +1,3006 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.N8NDocumentationMCPServer = void 0;
|
|
40
|
+
const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
|
|
41
|
+
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
42
|
+
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
|
|
43
|
+
const fs_1 = require("fs");
|
|
44
|
+
const path_1 = __importDefault(require("path"));
|
|
45
|
+
const tools_1 = require("./tools");
|
|
46
|
+
const tools_n8n_manager_1 = require("./tools-n8n-manager");
|
|
47
|
+
const tools_n8n_friendly_1 = require("./tools-n8n-friendly");
|
|
48
|
+
const workflow_examples_1 = require("./workflow-examples");
|
|
49
|
+
const logger_1 = require("../utils/logger");
|
|
50
|
+
const node_repository_1 = require("../database/node-repository");
|
|
51
|
+
const database_adapter_1 = require("../database/database-adapter");
|
|
52
|
+
const property_filter_1 = require("../services/property-filter");
|
|
53
|
+
const task_templates_1 = require("../services/task-templates");
|
|
54
|
+
const config_validator_1 = require("../services/config-validator");
|
|
55
|
+
const enhanced_config_validator_1 = require("../services/enhanced-config-validator");
|
|
56
|
+
const property_dependencies_1 = require("../services/property-dependencies");
|
|
57
|
+
const type_structure_service_1 = require("../services/type-structure-service");
|
|
58
|
+
const simple_cache_1 = require("../utils/simple-cache");
|
|
59
|
+
const template_service_1 = require("../templates/template-service");
|
|
60
|
+
const workflow_validator_1 = require("../services/workflow-validator");
|
|
61
|
+
const n8n_api_1 = require("../config/n8n-api");
|
|
62
|
+
const n8nHandlers = __importStar(require("./handlers-n8n-manager"));
|
|
63
|
+
const handlers_workflow_diff_1 = require("./handlers-workflow-diff");
|
|
64
|
+
const tools_documentation_1 = require("./tools-documentation");
|
|
65
|
+
const version_1 = require("../utils/version");
|
|
66
|
+
const node_utils_1 = require("../utils/node-utils");
|
|
67
|
+
const node_type_normalizer_1 = require("../utils/node-type-normalizer");
|
|
68
|
+
const validation_schemas_1 = require("../utils/validation-schemas");
|
|
69
|
+
const protocol_version_1 = require("../utils/protocol-version");
|
|
70
|
+
const telemetry_1 = require("../telemetry");
|
|
71
|
+
const startup_checkpoints_1 = require("../telemetry/startup-checkpoints");
|
|
72
|
+
class N8NDocumentationMCPServer {
|
|
73
|
+
constructor(instanceContext, earlyLogger) {
|
|
74
|
+
this.db = null;
|
|
75
|
+
this.repository = null;
|
|
76
|
+
this.templateService = null;
|
|
77
|
+
this.cache = new simple_cache_1.SimpleCache();
|
|
78
|
+
this.clientInfo = null;
|
|
79
|
+
this.previousTool = null;
|
|
80
|
+
this.previousToolTimestamp = Date.now();
|
|
81
|
+
this.earlyLogger = null;
|
|
82
|
+
this.disabledToolsCache = null;
|
|
83
|
+
this.dbHealthChecked = false;
|
|
84
|
+
this.instanceContext = instanceContext;
|
|
85
|
+
this.earlyLogger = earlyLogger || null;
|
|
86
|
+
const envDbPath = process.env.NODE_DB_PATH;
|
|
87
|
+
let dbPath = null;
|
|
88
|
+
let possiblePaths = [];
|
|
89
|
+
if (envDbPath && (envDbPath === ':memory:' || (0, fs_1.existsSync)(envDbPath))) {
|
|
90
|
+
dbPath = envDbPath;
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
possiblePaths = [
|
|
94
|
+
path_1.default.join(process.cwd(), 'data', 'nodes.db'),
|
|
95
|
+
path_1.default.join(__dirname, '../../data', 'nodes.db'),
|
|
96
|
+
'./data/nodes.db'
|
|
97
|
+
];
|
|
98
|
+
for (const p of possiblePaths) {
|
|
99
|
+
if ((0, fs_1.existsSync)(p)) {
|
|
100
|
+
dbPath = p;
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (!dbPath) {
|
|
106
|
+
logger_1.logger.error('Database not found in any of the expected locations:', possiblePaths);
|
|
107
|
+
throw new Error('Database nodes.db not found. Please run npm run rebuild first.');
|
|
108
|
+
}
|
|
109
|
+
this.initialized = this.initializeDatabase(dbPath).then(() => {
|
|
110
|
+
if (this.earlyLogger) {
|
|
111
|
+
this.earlyLogger.logCheckpoint(startup_checkpoints_1.STARTUP_CHECKPOINTS.N8N_API_CHECKING);
|
|
112
|
+
}
|
|
113
|
+
const apiConfigured = (0, n8n_api_1.isN8nApiConfigured)();
|
|
114
|
+
const totalTools = apiConfigured ?
|
|
115
|
+
tools_1.n8nDocumentationToolsFinal.length + tools_n8n_manager_1.n8nManagementTools.length :
|
|
116
|
+
tools_1.n8nDocumentationToolsFinal.length;
|
|
117
|
+
logger_1.logger.info(`MCP server initialized with ${totalTools} tools (n8n API: ${apiConfigured ? 'configured' : 'not configured'})`);
|
|
118
|
+
if (this.earlyLogger) {
|
|
119
|
+
this.earlyLogger.logCheckpoint(startup_checkpoints_1.STARTUP_CHECKPOINTS.N8N_API_READY);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
logger_1.logger.info('Initializing n8n Documentation MCP server');
|
|
123
|
+
this.server = new index_js_1.Server({
|
|
124
|
+
name: 'n8n-documentation-mcp',
|
|
125
|
+
version: version_1.PROJECT_VERSION,
|
|
126
|
+
icons: [
|
|
127
|
+
{
|
|
128
|
+
src: "https://www.n8n-atom-mcp.com/logo.png",
|
|
129
|
+
mimeType: "image/png",
|
|
130
|
+
sizes: ["192x192"]
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
src: "https://www.n8n-atom-mcp.com/logo-128.png",
|
|
134
|
+
mimeType: "image/png",
|
|
135
|
+
sizes: ["128x128"]
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
src: "https://www.n8n-atom-mcp.com/logo-48.png",
|
|
139
|
+
mimeType: "image/png",
|
|
140
|
+
sizes: ["48x48"]
|
|
141
|
+
}
|
|
142
|
+
],
|
|
143
|
+
websiteUrl: "https://n8n-atom-mcp.com"
|
|
144
|
+
}, {
|
|
145
|
+
capabilities: {
|
|
146
|
+
tools: {},
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
this.setupHandlers();
|
|
150
|
+
}
|
|
151
|
+
async close() {
|
|
152
|
+
try {
|
|
153
|
+
await this.server.close();
|
|
154
|
+
this.cache.destroy();
|
|
155
|
+
if (this.db) {
|
|
156
|
+
try {
|
|
157
|
+
this.db.close();
|
|
158
|
+
}
|
|
159
|
+
catch (dbError) {
|
|
160
|
+
logger_1.logger.warn('Error closing database', {
|
|
161
|
+
error: dbError instanceof Error ? dbError.message : String(dbError)
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
this.db = null;
|
|
166
|
+
this.repository = null;
|
|
167
|
+
this.templateService = null;
|
|
168
|
+
this.earlyLogger = null;
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
logger_1.logger.warn('Error closing MCP server', { error: error instanceof Error ? error.message : String(error) });
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
async initializeDatabase(dbPath) {
|
|
175
|
+
try {
|
|
176
|
+
if (this.earlyLogger) {
|
|
177
|
+
this.earlyLogger.logCheckpoint(startup_checkpoints_1.STARTUP_CHECKPOINTS.DATABASE_CONNECTING);
|
|
178
|
+
}
|
|
179
|
+
logger_1.logger.debug('Database initialization starting...', { dbPath });
|
|
180
|
+
this.db = await (0, database_adapter_1.createDatabaseAdapter)(dbPath);
|
|
181
|
+
logger_1.logger.debug('Database adapter created');
|
|
182
|
+
if (dbPath === ':memory:') {
|
|
183
|
+
await this.initializeInMemorySchema();
|
|
184
|
+
logger_1.logger.debug('In-memory schema initialized');
|
|
185
|
+
}
|
|
186
|
+
this.repository = new node_repository_1.NodeRepository(this.db);
|
|
187
|
+
logger_1.logger.debug('Node repository initialized');
|
|
188
|
+
this.templateService = new template_service_1.TemplateService(this.db);
|
|
189
|
+
logger_1.logger.debug('Template service initialized');
|
|
190
|
+
enhanced_config_validator_1.EnhancedConfigValidator.initializeSimilarityServices(this.repository);
|
|
191
|
+
logger_1.logger.debug('Similarity services initialized');
|
|
192
|
+
if (this.earlyLogger) {
|
|
193
|
+
this.earlyLogger.logCheckpoint(startup_checkpoints_1.STARTUP_CHECKPOINTS.DATABASE_CONNECTED);
|
|
194
|
+
}
|
|
195
|
+
logger_1.logger.info(`Database initialized successfully from: ${dbPath}`);
|
|
196
|
+
}
|
|
197
|
+
catch (error) {
|
|
198
|
+
logger_1.logger.error('Failed to initialize database:', error);
|
|
199
|
+
throw new Error(`Failed to open database: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
async initializeInMemorySchema() {
|
|
203
|
+
if (!this.db)
|
|
204
|
+
return;
|
|
205
|
+
const schemaPath = path_1.default.join(__dirname, '../../src/database/schema.sql');
|
|
206
|
+
const schema = await fs_1.promises.readFile(schemaPath, 'utf-8');
|
|
207
|
+
const statements = this.parseSQLStatements(schema);
|
|
208
|
+
for (const statement of statements) {
|
|
209
|
+
if (statement.trim()) {
|
|
210
|
+
try {
|
|
211
|
+
this.db.exec(statement);
|
|
212
|
+
}
|
|
213
|
+
catch (error) {
|
|
214
|
+
logger_1.logger.error(`Failed to execute SQL statement: ${statement.substring(0, 100)}...`, error);
|
|
215
|
+
throw error;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
parseSQLStatements(sql) {
|
|
221
|
+
const statements = [];
|
|
222
|
+
let current = '';
|
|
223
|
+
let inBlock = false;
|
|
224
|
+
const lines = sql.split('\n');
|
|
225
|
+
for (const line of lines) {
|
|
226
|
+
const trimmed = line.trim().toUpperCase();
|
|
227
|
+
if (trimmed.startsWith('--') || trimmed === '') {
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
if (trimmed.includes('BEGIN')) {
|
|
231
|
+
inBlock = true;
|
|
232
|
+
}
|
|
233
|
+
current += line + '\n';
|
|
234
|
+
if (inBlock && trimmed === 'END;') {
|
|
235
|
+
statements.push(current.trim());
|
|
236
|
+
current = '';
|
|
237
|
+
inBlock = false;
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
if (!inBlock && trimmed.endsWith(';')) {
|
|
241
|
+
statements.push(current.trim());
|
|
242
|
+
current = '';
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
if (current.trim()) {
|
|
246
|
+
statements.push(current.trim());
|
|
247
|
+
}
|
|
248
|
+
return statements.filter(s => s.length > 0);
|
|
249
|
+
}
|
|
250
|
+
async ensureInitialized() {
|
|
251
|
+
await this.initialized;
|
|
252
|
+
if (!this.db || !this.repository) {
|
|
253
|
+
throw new Error('Database not initialized');
|
|
254
|
+
}
|
|
255
|
+
if (!this.dbHealthChecked) {
|
|
256
|
+
await this.validateDatabaseHealth();
|
|
257
|
+
this.dbHealthChecked = true;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
async validateDatabaseHealth() {
|
|
261
|
+
if (!this.db)
|
|
262
|
+
return;
|
|
263
|
+
try {
|
|
264
|
+
const nodeCount = this.db.prepare('SELECT COUNT(*) as count FROM nodes').get();
|
|
265
|
+
if (nodeCount.count === 0) {
|
|
266
|
+
logger_1.logger.error('CRITICAL: Database is empty - no nodes found! Please run: npm run rebuild');
|
|
267
|
+
throw new Error('Database is empty. Run "npm run rebuild" to populate node data.');
|
|
268
|
+
}
|
|
269
|
+
try {
|
|
270
|
+
const ftsExists = this.db.prepare(`
|
|
271
|
+
SELECT name FROM sqlite_master
|
|
272
|
+
WHERE type='table' AND name='nodes_fts'
|
|
273
|
+
`).get();
|
|
274
|
+
if (!ftsExists) {
|
|
275
|
+
logger_1.logger.warn('FTS5 table missing - search performance will be degraded. Please run: npm run rebuild');
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
const ftsCount = this.db.prepare('SELECT COUNT(*) as count FROM nodes_fts').get();
|
|
279
|
+
if (ftsCount.count === 0) {
|
|
280
|
+
logger_1.logger.warn('FTS5 index is empty - search will not work properly. Please run: npm run rebuild');
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
catch (ftsError) {
|
|
285
|
+
logger_1.logger.warn('FTS5 not available - using fallback search. For better performance, ensure better-sqlite3 is properly installed.');
|
|
286
|
+
}
|
|
287
|
+
logger_1.logger.info(`Database health check passed: ${nodeCount.count} nodes loaded`);
|
|
288
|
+
}
|
|
289
|
+
catch (error) {
|
|
290
|
+
logger_1.logger.error('Database health check failed:', error);
|
|
291
|
+
throw error;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
getDisabledTools() {
|
|
295
|
+
if (this.disabledToolsCache !== null) {
|
|
296
|
+
return this.disabledToolsCache;
|
|
297
|
+
}
|
|
298
|
+
let disabledToolsEnv = process.env.DISABLED_TOOLS || '';
|
|
299
|
+
if (!disabledToolsEnv) {
|
|
300
|
+
this.disabledToolsCache = new Set();
|
|
301
|
+
return this.disabledToolsCache;
|
|
302
|
+
}
|
|
303
|
+
if (disabledToolsEnv.length > 10000) {
|
|
304
|
+
logger_1.logger.warn(`DISABLED_TOOLS environment variable too long (${disabledToolsEnv.length} chars), truncating to 10000`);
|
|
305
|
+
disabledToolsEnv = disabledToolsEnv.substring(0, 10000);
|
|
306
|
+
}
|
|
307
|
+
let tools = disabledToolsEnv
|
|
308
|
+
.split(',')
|
|
309
|
+
.map(t => t.trim())
|
|
310
|
+
.filter(Boolean);
|
|
311
|
+
if (tools.length > 200) {
|
|
312
|
+
logger_1.logger.warn(`DISABLED_TOOLS contains ${tools.length} tools, limiting to first 200`);
|
|
313
|
+
tools = tools.slice(0, 200);
|
|
314
|
+
}
|
|
315
|
+
if (tools.length > 0) {
|
|
316
|
+
logger_1.logger.info(`Disabled tools configured: ${tools.join(', ')}`);
|
|
317
|
+
}
|
|
318
|
+
this.disabledToolsCache = new Set(tools);
|
|
319
|
+
return this.disabledToolsCache;
|
|
320
|
+
}
|
|
321
|
+
setupHandlers() {
|
|
322
|
+
this.server.setRequestHandler(types_js_1.InitializeRequestSchema, async (request) => {
|
|
323
|
+
const clientVersion = request.params.protocolVersion;
|
|
324
|
+
const clientCapabilities = request.params.capabilities;
|
|
325
|
+
const clientInfo = request.params.clientInfo;
|
|
326
|
+
logger_1.logger.info('MCP Initialize request received', {
|
|
327
|
+
clientVersion,
|
|
328
|
+
clientCapabilities,
|
|
329
|
+
clientInfo
|
|
330
|
+
});
|
|
331
|
+
telemetry_1.telemetry.trackSessionStart();
|
|
332
|
+
this.clientInfo = clientInfo;
|
|
333
|
+
const negotiationResult = (0, protocol_version_1.negotiateProtocolVersion)(clientVersion, clientInfo, undefined, undefined);
|
|
334
|
+
(0, protocol_version_1.logProtocolNegotiation)(negotiationResult, logger_1.logger, 'MCP_INITIALIZE');
|
|
335
|
+
if (clientVersion && clientVersion !== negotiationResult.version) {
|
|
336
|
+
logger_1.logger.warn(`Protocol version negotiated: client requested ${clientVersion}, server will use ${negotiationResult.version}`, {
|
|
337
|
+
reasoning: negotiationResult.reasoning
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
const response = {
|
|
341
|
+
protocolVersion: negotiationResult.version,
|
|
342
|
+
capabilities: {
|
|
343
|
+
tools: {},
|
|
344
|
+
},
|
|
345
|
+
serverInfo: {
|
|
346
|
+
name: 'n8n-documentation-mcp',
|
|
347
|
+
version: version_1.PROJECT_VERSION,
|
|
348
|
+
},
|
|
349
|
+
};
|
|
350
|
+
logger_1.logger.info('MCP Initialize response', { response });
|
|
351
|
+
return response;
|
|
352
|
+
});
|
|
353
|
+
this.server.setRequestHandler(types_js_1.ListToolsRequestSchema, async (request) => {
|
|
354
|
+
const disabledTools = this.getDisabledTools();
|
|
355
|
+
const enabledDocTools = tools_1.n8nDocumentationToolsFinal.filter(tool => !disabledTools.has(tool.name));
|
|
356
|
+
let tools = [...enabledDocTools];
|
|
357
|
+
const hasEnvConfig = (0, n8n_api_1.isN8nApiConfigured)();
|
|
358
|
+
const hasInstanceConfig = !!(this.instanceContext?.n8nApiUrl && this.instanceContext?.n8nApiKey);
|
|
359
|
+
const isMultiTenantEnabled = process.env.ENABLE_MULTI_TENANT === 'true';
|
|
360
|
+
const shouldIncludeManagementTools = hasEnvConfig || hasInstanceConfig || isMultiTenantEnabled;
|
|
361
|
+
if (shouldIncludeManagementTools) {
|
|
362
|
+
const enabledMgmtTools = tools_n8n_manager_1.n8nManagementTools.filter(tool => !disabledTools.has(tool.name));
|
|
363
|
+
tools.push(...enabledMgmtTools);
|
|
364
|
+
logger_1.logger.debug(`Tool listing: ${tools.length} tools available (${enabledDocTools.length} documentation + ${enabledMgmtTools.length} management)`, {
|
|
365
|
+
hasEnvConfig,
|
|
366
|
+
hasInstanceConfig,
|
|
367
|
+
isMultiTenantEnabled,
|
|
368
|
+
disabledToolsCount: disabledTools.size
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
else {
|
|
372
|
+
logger_1.logger.debug(`Tool listing: ${tools.length} tools available (documentation only)`, {
|
|
373
|
+
hasEnvConfig,
|
|
374
|
+
hasInstanceConfig,
|
|
375
|
+
isMultiTenantEnabled,
|
|
376
|
+
disabledToolsCount: disabledTools.size
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
if (disabledTools.size > 0) {
|
|
380
|
+
const totalAvailableTools = tools_1.n8nDocumentationToolsFinal.length + (shouldIncludeManagementTools ? tools_n8n_manager_1.n8nManagementTools.length : 0);
|
|
381
|
+
logger_1.logger.debug(`Filtered ${disabledTools.size} disabled tools, ${tools.length}/${totalAvailableTools} tools available`);
|
|
382
|
+
}
|
|
383
|
+
const clientInfo = this.clientInfo;
|
|
384
|
+
const isN8nClient = clientInfo?.name?.includes('n8n') ||
|
|
385
|
+
clientInfo?.name?.includes('langchain');
|
|
386
|
+
if (isN8nClient) {
|
|
387
|
+
logger_1.logger.info('Detected n8n client, using n8n-friendly tool descriptions');
|
|
388
|
+
tools = (0, tools_n8n_friendly_1.makeToolsN8nFriendly)(tools);
|
|
389
|
+
}
|
|
390
|
+
const validationTools = tools.filter(t => t.name.startsWith('validate_'));
|
|
391
|
+
validationTools.forEach(tool => {
|
|
392
|
+
logger_1.logger.info('Validation tool schema', {
|
|
393
|
+
toolName: tool.name,
|
|
394
|
+
inputSchema: JSON.stringify(tool.inputSchema, null, 2),
|
|
395
|
+
hasOutputSchema: !!tool.outputSchema,
|
|
396
|
+
description: tool.description
|
|
397
|
+
});
|
|
398
|
+
});
|
|
399
|
+
return { tools };
|
|
400
|
+
});
|
|
401
|
+
this.server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
402
|
+
const { name, arguments: args } = request.params;
|
|
403
|
+
logger_1.logger.info('Tool call received - DETAILED DEBUG', {
|
|
404
|
+
toolName: name,
|
|
405
|
+
arguments: JSON.stringify(args, null, 2),
|
|
406
|
+
argumentsType: typeof args,
|
|
407
|
+
argumentsKeys: args ? Object.keys(args) : [],
|
|
408
|
+
hasNodeType: args && 'nodeType' in args,
|
|
409
|
+
hasConfig: args && 'config' in args,
|
|
410
|
+
configType: args && args.config ? typeof args.config : 'N/A',
|
|
411
|
+
rawRequest: JSON.stringify(request.params)
|
|
412
|
+
});
|
|
413
|
+
const disabledTools = this.getDisabledTools();
|
|
414
|
+
if (disabledTools.has(name)) {
|
|
415
|
+
logger_1.logger.warn(`Attempted to call disabled tool: ${name}`);
|
|
416
|
+
return {
|
|
417
|
+
content: [{
|
|
418
|
+
type: 'text',
|
|
419
|
+
text: JSON.stringify({
|
|
420
|
+
error: 'TOOL_DISABLED',
|
|
421
|
+
message: `Tool '${name}' is not available in this deployment. It has been disabled via DISABLED_TOOLS environment variable.`,
|
|
422
|
+
tool: name
|
|
423
|
+
}, null, 2)
|
|
424
|
+
}]
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
let processedArgs = args;
|
|
428
|
+
if (args && typeof args === 'object' && 'output' in args) {
|
|
429
|
+
try {
|
|
430
|
+
const possibleNestedData = args.output;
|
|
431
|
+
if (typeof possibleNestedData === 'string' && possibleNestedData.trim().startsWith('{')) {
|
|
432
|
+
const parsed = JSON.parse(possibleNestedData);
|
|
433
|
+
if (parsed && typeof parsed === 'object') {
|
|
434
|
+
logger_1.logger.warn('Detected n8n nested output bug, attempting to extract actual arguments', {
|
|
435
|
+
originalArgs: args,
|
|
436
|
+
extractedArgs: parsed
|
|
437
|
+
});
|
|
438
|
+
if (this.validateExtractedArgs(name, parsed)) {
|
|
439
|
+
processedArgs = parsed;
|
|
440
|
+
}
|
|
441
|
+
else {
|
|
442
|
+
logger_1.logger.warn('Extracted arguments failed validation, using original args', {
|
|
443
|
+
toolName: name,
|
|
444
|
+
extractedArgs: parsed
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
catch (parseError) {
|
|
451
|
+
logger_1.logger.debug('Failed to parse nested output, continuing with original args', {
|
|
452
|
+
error: parseError instanceof Error ? parseError.message : String(parseError)
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
try {
|
|
457
|
+
logger_1.logger.debug(`Executing tool: ${name}`, { args: processedArgs });
|
|
458
|
+
const startTime = Date.now();
|
|
459
|
+
const result = await this.executeTool(name, processedArgs);
|
|
460
|
+
const duration = Date.now() - startTime;
|
|
461
|
+
logger_1.logger.debug(`Tool ${name} executed successfully`);
|
|
462
|
+
telemetry_1.telemetry.trackToolUsage(name, true, duration);
|
|
463
|
+
if (this.previousTool) {
|
|
464
|
+
const timeDelta = Date.now() - this.previousToolTimestamp;
|
|
465
|
+
telemetry_1.telemetry.trackToolSequence(this.previousTool, name, timeDelta);
|
|
466
|
+
}
|
|
467
|
+
this.previousTool = name;
|
|
468
|
+
this.previousToolTimestamp = Date.now();
|
|
469
|
+
let responseText;
|
|
470
|
+
let structuredContent = null;
|
|
471
|
+
try {
|
|
472
|
+
if (name.startsWith('validate_') && typeof result === 'object' && result !== null) {
|
|
473
|
+
const cleanResult = this.sanitizeValidationResult(result, name);
|
|
474
|
+
structuredContent = cleanResult;
|
|
475
|
+
responseText = JSON.stringify(cleanResult, null, 2);
|
|
476
|
+
}
|
|
477
|
+
else {
|
|
478
|
+
responseText = typeof result === 'string' ? result : JSON.stringify(result, null, 2);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
catch (jsonError) {
|
|
482
|
+
logger_1.logger.warn(`Failed to stringify tool result for ${name}:`, jsonError);
|
|
483
|
+
responseText = String(result);
|
|
484
|
+
}
|
|
485
|
+
if (responseText.length > 1000000) {
|
|
486
|
+
logger_1.logger.warn(`Tool ${name} response is very large (${responseText.length} chars), truncating`);
|
|
487
|
+
responseText = responseText.substring(0, 999000) + '\n\n[Response truncated due to size limits]';
|
|
488
|
+
structuredContent = null;
|
|
489
|
+
}
|
|
490
|
+
const mcpResponse = {
|
|
491
|
+
content: [
|
|
492
|
+
{
|
|
493
|
+
type: 'text',
|
|
494
|
+
text: responseText,
|
|
495
|
+
},
|
|
496
|
+
],
|
|
497
|
+
};
|
|
498
|
+
if (name.startsWith('validate_') && structuredContent !== null) {
|
|
499
|
+
mcpResponse.structuredContent = structuredContent;
|
|
500
|
+
}
|
|
501
|
+
return mcpResponse;
|
|
502
|
+
}
|
|
503
|
+
catch (error) {
|
|
504
|
+
logger_1.logger.error(`Error executing tool ${name}`, error);
|
|
505
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
506
|
+
telemetry_1.telemetry.trackToolUsage(name, false);
|
|
507
|
+
telemetry_1.telemetry.trackError(error instanceof Error ? error.constructor.name : 'UnknownError', `tool_execution`, name, errorMessage);
|
|
508
|
+
if (this.previousTool) {
|
|
509
|
+
const timeDelta = Date.now() - this.previousToolTimestamp;
|
|
510
|
+
telemetry_1.telemetry.trackToolSequence(this.previousTool, name, timeDelta);
|
|
511
|
+
}
|
|
512
|
+
this.previousTool = name;
|
|
513
|
+
this.previousToolTimestamp = Date.now();
|
|
514
|
+
let helpfulMessage = `Error executing tool ${name}: ${errorMessage}`;
|
|
515
|
+
if (errorMessage.includes('required') || errorMessage.includes('missing')) {
|
|
516
|
+
helpfulMessage += '\n\nNote: This error often occurs when the AI agent sends incomplete or incorrectly formatted parameters. Please ensure all required fields are provided with the correct types.';
|
|
517
|
+
}
|
|
518
|
+
else if (errorMessage.includes('type') || errorMessage.includes('expected')) {
|
|
519
|
+
helpfulMessage += '\n\nNote: This error indicates a type mismatch. The AI agent may be sending data in the wrong format (e.g., string instead of object).';
|
|
520
|
+
}
|
|
521
|
+
else if (errorMessage.includes('Unknown category') || errorMessage.includes('not found')) {
|
|
522
|
+
helpfulMessage += '\n\nNote: The requested resource or category was not found. Please check the available options.';
|
|
523
|
+
}
|
|
524
|
+
if (name.startsWith('validate_') && (errorMessage.includes('config') || errorMessage.includes('nodeType'))) {
|
|
525
|
+
helpfulMessage += '\n\nFor validation tools:\n- nodeType should be a string (e.g., "nodes-base.webhook")\n- config should be an object (e.g., {})';
|
|
526
|
+
}
|
|
527
|
+
return {
|
|
528
|
+
content: [
|
|
529
|
+
{
|
|
530
|
+
type: 'text',
|
|
531
|
+
text: helpfulMessage,
|
|
532
|
+
},
|
|
533
|
+
],
|
|
534
|
+
isError: true,
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
sanitizeValidationResult(result, toolName) {
|
|
540
|
+
if (!result || typeof result !== 'object') {
|
|
541
|
+
return result;
|
|
542
|
+
}
|
|
543
|
+
const sanitized = { ...result };
|
|
544
|
+
if (toolName === 'validate_node_minimal') {
|
|
545
|
+
const filtered = {
|
|
546
|
+
nodeType: String(sanitized.nodeType || ''),
|
|
547
|
+
displayName: String(sanitized.displayName || ''),
|
|
548
|
+
valid: Boolean(sanitized.valid),
|
|
549
|
+
missingRequiredFields: Array.isArray(sanitized.missingRequiredFields)
|
|
550
|
+
? sanitized.missingRequiredFields.map(String)
|
|
551
|
+
: []
|
|
552
|
+
};
|
|
553
|
+
return filtered;
|
|
554
|
+
}
|
|
555
|
+
else if (toolName === 'validate_node_operation') {
|
|
556
|
+
let summary = sanitized.summary;
|
|
557
|
+
if (!summary || typeof summary !== 'object') {
|
|
558
|
+
summary = {
|
|
559
|
+
hasErrors: Array.isArray(sanitized.errors) ? sanitized.errors.length > 0 : false,
|
|
560
|
+
errorCount: Array.isArray(sanitized.errors) ? sanitized.errors.length : 0,
|
|
561
|
+
warningCount: Array.isArray(sanitized.warnings) ? sanitized.warnings.length : 0,
|
|
562
|
+
suggestionCount: Array.isArray(sanitized.suggestions) ? sanitized.suggestions.length : 0
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
const filtered = {
|
|
566
|
+
nodeType: String(sanitized.nodeType || ''),
|
|
567
|
+
workflowNodeType: String(sanitized.workflowNodeType || sanitized.nodeType || ''),
|
|
568
|
+
displayName: String(sanitized.displayName || ''),
|
|
569
|
+
valid: Boolean(sanitized.valid),
|
|
570
|
+
errors: Array.isArray(sanitized.errors) ? sanitized.errors : [],
|
|
571
|
+
warnings: Array.isArray(sanitized.warnings) ? sanitized.warnings : [],
|
|
572
|
+
suggestions: Array.isArray(sanitized.suggestions) ? sanitized.suggestions : [],
|
|
573
|
+
summary: summary
|
|
574
|
+
};
|
|
575
|
+
return filtered;
|
|
576
|
+
}
|
|
577
|
+
else if (toolName.startsWith('validate_workflow')) {
|
|
578
|
+
sanitized.valid = Boolean(sanitized.valid);
|
|
579
|
+
sanitized.errors = Array.isArray(sanitized.errors) ? sanitized.errors : [];
|
|
580
|
+
sanitized.warnings = Array.isArray(sanitized.warnings) ? sanitized.warnings : [];
|
|
581
|
+
if (toolName === 'validate_workflow') {
|
|
582
|
+
if (!sanitized.summary || typeof sanitized.summary !== 'object') {
|
|
583
|
+
sanitized.summary = {
|
|
584
|
+
totalNodes: 0,
|
|
585
|
+
enabledNodes: 0,
|
|
586
|
+
triggerNodes: 0,
|
|
587
|
+
validConnections: 0,
|
|
588
|
+
invalidConnections: 0,
|
|
589
|
+
expressionsValidated: 0,
|
|
590
|
+
errorCount: sanitized.errors.length,
|
|
591
|
+
warningCount: sanitized.warnings.length
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
else {
|
|
596
|
+
if (!sanitized.statistics || typeof sanitized.statistics !== 'object') {
|
|
597
|
+
sanitized.statistics = {
|
|
598
|
+
totalNodes: 0,
|
|
599
|
+
triggerNodes: 0,
|
|
600
|
+
validConnections: 0,
|
|
601
|
+
invalidConnections: 0,
|
|
602
|
+
expressionsValidated: 0
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
return JSON.parse(JSON.stringify(sanitized));
|
|
608
|
+
}
|
|
609
|
+
validateToolParams(toolName, args, legacyRequiredParams) {
|
|
610
|
+
try {
|
|
611
|
+
let validationResult;
|
|
612
|
+
switch (toolName) {
|
|
613
|
+
case 'validate_node':
|
|
614
|
+
validationResult = validation_schemas_1.ToolValidation.validateNodeOperation(args);
|
|
615
|
+
break;
|
|
616
|
+
case 'validate_workflow':
|
|
617
|
+
validationResult = validation_schemas_1.ToolValidation.validateWorkflow(args);
|
|
618
|
+
break;
|
|
619
|
+
case 'search_nodes':
|
|
620
|
+
validationResult = validation_schemas_1.ToolValidation.validateSearchNodes(args);
|
|
621
|
+
break;
|
|
622
|
+
case 'n8n_create_workflow':
|
|
623
|
+
validationResult = validation_schemas_1.ToolValidation.validateCreateWorkflow(args);
|
|
624
|
+
break;
|
|
625
|
+
case 'n8n_get_workflow':
|
|
626
|
+
case 'n8n_update_full_workflow':
|
|
627
|
+
case 'n8n_delete_workflow':
|
|
628
|
+
case 'n8n_validate_workflow':
|
|
629
|
+
case 'n8n_autofix_workflow':
|
|
630
|
+
validationResult = validation_schemas_1.ToolValidation.validateWorkflowId(args);
|
|
631
|
+
break;
|
|
632
|
+
case 'n8n_executions':
|
|
633
|
+
validationResult = args.action
|
|
634
|
+
? { valid: true, errors: [] }
|
|
635
|
+
: { valid: false, errors: [{ field: 'action', message: 'action is required' }] };
|
|
636
|
+
break;
|
|
637
|
+
case 'n8n_deploy_template':
|
|
638
|
+
validationResult = args.templateId !== undefined
|
|
639
|
+
? { valid: true, errors: [] }
|
|
640
|
+
: { valid: false, errors: [{ field: 'templateId', message: 'templateId is required' }] };
|
|
641
|
+
break;
|
|
642
|
+
default:
|
|
643
|
+
return this.validateToolParamsBasic(toolName, args, legacyRequiredParams || []);
|
|
644
|
+
}
|
|
645
|
+
if (!validationResult.valid) {
|
|
646
|
+
const errorMessage = validation_schemas_1.Validator.formatErrors(validationResult, toolName);
|
|
647
|
+
logger_1.logger.error(`Parameter validation failed for ${toolName}:`, errorMessage);
|
|
648
|
+
throw new validation_schemas_1.ValidationError(errorMessage);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
catch (error) {
|
|
652
|
+
if (error instanceof validation_schemas_1.ValidationError) {
|
|
653
|
+
throw error;
|
|
654
|
+
}
|
|
655
|
+
logger_1.logger.error(`Validation system error for ${toolName}:`, error);
|
|
656
|
+
const errorMessage = error instanceof Error
|
|
657
|
+
? `Internal validation error: ${error.message}`
|
|
658
|
+
: `Internal validation error while processing ${toolName}`;
|
|
659
|
+
throw new Error(errorMessage);
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
validateToolParamsBasic(toolName, args, requiredParams) {
|
|
663
|
+
const missing = [];
|
|
664
|
+
const invalid = [];
|
|
665
|
+
for (const param of requiredParams) {
|
|
666
|
+
if (!(param in args) || args[param] === undefined || args[param] === null) {
|
|
667
|
+
missing.push(param);
|
|
668
|
+
}
|
|
669
|
+
else if (typeof args[param] === 'string' && args[param].trim() === '') {
|
|
670
|
+
invalid.push(`${param} (empty string)`);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
if (missing.length > 0) {
|
|
674
|
+
throw new Error(`Missing required parameters for ${toolName}: ${missing.join(', ')}. Please provide the required parameters to use this tool.`);
|
|
675
|
+
}
|
|
676
|
+
if (invalid.length > 0) {
|
|
677
|
+
throw new Error(`Invalid parameters for ${toolName}: ${invalid.join(', ')}. String parameters cannot be empty.`);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
validateExtractedArgs(toolName, args) {
|
|
681
|
+
if (!args || typeof args !== 'object') {
|
|
682
|
+
return false;
|
|
683
|
+
}
|
|
684
|
+
const allTools = [...tools_1.n8nDocumentationToolsFinal, ...tools_n8n_manager_1.n8nManagementTools];
|
|
685
|
+
const tool = allTools.find(t => t.name === toolName);
|
|
686
|
+
if (!tool || !tool.inputSchema) {
|
|
687
|
+
return true;
|
|
688
|
+
}
|
|
689
|
+
const schema = tool.inputSchema;
|
|
690
|
+
const required = schema.required || [];
|
|
691
|
+
const properties = schema.properties || {};
|
|
692
|
+
for (const requiredField of required) {
|
|
693
|
+
if (!(requiredField in args)) {
|
|
694
|
+
logger_1.logger.debug(`Extracted args missing required field: ${requiredField}`, {
|
|
695
|
+
toolName,
|
|
696
|
+
extractedArgs: args,
|
|
697
|
+
required
|
|
698
|
+
});
|
|
699
|
+
return false;
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
for (const [fieldName, fieldValue] of Object.entries(args)) {
|
|
703
|
+
if (properties[fieldName]) {
|
|
704
|
+
const expectedType = properties[fieldName].type;
|
|
705
|
+
const actualType = Array.isArray(fieldValue) ? 'array' : typeof fieldValue;
|
|
706
|
+
if (expectedType && expectedType !== actualType) {
|
|
707
|
+
if (expectedType === 'number' && actualType === 'string' && !isNaN(Number(fieldValue))) {
|
|
708
|
+
continue;
|
|
709
|
+
}
|
|
710
|
+
logger_1.logger.debug(`Extracted args field type mismatch: ${fieldName}`, {
|
|
711
|
+
toolName,
|
|
712
|
+
expectedType,
|
|
713
|
+
actualType,
|
|
714
|
+
fieldValue
|
|
715
|
+
});
|
|
716
|
+
return false;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
if (schema.additionalProperties === false) {
|
|
721
|
+
const allowedFields = Object.keys(properties);
|
|
722
|
+
const extraFields = Object.keys(args).filter(field => !allowedFields.includes(field));
|
|
723
|
+
if (extraFields.length > 0) {
|
|
724
|
+
logger_1.logger.debug(`Extracted args have extra fields`, {
|
|
725
|
+
toolName,
|
|
726
|
+
extraFields,
|
|
727
|
+
allowedFields
|
|
728
|
+
});
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
return true;
|
|
732
|
+
}
|
|
733
|
+
async executeTool(name, args) {
|
|
734
|
+
args = args || {};
|
|
735
|
+
const disabledTools = this.getDisabledTools();
|
|
736
|
+
if (disabledTools.has(name)) {
|
|
737
|
+
throw new Error(`Tool '${name}' is disabled via DISABLED_TOOLS environment variable`);
|
|
738
|
+
}
|
|
739
|
+
logger_1.logger.info(`Tool execution: ${name}`, {
|
|
740
|
+
args: typeof args === 'object' ? JSON.stringify(args) : args,
|
|
741
|
+
argsType: typeof args,
|
|
742
|
+
argsKeys: typeof args === 'object' ? Object.keys(args) : 'not-object'
|
|
743
|
+
});
|
|
744
|
+
if (typeof args !== 'object' || args === null) {
|
|
745
|
+
throw new Error(`Invalid arguments for tool ${name}: expected object, got ${typeof args}`);
|
|
746
|
+
}
|
|
747
|
+
switch (name) {
|
|
748
|
+
case 'tools_documentation':
|
|
749
|
+
return this.getToolsDocumentation(args.topic, args.depth);
|
|
750
|
+
case 'search_nodes':
|
|
751
|
+
this.validateToolParams(name, args, ['query']);
|
|
752
|
+
const limit = args.limit !== undefined ? Number(args.limit) || 20 : 20;
|
|
753
|
+
return this.searchNodes(args.query, limit, {
|
|
754
|
+
mode: args.mode,
|
|
755
|
+
includeExamples: args.includeExamples,
|
|
756
|
+
source: args.source
|
|
757
|
+
});
|
|
758
|
+
case 'get_node':
|
|
759
|
+
this.validateToolParams(name, args, ['nodeType']);
|
|
760
|
+
if (args.mode === 'docs') {
|
|
761
|
+
return this.getNodeDocumentation(args.nodeType);
|
|
762
|
+
}
|
|
763
|
+
if (args.mode === 'search_properties') {
|
|
764
|
+
if (!args.propertyQuery) {
|
|
765
|
+
throw new Error('propertyQuery is required for mode=search_properties');
|
|
766
|
+
}
|
|
767
|
+
const maxResults = args.maxPropertyResults !== undefined ? Number(args.maxPropertyResults) || 20 : 20;
|
|
768
|
+
return this.searchNodeProperties(args.nodeType, args.propertyQuery, maxResults);
|
|
769
|
+
}
|
|
770
|
+
return this.getNode(args.nodeType, args.detail, args.mode, args.includeTypeInfo, args.includeExamples, args.fromVersion, args.toVersion);
|
|
771
|
+
case 'validate_node':
|
|
772
|
+
this.validateToolParams(name, args, ['nodeType', 'config']);
|
|
773
|
+
if (typeof args.config !== 'object' || args.config === null) {
|
|
774
|
+
logger_1.logger.warn(`validate_node called with invalid config type: ${typeof args.config}`);
|
|
775
|
+
const validationMode = args.mode || 'full';
|
|
776
|
+
if (validationMode === 'minimal') {
|
|
777
|
+
return {
|
|
778
|
+
nodeType: args.nodeType || 'unknown',
|
|
779
|
+
displayName: 'Unknown Node',
|
|
780
|
+
valid: false,
|
|
781
|
+
missingRequiredFields: [
|
|
782
|
+
'Invalid config format - expected object',
|
|
783
|
+
'🔧 RECOVERY: Use format { "resource": "...", "operation": "..." } or {} for empty config'
|
|
784
|
+
]
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
return {
|
|
788
|
+
nodeType: args.nodeType || 'unknown',
|
|
789
|
+
workflowNodeType: args.nodeType || 'unknown',
|
|
790
|
+
displayName: 'Unknown Node',
|
|
791
|
+
valid: false,
|
|
792
|
+
errors: [{
|
|
793
|
+
type: 'config',
|
|
794
|
+
property: 'config',
|
|
795
|
+
message: 'Invalid config format - expected object',
|
|
796
|
+
fix: 'Provide config as an object with node properties'
|
|
797
|
+
}],
|
|
798
|
+
warnings: [],
|
|
799
|
+
suggestions: [
|
|
800
|
+
'🔧 RECOVERY: Invalid config detected. Fix with:',
|
|
801
|
+
' • Ensure config is an object: { "resource": "...", "operation": "..." }',
|
|
802
|
+
' • Use get_node to see required fields for this node type',
|
|
803
|
+
' • Check if the node type is correct before configuring it'
|
|
804
|
+
],
|
|
805
|
+
summary: {
|
|
806
|
+
hasErrors: true,
|
|
807
|
+
errorCount: 1,
|
|
808
|
+
warningCount: 0,
|
|
809
|
+
suggestionCount: 3
|
|
810
|
+
}
|
|
811
|
+
};
|
|
812
|
+
}
|
|
813
|
+
const validationMode = args.mode || 'full';
|
|
814
|
+
if (validationMode === 'minimal') {
|
|
815
|
+
return this.validateNodeMinimal(args.nodeType, args.config);
|
|
816
|
+
}
|
|
817
|
+
return this.validateNodeConfig(args.nodeType, args.config, 'operation', args.profile);
|
|
818
|
+
case 'get_template':
|
|
819
|
+
this.validateToolParams(name, args, ['templateId']);
|
|
820
|
+
const templateId = Number(args.templateId);
|
|
821
|
+
const templateMode = args.mode || 'full';
|
|
822
|
+
return this.getTemplate(templateId, templateMode);
|
|
823
|
+
case 'search_templates': {
|
|
824
|
+
const searchMode = args.searchMode || 'keyword';
|
|
825
|
+
const searchLimit = Math.min(Math.max(Number(args.limit) || 20, 1), 100);
|
|
826
|
+
const searchOffset = Math.max(Number(args.offset) || 0, 0);
|
|
827
|
+
switch (searchMode) {
|
|
828
|
+
case 'by_nodes':
|
|
829
|
+
if (!args.nodeTypes || !Array.isArray(args.nodeTypes) || args.nodeTypes.length === 0) {
|
|
830
|
+
throw new Error('nodeTypes array is required for searchMode=by_nodes');
|
|
831
|
+
}
|
|
832
|
+
return this.listNodeTemplates(args.nodeTypes, searchLimit, searchOffset);
|
|
833
|
+
case 'by_task':
|
|
834
|
+
if (!args.task) {
|
|
835
|
+
throw new Error('task is required for searchMode=by_task');
|
|
836
|
+
}
|
|
837
|
+
return this.getTemplatesForTask(args.task, searchLimit, searchOffset);
|
|
838
|
+
case 'by_metadata':
|
|
839
|
+
return this.searchTemplatesByMetadata({
|
|
840
|
+
category: args.category,
|
|
841
|
+
complexity: args.complexity,
|
|
842
|
+
maxSetupMinutes: args.maxSetupMinutes ? Number(args.maxSetupMinutes) : undefined,
|
|
843
|
+
minSetupMinutes: args.minSetupMinutes ? Number(args.minSetupMinutes) : undefined,
|
|
844
|
+
requiredService: args.requiredService,
|
|
845
|
+
targetAudience: args.targetAudience
|
|
846
|
+
}, searchLimit, searchOffset);
|
|
847
|
+
case 'keyword':
|
|
848
|
+
default:
|
|
849
|
+
if (!args.query) {
|
|
850
|
+
throw new Error('query is required for searchMode=keyword');
|
|
851
|
+
}
|
|
852
|
+
const searchFields = args.fields;
|
|
853
|
+
return this.searchTemplates(args.query, searchLimit, searchOffset, searchFields);
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
case 'validate_workflow':
|
|
857
|
+
this.validateToolParams(name, args, ['workflow']);
|
|
858
|
+
return this.validateWorkflow(args.workflow, args.options);
|
|
859
|
+
case 'n8n_create_workflow':
|
|
860
|
+
this.validateToolParams(name, args, ['name', 'nodes', 'connections']);
|
|
861
|
+
return n8nHandlers.handleCreateWorkflow(args, this.instanceContext);
|
|
862
|
+
case 'n8n_get_workflow': {
|
|
863
|
+
this.validateToolParams(name, args, ['id']);
|
|
864
|
+
const workflowMode = args.mode || 'full';
|
|
865
|
+
switch (workflowMode) {
|
|
866
|
+
case 'details':
|
|
867
|
+
return n8nHandlers.handleGetWorkflowDetails(args, this.instanceContext);
|
|
868
|
+
case 'structure':
|
|
869
|
+
return n8nHandlers.handleGetWorkflowStructure(args, this.instanceContext);
|
|
870
|
+
case 'minimal':
|
|
871
|
+
return n8nHandlers.handleGetWorkflowMinimal(args, this.instanceContext);
|
|
872
|
+
case 'full':
|
|
873
|
+
default:
|
|
874
|
+
return n8nHandlers.handleGetWorkflow(args, this.instanceContext);
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
case 'n8n_update_full_workflow':
|
|
878
|
+
this.validateToolParams(name, args, ['id']);
|
|
879
|
+
return n8nHandlers.handleUpdateWorkflow(args, this.repository, this.instanceContext);
|
|
880
|
+
case 'n8n_update_partial_workflow':
|
|
881
|
+
this.validateToolParams(name, args, ['id', 'operations']);
|
|
882
|
+
return (0, handlers_workflow_diff_1.handleUpdatePartialWorkflow)(args, this.repository, this.instanceContext);
|
|
883
|
+
case 'n8n_delete_workflow':
|
|
884
|
+
this.validateToolParams(name, args, ['id']);
|
|
885
|
+
return n8nHandlers.handleDeleteWorkflow(args, this.instanceContext);
|
|
886
|
+
case 'n8n_list_workflows':
|
|
887
|
+
return n8nHandlers.handleListWorkflows(args, this.instanceContext);
|
|
888
|
+
case 'n8n_validate_workflow':
|
|
889
|
+
this.validateToolParams(name, args, ['id']);
|
|
890
|
+
await this.ensureInitialized();
|
|
891
|
+
if (!this.repository)
|
|
892
|
+
throw new Error('Repository not initialized');
|
|
893
|
+
return n8nHandlers.handleValidateWorkflow(args, this.repository, this.instanceContext);
|
|
894
|
+
case 'n8n_autofix_workflow':
|
|
895
|
+
this.validateToolParams(name, args, ['id']);
|
|
896
|
+
await this.ensureInitialized();
|
|
897
|
+
if (!this.repository)
|
|
898
|
+
throw new Error('Repository not initialized');
|
|
899
|
+
return n8nHandlers.handleAutofixWorkflow(args, this.repository, this.instanceContext);
|
|
900
|
+
case 'n8n_test_workflow':
|
|
901
|
+
this.validateToolParams(name, args, ['workflowId']);
|
|
902
|
+
return n8nHandlers.handleTestWorkflow(args, this.instanceContext);
|
|
903
|
+
case 'n8n_executions': {
|
|
904
|
+
this.validateToolParams(name, args, ['action']);
|
|
905
|
+
const execAction = args.action;
|
|
906
|
+
switch (execAction) {
|
|
907
|
+
case 'get':
|
|
908
|
+
if (!args.id) {
|
|
909
|
+
throw new Error('id is required for action=get');
|
|
910
|
+
}
|
|
911
|
+
return n8nHandlers.handleGetExecution(args, this.instanceContext);
|
|
912
|
+
case 'list':
|
|
913
|
+
return n8nHandlers.handleListExecutions(args, this.instanceContext);
|
|
914
|
+
case 'delete':
|
|
915
|
+
if (!args.id) {
|
|
916
|
+
throw new Error('id is required for action=delete');
|
|
917
|
+
}
|
|
918
|
+
return n8nHandlers.handleDeleteExecution(args, this.instanceContext);
|
|
919
|
+
default:
|
|
920
|
+
throw new Error(`Unknown action: ${execAction}. Valid actions: get, list, delete`);
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
case 'n8n_health_check':
|
|
924
|
+
if (args.mode === 'diagnostic') {
|
|
925
|
+
return n8nHandlers.handleDiagnostic({ params: { arguments: args } }, this.instanceContext);
|
|
926
|
+
}
|
|
927
|
+
return n8nHandlers.handleHealthCheck(this.instanceContext);
|
|
928
|
+
case 'n8n_workflow_versions':
|
|
929
|
+
this.validateToolParams(name, args, ['mode']);
|
|
930
|
+
return n8nHandlers.handleWorkflowVersions(args, this.repository, this.instanceContext);
|
|
931
|
+
case 'n8n_import_workflow_file':
|
|
932
|
+
this.validateToolParams(name, args, ['fileContent']);
|
|
933
|
+
return n8nHandlers.handleImportWorkflowFile(args, n8nHandlers.getN8nApiClient(this.instanceContext));
|
|
934
|
+
case 'n8n_export_workflow_file':
|
|
935
|
+
this.validateToolParams(name, args, ['workflowId']);
|
|
936
|
+
return n8nHandlers.handleExportWorkflowFile(args, n8nHandlers.getN8nApiClient(this.instanceContext));
|
|
937
|
+
case 'n8n_sync_workflow':
|
|
938
|
+
this.validateToolParams(name, args, ['workflowId', 'fileContent', 'direction']);
|
|
939
|
+
return n8nHandlers.handleSyncWorkflow(args, n8nHandlers.getN8nApiClient(this.instanceContext));
|
|
940
|
+
case 'n8n_validate_workflow_file':
|
|
941
|
+
this.validateToolParams(name, args, ['fileContent']);
|
|
942
|
+
return n8nHandlers.handleValidateWorkflowFile(args);
|
|
943
|
+
case 'n8n_deploy_template':
|
|
944
|
+
this.validateToolParams(name, args, ['templateId']);
|
|
945
|
+
await this.ensureInitialized();
|
|
946
|
+
if (!this.templateService)
|
|
947
|
+
throw new Error('Template service not initialized');
|
|
948
|
+
if (!this.repository)
|
|
949
|
+
throw new Error('Repository not initialized');
|
|
950
|
+
return n8nHandlers.handleDeployTemplate(args, this.templateService, this.repository, this.instanceContext);
|
|
951
|
+
case 'n8n_search_community_nodes':
|
|
952
|
+
this.validateToolParams(name, args, ['query']);
|
|
953
|
+
await this.ensureInitialized();
|
|
954
|
+
return n8nHandlers.handleSearchCommunityNodes(args, this.repository, this.instanceContext);
|
|
955
|
+
case 'n8n_list_community_packages':
|
|
956
|
+
return n8nHandlers.handleListCommunityPackages(args, this.instanceContext);
|
|
957
|
+
case 'n8n_install_community_package':
|
|
958
|
+
this.validateToolParams(name, args, ['packageName']);
|
|
959
|
+
return n8nHandlers.handleInstallCommunityPackage(args, this.instanceContext);
|
|
960
|
+
case 'n8n_uninstall_community_package':
|
|
961
|
+
this.validateToolParams(name, args, ['packageName']);
|
|
962
|
+
return n8nHandlers.handleUninstallCommunityPackage(args, this.instanceContext);
|
|
963
|
+
case 'n8n_update_community_package':
|
|
964
|
+
this.validateToolParams(name, args, ['packageName']);
|
|
965
|
+
return n8nHandlers.handleUpdateCommunityPackage(args, this.instanceContext);
|
|
966
|
+
case 'n8n_sync_installed_nodes':
|
|
967
|
+
await this.ensureInitialized();
|
|
968
|
+
if (!this.repository)
|
|
969
|
+
throw new Error('Repository not initialized');
|
|
970
|
+
return n8nHandlers.handleSyncInstalledNodes(args, this.repository, this.instanceContext);
|
|
971
|
+
case 'n8n_list_credentials':
|
|
972
|
+
return n8nHandlers.handleListCredentials(args, this.instanceContext);
|
|
973
|
+
case 'n8n_get_credential':
|
|
974
|
+
this.validateToolParams(name, args, ['id']);
|
|
975
|
+
return n8nHandlers.handleGetCredential(args, this.instanceContext);
|
|
976
|
+
case 'n8n_create_credential':
|
|
977
|
+
this.validateToolParams(name, args, ['name', 'type', 'data']);
|
|
978
|
+
return n8nHandlers.handleCreateCredential(args, this.instanceContext);
|
|
979
|
+
case 'n8n_delete_credential':
|
|
980
|
+
this.validateToolParams(name, args, ['id']);
|
|
981
|
+
return n8nHandlers.handleDeleteCredential(args, this.instanceContext);
|
|
982
|
+
case 'n8n_get_credential_types':
|
|
983
|
+
await this.ensureInitialized();
|
|
984
|
+
if (!this.repository)
|
|
985
|
+
throw new Error('Repository not initialized');
|
|
986
|
+
return n8nHandlers.handleGetCredentialTypes(args, this.repository, this.instanceContext);
|
|
987
|
+
case 'n8n_list_tags':
|
|
988
|
+
return n8nHandlers.handleListTags(args, this.instanceContext);
|
|
989
|
+
case 'n8n_create_tag':
|
|
990
|
+
this.validateToolParams(name, args, ['name']);
|
|
991
|
+
return n8nHandlers.handleCreateTag(args, this.instanceContext);
|
|
992
|
+
case 'n8n_update_tag':
|
|
993
|
+
this.validateToolParams(name, args, ['id', 'name']);
|
|
994
|
+
return n8nHandlers.handleUpdateTag(args, this.instanceContext);
|
|
995
|
+
case 'n8n_delete_tag':
|
|
996
|
+
this.validateToolParams(name, args, ['id']);
|
|
997
|
+
return n8nHandlers.handleDeleteTag(args, this.instanceContext);
|
|
998
|
+
case 'n8n_assign_tags_to_workflow':
|
|
999
|
+
this.validateToolParams(name, args, ['workflowId', 'tags']);
|
|
1000
|
+
return n8nHandlers.handleAssignTagsToWorkflow(args, this.instanceContext);
|
|
1001
|
+
case 'n8n_clone_workflow':
|
|
1002
|
+
this.validateToolParams(name, args, ['workflowId', 'newName']);
|
|
1003
|
+
return n8nHandlers.handleCloneWorkflow(args, this.instanceContext);
|
|
1004
|
+
case 'n8n_backup_workflows':
|
|
1005
|
+
return n8nHandlers.handleBackupWorkflows(args, this.instanceContext);
|
|
1006
|
+
case 'n8n_restore_workflow':
|
|
1007
|
+
this.validateToolParams(name, args, ['workflowData']);
|
|
1008
|
+
return n8nHandlers.handleRestoreWorkflow(args, this.instanceContext);
|
|
1009
|
+
case 'n8n_activate_workflow':
|
|
1010
|
+
this.validateToolParams(name, args, ['workflowId']);
|
|
1011
|
+
return n8nHandlers.handleActivateWorkflow(args, this.instanceContext);
|
|
1012
|
+
case 'n8n_deactivate_workflow':
|
|
1013
|
+
this.validateToolParams(name, args, ['workflowId']);
|
|
1014
|
+
return n8nHandlers.handleDeactivateWorkflow(args, this.instanceContext);
|
|
1015
|
+
case 'n8n_list_active_workflows':
|
|
1016
|
+
return n8nHandlers.handleListActiveWorkflows(args, this.instanceContext);
|
|
1017
|
+
case 'n8n_get_workflow_stats':
|
|
1018
|
+
this.validateToolParams(name, args, ['workflowId']);
|
|
1019
|
+
return n8nHandlers.handleGetWorkflowStats(args, this.instanceContext);
|
|
1020
|
+
case 'n8n_get_execution_trends':
|
|
1021
|
+
this.validateToolParams(name, args, ['workflowId']);
|
|
1022
|
+
return n8nHandlers.handleGetExecutionTrends(args, this.instanceContext);
|
|
1023
|
+
case 'n8n_get_slow_workflows':
|
|
1024
|
+
return n8nHandlers.handleGetSlowWorkflows(args, this.instanceContext);
|
|
1025
|
+
case 'n8n_trace_execution':
|
|
1026
|
+
this.validateToolParams(name, args, ['executionId']);
|
|
1027
|
+
return n8nHandlers.handleTraceExecution(args, this.instanceContext);
|
|
1028
|
+
case 'n8n_compare_executions':
|
|
1029
|
+
this.validateToolParams(name, args, ['executionId1', 'executionId2']);
|
|
1030
|
+
return n8nHandlers.handleCompareExecutions(args, this.instanceContext);
|
|
1031
|
+
default:
|
|
1032
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
async listNodes(filters = {}) {
|
|
1036
|
+
await this.ensureInitialized();
|
|
1037
|
+
let query = 'SELECT * FROM nodes WHERE 1=1';
|
|
1038
|
+
const params = [];
|
|
1039
|
+
if (filters.package) {
|
|
1040
|
+
const packageVariants = [
|
|
1041
|
+
filters.package,
|
|
1042
|
+
`@n8n/${filters.package}`,
|
|
1043
|
+
filters.package.replace('@n8n/', '')
|
|
1044
|
+
];
|
|
1045
|
+
query += ' AND package_name IN (' + packageVariants.map(() => '?').join(',') + ')';
|
|
1046
|
+
params.push(...packageVariants);
|
|
1047
|
+
}
|
|
1048
|
+
if (filters.category) {
|
|
1049
|
+
query += ' AND category = ?';
|
|
1050
|
+
params.push(filters.category);
|
|
1051
|
+
}
|
|
1052
|
+
if (filters.developmentStyle) {
|
|
1053
|
+
query += ' AND development_style = ?';
|
|
1054
|
+
params.push(filters.developmentStyle);
|
|
1055
|
+
}
|
|
1056
|
+
if (filters.isAITool !== undefined) {
|
|
1057
|
+
query += ' AND is_ai_tool = ?';
|
|
1058
|
+
params.push(filters.isAITool ? 1 : 0);
|
|
1059
|
+
}
|
|
1060
|
+
query += ' ORDER BY display_name';
|
|
1061
|
+
if (filters.limit) {
|
|
1062
|
+
query += ' LIMIT ?';
|
|
1063
|
+
params.push(filters.limit);
|
|
1064
|
+
}
|
|
1065
|
+
const nodes = this.db.prepare(query).all(...params);
|
|
1066
|
+
return {
|
|
1067
|
+
nodes: nodes.map(node => ({
|
|
1068
|
+
nodeType: node.node_type,
|
|
1069
|
+
displayName: node.display_name,
|
|
1070
|
+
description: node.description,
|
|
1071
|
+
category: node.category,
|
|
1072
|
+
package: node.package_name,
|
|
1073
|
+
developmentStyle: node.development_style,
|
|
1074
|
+
isAITool: Number(node.is_ai_tool) === 1,
|
|
1075
|
+
isTrigger: Number(node.is_trigger) === 1,
|
|
1076
|
+
isVersioned: Number(node.is_versioned) === 1,
|
|
1077
|
+
})),
|
|
1078
|
+
totalCount: nodes.length,
|
|
1079
|
+
};
|
|
1080
|
+
}
|
|
1081
|
+
async getNodeInfo(nodeType) {
|
|
1082
|
+
await this.ensureInitialized();
|
|
1083
|
+
if (!this.repository)
|
|
1084
|
+
throw new Error('Repository not initialized');
|
|
1085
|
+
const normalizedType = node_type_normalizer_1.NodeTypeNormalizer.normalizeToFullForm(nodeType);
|
|
1086
|
+
let node = this.repository.getNode(normalizedType);
|
|
1087
|
+
if (!node && normalizedType !== nodeType) {
|
|
1088
|
+
node = this.repository.getNode(nodeType);
|
|
1089
|
+
}
|
|
1090
|
+
if (!node) {
|
|
1091
|
+
const alternatives = (0, node_utils_1.getNodeTypeAlternatives)(normalizedType);
|
|
1092
|
+
for (const alt of alternatives) {
|
|
1093
|
+
const found = this.repository.getNode(alt);
|
|
1094
|
+
if (found) {
|
|
1095
|
+
node = found;
|
|
1096
|
+
break;
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
if (!node) {
|
|
1101
|
+
throw new Error(`Node ${nodeType} not found`);
|
|
1102
|
+
}
|
|
1103
|
+
const aiToolCapabilities = {
|
|
1104
|
+
canBeUsedAsTool: true,
|
|
1105
|
+
hasUsableAsToolProperty: node.isAITool ?? false,
|
|
1106
|
+
requiresEnvironmentVariable: !(node.isAITool ?? false) && node.package !== 'n8n-nodes-base',
|
|
1107
|
+
toolConnectionType: 'ai_tool',
|
|
1108
|
+
commonToolUseCases: this.getCommonAIToolUseCases(node.nodeType),
|
|
1109
|
+
environmentRequirement: node.package && node.package !== 'n8n-nodes-base' ?
|
|
1110
|
+
'N8N_COMMUNITY_PACKAGES_ALLOW_TOOL_USAGE=true' :
|
|
1111
|
+
null
|
|
1112
|
+
};
|
|
1113
|
+
let outputs = undefined;
|
|
1114
|
+
if (node.outputNames && Array.isArray(node.outputNames) && node.outputNames.length > 0) {
|
|
1115
|
+
outputs = node.outputNames.map((name, index) => {
|
|
1116
|
+
const descriptions = this.getOutputDescriptions(node.nodeType, name, index);
|
|
1117
|
+
return {
|
|
1118
|
+
index,
|
|
1119
|
+
name,
|
|
1120
|
+
description: descriptions?.description ?? '',
|
|
1121
|
+
connectionGuidance: descriptions?.connectionGuidance ?? ''
|
|
1122
|
+
};
|
|
1123
|
+
});
|
|
1124
|
+
}
|
|
1125
|
+
const result = {
|
|
1126
|
+
...node,
|
|
1127
|
+
workflowNodeType: (0, node_utils_1.getWorkflowNodeType)(node.package ?? 'n8n-nodes-base', node.nodeType),
|
|
1128
|
+
aiToolCapabilities,
|
|
1129
|
+
outputs
|
|
1130
|
+
};
|
|
1131
|
+
const toolVariantInfo = this.buildToolVariantGuidance(node);
|
|
1132
|
+
if (toolVariantInfo) {
|
|
1133
|
+
result.toolVariantInfo = toolVariantInfo;
|
|
1134
|
+
}
|
|
1135
|
+
return result;
|
|
1136
|
+
}
|
|
1137
|
+
async searchNodes(query, limit = 20, options) {
|
|
1138
|
+
await this.ensureInitialized();
|
|
1139
|
+
if (!this.db)
|
|
1140
|
+
throw new Error('Database not initialized');
|
|
1141
|
+
let normalizedQuery = query;
|
|
1142
|
+
if (query.includes('n8n-nodes-base.') || query.includes('@n8n/n8n-nodes-langchain.')) {
|
|
1143
|
+
normalizedQuery = query
|
|
1144
|
+
.replace(/n8n-nodes-base\./g, 'nodes-base.')
|
|
1145
|
+
.replace(/@n8n\/n8n-nodes-langchain\./g, 'nodes-langchain.');
|
|
1146
|
+
}
|
|
1147
|
+
const searchMode = options?.mode || 'OR';
|
|
1148
|
+
const ftsExists = this.db.prepare(`
|
|
1149
|
+
SELECT name FROM sqlite_master
|
|
1150
|
+
WHERE type='table' AND name='nodes_fts'
|
|
1151
|
+
`).get();
|
|
1152
|
+
if (ftsExists) {
|
|
1153
|
+
logger_1.logger.debug(`Using FTS5 search with includeExamples=${options?.includeExamples}`);
|
|
1154
|
+
return this.searchNodesFTS(normalizedQuery, limit, searchMode, options);
|
|
1155
|
+
}
|
|
1156
|
+
else {
|
|
1157
|
+
logger_1.logger.debug('Using LIKE search (no FTS5)');
|
|
1158
|
+
return this.searchNodesLIKE(normalizedQuery, limit, options);
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
async searchNodesFTS(query, limit, mode, options) {
|
|
1162
|
+
if (!this.db)
|
|
1163
|
+
throw new Error('Database not initialized');
|
|
1164
|
+
const cleanedQuery = query.trim();
|
|
1165
|
+
if (!cleanedQuery) {
|
|
1166
|
+
return { query, results: [], totalCount: 0 };
|
|
1167
|
+
}
|
|
1168
|
+
if (mode === 'FUZZY') {
|
|
1169
|
+
return this.searchNodesFuzzy(cleanedQuery, limit);
|
|
1170
|
+
}
|
|
1171
|
+
let ftsQuery;
|
|
1172
|
+
if (cleanedQuery.startsWith('"') && cleanedQuery.endsWith('"')) {
|
|
1173
|
+
ftsQuery = cleanedQuery;
|
|
1174
|
+
}
|
|
1175
|
+
else {
|
|
1176
|
+
const words = cleanedQuery.split(/\s+/).filter(w => w.length > 0);
|
|
1177
|
+
switch (mode) {
|
|
1178
|
+
case 'AND':
|
|
1179
|
+
ftsQuery = words.join(' AND ');
|
|
1180
|
+
break;
|
|
1181
|
+
case 'OR':
|
|
1182
|
+
default:
|
|
1183
|
+
ftsQuery = words.join(' OR ');
|
|
1184
|
+
break;
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
try {
|
|
1188
|
+
let sourceFilter = '';
|
|
1189
|
+
const sourceValue = options?.source || 'all';
|
|
1190
|
+
switch (sourceValue) {
|
|
1191
|
+
case 'core':
|
|
1192
|
+
sourceFilter = 'AND n.is_community = 0';
|
|
1193
|
+
break;
|
|
1194
|
+
case 'community':
|
|
1195
|
+
sourceFilter = 'AND n.is_community = 1';
|
|
1196
|
+
break;
|
|
1197
|
+
case 'verified':
|
|
1198
|
+
sourceFilter = 'AND n.is_community = 1 AND n.is_verified = 1';
|
|
1199
|
+
break;
|
|
1200
|
+
}
|
|
1201
|
+
const nodes = this.db.prepare(`
|
|
1202
|
+
SELECT
|
|
1203
|
+
n.*,
|
|
1204
|
+
rank
|
|
1205
|
+
FROM nodes n
|
|
1206
|
+
JOIN nodes_fts ON n.rowid = nodes_fts.rowid
|
|
1207
|
+
WHERE nodes_fts MATCH ?
|
|
1208
|
+
${sourceFilter}
|
|
1209
|
+
ORDER BY
|
|
1210
|
+
CASE
|
|
1211
|
+
WHEN LOWER(n.display_name) = LOWER(?) THEN 0
|
|
1212
|
+
WHEN LOWER(n.display_name) LIKE LOWER(?) THEN 1
|
|
1213
|
+
WHEN LOWER(n.node_type) LIKE LOWER(?) THEN 2
|
|
1214
|
+
ELSE 3
|
|
1215
|
+
END,
|
|
1216
|
+
rank,
|
|
1217
|
+
n.display_name
|
|
1218
|
+
LIMIT ?
|
|
1219
|
+
`).all(ftsQuery, cleanedQuery, `%${cleanedQuery}%`, `%${cleanedQuery}%`, limit);
|
|
1220
|
+
const scoredNodes = nodes.map(node => {
|
|
1221
|
+
const relevanceScore = this.calculateRelevanceScore(node, cleanedQuery);
|
|
1222
|
+
return { ...node, relevanceScore };
|
|
1223
|
+
});
|
|
1224
|
+
scoredNodes.sort((a, b) => {
|
|
1225
|
+
if (a.display_name.toLowerCase() === cleanedQuery.toLowerCase())
|
|
1226
|
+
return -1;
|
|
1227
|
+
if (b.display_name.toLowerCase() === cleanedQuery.toLowerCase())
|
|
1228
|
+
return 1;
|
|
1229
|
+
if (a.relevanceScore !== b.relevanceScore) {
|
|
1230
|
+
return b.relevanceScore - a.relevanceScore;
|
|
1231
|
+
}
|
|
1232
|
+
return a.rank - b.rank;
|
|
1233
|
+
});
|
|
1234
|
+
const hasHttpRequest = scoredNodes.some(n => n.node_type === 'nodes-base.httpRequest');
|
|
1235
|
+
if (cleanedQuery.toLowerCase().includes('http') && !hasHttpRequest) {
|
|
1236
|
+
logger_1.logger.debug('FTS missed HTTP Request node, augmenting with LIKE search');
|
|
1237
|
+
return this.searchNodesLIKE(query, limit);
|
|
1238
|
+
}
|
|
1239
|
+
const result = {
|
|
1240
|
+
query,
|
|
1241
|
+
results: scoredNodes.map(node => {
|
|
1242
|
+
const nodeResult = {
|
|
1243
|
+
nodeType: node.node_type,
|
|
1244
|
+
workflowNodeType: (0, node_utils_1.getWorkflowNodeType)(node.package_name, node.node_type),
|
|
1245
|
+
displayName: node.display_name,
|
|
1246
|
+
description: node.description,
|
|
1247
|
+
category: node.category,
|
|
1248
|
+
package: node.package_name,
|
|
1249
|
+
relevance: this.calculateRelevance(node, cleanedQuery)
|
|
1250
|
+
};
|
|
1251
|
+
if (node.is_community === 1) {
|
|
1252
|
+
nodeResult.isCommunity = true;
|
|
1253
|
+
nodeResult.isVerified = node.is_verified === 1;
|
|
1254
|
+
if (node.author_name) {
|
|
1255
|
+
nodeResult.authorName = node.author_name;
|
|
1256
|
+
}
|
|
1257
|
+
if (node.npm_downloads) {
|
|
1258
|
+
nodeResult.npmDownloads = node.npm_downloads;
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
return nodeResult;
|
|
1262
|
+
}),
|
|
1263
|
+
totalCount: scoredNodes.length
|
|
1264
|
+
};
|
|
1265
|
+
if (mode !== 'OR') {
|
|
1266
|
+
result.mode = mode;
|
|
1267
|
+
}
|
|
1268
|
+
if (options && options.includeExamples) {
|
|
1269
|
+
try {
|
|
1270
|
+
for (const nodeResult of result.results) {
|
|
1271
|
+
const examples = this.db.prepare(`
|
|
1272
|
+
SELECT
|
|
1273
|
+
parameters_json,
|
|
1274
|
+
template_name,
|
|
1275
|
+
template_views
|
|
1276
|
+
FROM template_node_configs
|
|
1277
|
+
WHERE node_type = ?
|
|
1278
|
+
ORDER BY rank
|
|
1279
|
+
LIMIT 2
|
|
1280
|
+
`).all(nodeResult.workflowNodeType);
|
|
1281
|
+
if (examples.length > 0) {
|
|
1282
|
+
nodeResult.examples = examples.map((ex) => ({
|
|
1283
|
+
configuration: JSON.parse(ex.parameters_json),
|
|
1284
|
+
template: ex.template_name,
|
|
1285
|
+
views: ex.template_views
|
|
1286
|
+
}));
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
catch (error) {
|
|
1291
|
+
logger_1.logger.error(`Failed to add examples:`, error);
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
telemetry_1.telemetry.trackSearchQuery(query, scoredNodes.length, mode ?? 'OR');
|
|
1295
|
+
return result;
|
|
1296
|
+
}
|
|
1297
|
+
catch (error) {
|
|
1298
|
+
logger_1.logger.warn('FTS5 search failed, falling back to LIKE search:', error.message);
|
|
1299
|
+
if (error.message.includes('syntax error') || error.message.includes('fts5')) {
|
|
1300
|
+
logger_1.logger.warn(`FTS5 syntax error for query "${query}" in mode ${mode}`);
|
|
1301
|
+
const likeResult = await this.searchNodesLIKE(query, limit);
|
|
1302
|
+
telemetry_1.telemetry.trackSearchQuery(query, likeResult.results?.length ?? 0, `${mode}_LIKE_FALLBACK`);
|
|
1303
|
+
return {
|
|
1304
|
+
...likeResult,
|
|
1305
|
+
mode
|
|
1306
|
+
};
|
|
1307
|
+
}
|
|
1308
|
+
return this.searchNodesLIKE(query, limit);
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
async searchNodesFuzzy(query, limit) {
|
|
1312
|
+
if (!this.db)
|
|
1313
|
+
throw new Error('Database not initialized');
|
|
1314
|
+
const words = query.toLowerCase().split(/\s+/).filter(w => w.length > 0);
|
|
1315
|
+
if (words.length === 0) {
|
|
1316
|
+
return { query, results: [], totalCount: 0, mode: 'FUZZY' };
|
|
1317
|
+
}
|
|
1318
|
+
const candidateNodes = this.db.prepare(`
|
|
1319
|
+
SELECT * FROM nodes
|
|
1320
|
+
`).all();
|
|
1321
|
+
const scoredNodes = candidateNodes.map(node => {
|
|
1322
|
+
const score = this.calculateFuzzyScore(node, query);
|
|
1323
|
+
return { node, score };
|
|
1324
|
+
});
|
|
1325
|
+
const matchingNodes = scoredNodes
|
|
1326
|
+
.filter(item => item.score >= 200)
|
|
1327
|
+
.sort((a, b) => b.score - a.score)
|
|
1328
|
+
.slice(0, limit)
|
|
1329
|
+
.map(item => item.node);
|
|
1330
|
+
if (matchingNodes.length === 0) {
|
|
1331
|
+
const topScores = scoredNodes
|
|
1332
|
+
.sort((a, b) => b.score - a.score)
|
|
1333
|
+
.slice(0, 5);
|
|
1334
|
+
logger_1.logger.debug(`FUZZY search for "${query}" - no matches above 400. Top scores:`, topScores.map(s => ({ name: s.node.display_name, score: s.score })));
|
|
1335
|
+
}
|
|
1336
|
+
return {
|
|
1337
|
+
query,
|
|
1338
|
+
mode: 'FUZZY',
|
|
1339
|
+
results: matchingNodes.map(node => ({
|
|
1340
|
+
nodeType: node.node_type,
|
|
1341
|
+
workflowNodeType: (0, node_utils_1.getWorkflowNodeType)(node.package_name, node.node_type),
|
|
1342
|
+
displayName: node.display_name,
|
|
1343
|
+
description: node.description,
|
|
1344
|
+
category: node.category,
|
|
1345
|
+
package: node.package_name
|
|
1346
|
+
})),
|
|
1347
|
+
totalCount: matchingNodes.length
|
|
1348
|
+
};
|
|
1349
|
+
}
|
|
1350
|
+
calculateFuzzyScore(node, query) {
|
|
1351
|
+
const queryLower = query.toLowerCase();
|
|
1352
|
+
const displayNameLower = node.display_name.toLowerCase();
|
|
1353
|
+
const nodeTypeLower = node.node_type.toLowerCase();
|
|
1354
|
+
const nodeTypeClean = nodeTypeLower.replace(/^nodes-base\./, '').replace(/^nodes-langchain\./, '');
|
|
1355
|
+
if (displayNameLower === queryLower || nodeTypeClean === queryLower) {
|
|
1356
|
+
return 1000;
|
|
1357
|
+
}
|
|
1358
|
+
const nameDistance = this.getEditDistance(queryLower, displayNameLower);
|
|
1359
|
+
const typeDistance = this.getEditDistance(queryLower, nodeTypeClean);
|
|
1360
|
+
const nameWords = displayNameLower.split(/\s+/);
|
|
1361
|
+
let minWordDistance = Infinity;
|
|
1362
|
+
for (const word of nameWords) {
|
|
1363
|
+
const distance = this.getEditDistance(queryLower, word);
|
|
1364
|
+
if (distance < minWordDistance) {
|
|
1365
|
+
minWordDistance = distance;
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
const bestDistance = Math.min(nameDistance, typeDistance, minWordDistance);
|
|
1369
|
+
let matchedLen = queryLower.length;
|
|
1370
|
+
if (minWordDistance === bestDistance) {
|
|
1371
|
+
for (const word of nameWords) {
|
|
1372
|
+
if (this.getEditDistance(queryLower, word) === minWordDistance) {
|
|
1373
|
+
matchedLen = Math.max(queryLower.length, word.length);
|
|
1374
|
+
break;
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
else if (typeDistance === bestDistance) {
|
|
1379
|
+
matchedLen = Math.max(queryLower.length, nodeTypeClean.length);
|
|
1380
|
+
}
|
|
1381
|
+
else {
|
|
1382
|
+
matchedLen = Math.max(queryLower.length, displayNameLower.length);
|
|
1383
|
+
}
|
|
1384
|
+
const similarity = 1 - (bestDistance / matchedLen);
|
|
1385
|
+
if (displayNameLower.includes(queryLower) || nodeTypeClean.includes(queryLower)) {
|
|
1386
|
+
return 800 + (similarity * 100);
|
|
1387
|
+
}
|
|
1388
|
+
if (displayNameLower.startsWith(queryLower) ||
|
|
1389
|
+
nodeTypeClean.startsWith(queryLower) ||
|
|
1390
|
+
nameWords.some(w => w.startsWith(queryLower))) {
|
|
1391
|
+
return 700 + (similarity * 100);
|
|
1392
|
+
}
|
|
1393
|
+
if (bestDistance <= 2) {
|
|
1394
|
+
return 500 + ((2 - bestDistance) * 100) + (similarity * 50);
|
|
1395
|
+
}
|
|
1396
|
+
if (bestDistance <= 3 && queryLower.length >= 4) {
|
|
1397
|
+
return 400 + ((3 - bestDistance) * 50) + (similarity * 50);
|
|
1398
|
+
}
|
|
1399
|
+
return similarity * 300;
|
|
1400
|
+
}
|
|
1401
|
+
getEditDistance(s1, s2) {
|
|
1402
|
+
const m = s1.length;
|
|
1403
|
+
const n = s2.length;
|
|
1404
|
+
const dp = Array(m + 1).fill(null).map(() => Array(n + 1).fill(0));
|
|
1405
|
+
for (let i = 0; i <= m; i++)
|
|
1406
|
+
dp[i][0] = i;
|
|
1407
|
+
for (let j = 0; j <= n; j++)
|
|
1408
|
+
dp[0][j] = j;
|
|
1409
|
+
for (let i = 1; i <= m; i++) {
|
|
1410
|
+
for (let j = 1; j <= n; j++) {
|
|
1411
|
+
if (s1[i - 1] === s2[j - 1]) {
|
|
1412
|
+
dp[i][j] = dp[i - 1][j - 1];
|
|
1413
|
+
}
|
|
1414
|
+
else {
|
|
1415
|
+
dp[i][j] = 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
return dp[m][n];
|
|
1420
|
+
}
|
|
1421
|
+
async searchNodesLIKE(query, limit, options) {
|
|
1422
|
+
if (!this.db)
|
|
1423
|
+
throw new Error('Database not initialized');
|
|
1424
|
+
let sourceFilter = '';
|
|
1425
|
+
const sourceValue = options?.source || 'all';
|
|
1426
|
+
switch (sourceValue) {
|
|
1427
|
+
case 'core':
|
|
1428
|
+
sourceFilter = 'AND is_community = 0';
|
|
1429
|
+
break;
|
|
1430
|
+
case 'community':
|
|
1431
|
+
sourceFilter = 'AND is_community = 1';
|
|
1432
|
+
break;
|
|
1433
|
+
case 'verified':
|
|
1434
|
+
sourceFilter = 'AND is_community = 1 AND is_verified = 1';
|
|
1435
|
+
break;
|
|
1436
|
+
}
|
|
1437
|
+
if (query.startsWith('"') && query.endsWith('"')) {
|
|
1438
|
+
const exactPhrase = query.slice(1, -1);
|
|
1439
|
+
const nodes = this.db.prepare(`
|
|
1440
|
+
SELECT * FROM nodes
|
|
1441
|
+
WHERE (node_type LIKE ? OR display_name LIKE ? OR description LIKE ?)
|
|
1442
|
+
${sourceFilter}
|
|
1443
|
+
LIMIT ?
|
|
1444
|
+
`).all(`%${exactPhrase}%`, `%${exactPhrase}%`, `%${exactPhrase}%`, limit * 3);
|
|
1445
|
+
const rankedNodes = this.rankSearchResults(nodes, exactPhrase, limit);
|
|
1446
|
+
const result = {
|
|
1447
|
+
query,
|
|
1448
|
+
results: rankedNodes.map(node => {
|
|
1449
|
+
const nodeResult = {
|
|
1450
|
+
nodeType: node.node_type,
|
|
1451
|
+
workflowNodeType: (0, node_utils_1.getWorkflowNodeType)(node.package_name, node.node_type),
|
|
1452
|
+
displayName: node.display_name,
|
|
1453
|
+
description: node.description,
|
|
1454
|
+
category: node.category,
|
|
1455
|
+
package: node.package_name
|
|
1456
|
+
};
|
|
1457
|
+
if (node.is_community === 1) {
|
|
1458
|
+
nodeResult.isCommunity = true;
|
|
1459
|
+
nodeResult.isVerified = node.is_verified === 1;
|
|
1460
|
+
if (node.author_name) {
|
|
1461
|
+
nodeResult.authorName = node.author_name;
|
|
1462
|
+
}
|
|
1463
|
+
if (node.npm_downloads) {
|
|
1464
|
+
nodeResult.npmDownloads = node.npm_downloads;
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
return nodeResult;
|
|
1468
|
+
}),
|
|
1469
|
+
totalCount: rankedNodes.length
|
|
1470
|
+
};
|
|
1471
|
+
if (options?.includeExamples) {
|
|
1472
|
+
for (const nodeResult of result.results) {
|
|
1473
|
+
try {
|
|
1474
|
+
const examples = this.db.prepare(`
|
|
1475
|
+
SELECT
|
|
1476
|
+
parameters_json,
|
|
1477
|
+
template_name,
|
|
1478
|
+
template_views
|
|
1479
|
+
FROM template_node_configs
|
|
1480
|
+
WHERE node_type = ?
|
|
1481
|
+
ORDER BY rank
|
|
1482
|
+
LIMIT 2
|
|
1483
|
+
`).all(nodeResult.workflowNodeType);
|
|
1484
|
+
if (examples.length > 0) {
|
|
1485
|
+
nodeResult.examples = examples.map((ex) => ({
|
|
1486
|
+
configuration: JSON.parse(ex.parameters_json),
|
|
1487
|
+
template: ex.template_name,
|
|
1488
|
+
views: ex.template_views
|
|
1489
|
+
}));
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
catch (error) {
|
|
1493
|
+
logger_1.logger.warn(`Failed to fetch examples for ${nodeResult.nodeType}:`, error.message);
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
return result;
|
|
1498
|
+
}
|
|
1499
|
+
const words = query.toLowerCase().split(/\s+/).filter(w => w.length > 0);
|
|
1500
|
+
if (words.length === 0) {
|
|
1501
|
+
return { query, results: [], totalCount: 0 };
|
|
1502
|
+
}
|
|
1503
|
+
const conditions = words.map(() => '(node_type LIKE ? OR display_name LIKE ? OR description LIKE ?)').join(' OR ');
|
|
1504
|
+
const params = words.flatMap(w => [`%${w}%`, `%${w}%`, `%${w}%`]);
|
|
1505
|
+
params.push(limit * 3);
|
|
1506
|
+
const nodes = this.db.prepare(`
|
|
1507
|
+
SELECT DISTINCT * FROM nodes
|
|
1508
|
+
WHERE (${conditions})
|
|
1509
|
+
${sourceFilter}
|
|
1510
|
+
LIMIT ?
|
|
1511
|
+
`).all(...params);
|
|
1512
|
+
const rankedNodes = this.rankSearchResults(nodes, query, limit);
|
|
1513
|
+
const result = {
|
|
1514
|
+
query,
|
|
1515
|
+
results: rankedNodes.map(node => {
|
|
1516
|
+
const nodeResult = {
|
|
1517
|
+
nodeType: node.node_type,
|
|
1518
|
+
workflowNodeType: (0, node_utils_1.getWorkflowNodeType)(node.package_name, node.node_type),
|
|
1519
|
+
displayName: node.display_name,
|
|
1520
|
+
description: node.description,
|
|
1521
|
+
category: node.category,
|
|
1522
|
+
package: node.package_name
|
|
1523
|
+
};
|
|
1524
|
+
if (node.is_community === 1) {
|
|
1525
|
+
nodeResult.isCommunity = true;
|
|
1526
|
+
nodeResult.isVerified = node.is_verified === 1;
|
|
1527
|
+
if (node.author_name) {
|
|
1528
|
+
nodeResult.authorName = node.author_name;
|
|
1529
|
+
}
|
|
1530
|
+
if (node.npm_downloads) {
|
|
1531
|
+
nodeResult.npmDownloads = node.npm_downloads;
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
return nodeResult;
|
|
1535
|
+
}),
|
|
1536
|
+
totalCount: rankedNodes.length
|
|
1537
|
+
};
|
|
1538
|
+
if (options?.includeExamples) {
|
|
1539
|
+
for (const nodeResult of result.results) {
|
|
1540
|
+
try {
|
|
1541
|
+
const examples = this.db.prepare(`
|
|
1542
|
+
SELECT
|
|
1543
|
+
parameters_json,
|
|
1544
|
+
template_name,
|
|
1545
|
+
template_views
|
|
1546
|
+
FROM template_node_configs
|
|
1547
|
+
WHERE node_type = ?
|
|
1548
|
+
ORDER BY rank
|
|
1549
|
+
LIMIT 2
|
|
1550
|
+
`).all(nodeResult.workflowNodeType);
|
|
1551
|
+
if (examples.length > 0) {
|
|
1552
|
+
nodeResult.examples = examples.map((ex) => ({
|
|
1553
|
+
configuration: JSON.parse(ex.parameters_json),
|
|
1554
|
+
template: ex.template_name,
|
|
1555
|
+
views: ex.template_views
|
|
1556
|
+
}));
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
catch (error) {
|
|
1560
|
+
logger_1.logger.warn(`Failed to fetch examples for ${nodeResult.nodeType}:`, error.message);
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
return result;
|
|
1565
|
+
}
|
|
1566
|
+
calculateRelevance(node, query) {
|
|
1567
|
+
const lowerQuery = query.toLowerCase();
|
|
1568
|
+
if (node.node_type.toLowerCase().includes(lowerQuery))
|
|
1569
|
+
return 'high';
|
|
1570
|
+
if (node.display_name.toLowerCase().includes(lowerQuery))
|
|
1571
|
+
return 'high';
|
|
1572
|
+
if (node.description?.toLowerCase().includes(lowerQuery))
|
|
1573
|
+
return 'medium';
|
|
1574
|
+
return 'low';
|
|
1575
|
+
}
|
|
1576
|
+
calculateRelevanceScore(node, query) {
|
|
1577
|
+
const query_lower = query.toLowerCase();
|
|
1578
|
+
const name_lower = node.display_name.toLowerCase();
|
|
1579
|
+
const type_lower = node.node_type.toLowerCase();
|
|
1580
|
+
const type_without_prefix = type_lower.replace(/^nodes-base\./, '').replace(/^nodes-langchain\./, '');
|
|
1581
|
+
let score = 0;
|
|
1582
|
+
if (name_lower === query_lower) {
|
|
1583
|
+
score = 1000;
|
|
1584
|
+
}
|
|
1585
|
+
else if (type_without_prefix === query_lower) {
|
|
1586
|
+
score = 950;
|
|
1587
|
+
}
|
|
1588
|
+
else if (query_lower === 'webhook' && node.node_type === 'nodes-base.webhook') {
|
|
1589
|
+
score = 900;
|
|
1590
|
+
}
|
|
1591
|
+
else if ((query_lower === 'http' || query_lower === 'http request' || query_lower === 'http call') && node.node_type === 'nodes-base.httpRequest') {
|
|
1592
|
+
score = 900;
|
|
1593
|
+
}
|
|
1594
|
+
else if (query_lower.includes('http') && query_lower.includes('call') && node.node_type === 'nodes-base.httpRequest') {
|
|
1595
|
+
score = 890;
|
|
1596
|
+
}
|
|
1597
|
+
else if (query_lower.includes('http') && node.node_type === 'nodes-base.httpRequest') {
|
|
1598
|
+
score = 850;
|
|
1599
|
+
}
|
|
1600
|
+
else if (query_lower.includes('webhook') && node.node_type === 'nodes-base.webhook') {
|
|
1601
|
+
score = 850;
|
|
1602
|
+
}
|
|
1603
|
+
else if (name_lower.startsWith(query_lower)) {
|
|
1604
|
+
score = 800;
|
|
1605
|
+
}
|
|
1606
|
+
else if (new RegExp(`\\b${query_lower}\\b`, 'i').test(node.display_name)) {
|
|
1607
|
+
score = 700;
|
|
1608
|
+
}
|
|
1609
|
+
else if (name_lower.includes(query_lower)) {
|
|
1610
|
+
score = 600;
|
|
1611
|
+
}
|
|
1612
|
+
else if (type_without_prefix.includes(query_lower)) {
|
|
1613
|
+
score = 500;
|
|
1614
|
+
}
|
|
1615
|
+
else if (node.description?.toLowerCase().includes(query_lower)) {
|
|
1616
|
+
score = 400;
|
|
1617
|
+
}
|
|
1618
|
+
return score;
|
|
1619
|
+
}
|
|
1620
|
+
rankSearchResults(nodes, query, limit) {
|
|
1621
|
+
const query_lower = query.toLowerCase();
|
|
1622
|
+
const scoredNodes = nodes.map(node => {
|
|
1623
|
+
const name_lower = node.display_name.toLowerCase();
|
|
1624
|
+
const type_lower = node.node_type.toLowerCase();
|
|
1625
|
+
const type_without_prefix = type_lower.replace(/^nodes-base\./, '').replace(/^nodes-langchain\./, '');
|
|
1626
|
+
let score = 0;
|
|
1627
|
+
if (name_lower === query_lower) {
|
|
1628
|
+
score = 1000;
|
|
1629
|
+
}
|
|
1630
|
+
else if (type_without_prefix === query_lower) {
|
|
1631
|
+
score = 950;
|
|
1632
|
+
}
|
|
1633
|
+
else if (query_lower === 'webhook' && node.node_type === 'nodes-base.webhook') {
|
|
1634
|
+
score = 900;
|
|
1635
|
+
}
|
|
1636
|
+
else if ((query_lower === 'http' || query_lower === 'http request' || query_lower === 'http call') && node.node_type === 'nodes-base.httpRequest') {
|
|
1637
|
+
score = 900;
|
|
1638
|
+
}
|
|
1639
|
+
else if (query_lower.includes('webhook') && node.node_type === 'nodes-base.webhook') {
|
|
1640
|
+
score = 850;
|
|
1641
|
+
}
|
|
1642
|
+
else if (query_lower.includes('http') && node.node_type === 'nodes-base.httpRequest') {
|
|
1643
|
+
score = 850;
|
|
1644
|
+
}
|
|
1645
|
+
else if (name_lower.startsWith(query_lower)) {
|
|
1646
|
+
score = 800;
|
|
1647
|
+
}
|
|
1648
|
+
else if (new RegExp(`\\b${query_lower}\\b`, 'i').test(node.display_name)) {
|
|
1649
|
+
score = 700;
|
|
1650
|
+
}
|
|
1651
|
+
else if (name_lower.includes(query_lower)) {
|
|
1652
|
+
score = 600;
|
|
1653
|
+
}
|
|
1654
|
+
else if (type_without_prefix.includes(query_lower)) {
|
|
1655
|
+
score = 500;
|
|
1656
|
+
}
|
|
1657
|
+
else if (node.description?.toLowerCase().includes(query_lower)) {
|
|
1658
|
+
score = 400;
|
|
1659
|
+
}
|
|
1660
|
+
const words = query_lower.split(/\s+/).filter(w => w.length > 0);
|
|
1661
|
+
if (words.length > 1) {
|
|
1662
|
+
const allWordsInName = words.every(word => name_lower.includes(word));
|
|
1663
|
+
const allWordsInDesc = words.every(word => node.description?.toLowerCase().includes(word));
|
|
1664
|
+
if (allWordsInName)
|
|
1665
|
+
score += 200;
|
|
1666
|
+
else if (allWordsInDesc)
|
|
1667
|
+
score += 100;
|
|
1668
|
+
if (query_lower === 'http call' && name_lower === 'http request') {
|
|
1669
|
+
score = 920;
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
return { node, score };
|
|
1673
|
+
});
|
|
1674
|
+
scoredNodes.sort((a, b) => {
|
|
1675
|
+
if (a.score !== b.score) {
|
|
1676
|
+
return b.score - a.score;
|
|
1677
|
+
}
|
|
1678
|
+
return a.node.display_name.localeCompare(b.node.display_name);
|
|
1679
|
+
});
|
|
1680
|
+
return scoredNodes.slice(0, limit).map(item => item.node);
|
|
1681
|
+
}
|
|
1682
|
+
async listAITools() {
|
|
1683
|
+
await this.ensureInitialized();
|
|
1684
|
+
if (!this.repository)
|
|
1685
|
+
throw new Error('Repository not initialized');
|
|
1686
|
+
const tools = this.repository.getAITools();
|
|
1687
|
+
const aiCount = this.db.prepare('SELECT COUNT(*) as ai_count FROM nodes WHERE is_ai_tool = 1').get();
|
|
1688
|
+
return {
|
|
1689
|
+
tools,
|
|
1690
|
+
totalCount: tools.length,
|
|
1691
|
+
requirements: {
|
|
1692
|
+
environmentVariable: 'N8N_COMMUNITY_PACKAGES_ALLOW_TOOL_USAGE=true',
|
|
1693
|
+
nodeProperty: 'usableAsTool: true',
|
|
1694
|
+
},
|
|
1695
|
+
usage: {
|
|
1696
|
+
description: 'These nodes have the usableAsTool property set to true, making them optimized for AI agent usage.',
|
|
1697
|
+
note: 'ANY node in n8n can be used as an AI tool by connecting it to the ai_tool port of an AI Agent node.',
|
|
1698
|
+
examples: [
|
|
1699
|
+
'Regular nodes like Slack, Google Sheets, or HTTP Request can be used as tools',
|
|
1700
|
+
'Connect any node to an AI Agent\'s tool port to make it available for AI-driven automation',
|
|
1701
|
+
'Community nodes require the environment variable to be set'
|
|
1702
|
+
]
|
|
1703
|
+
}
|
|
1704
|
+
};
|
|
1705
|
+
}
|
|
1706
|
+
async getNodeDocumentation(nodeType) {
|
|
1707
|
+
await this.ensureInitialized();
|
|
1708
|
+
if (!this.db)
|
|
1709
|
+
throw new Error('Database not initialized');
|
|
1710
|
+
const normalizedType = node_type_normalizer_1.NodeTypeNormalizer.normalizeToFullForm(nodeType);
|
|
1711
|
+
let node = this.db.prepare(`
|
|
1712
|
+
SELECT node_type, display_name, documentation, description,
|
|
1713
|
+
ai_documentation_summary, ai_summary_generated_at
|
|
1714
|
+
FROM nodes
|
|
1715
|
+
WHERE node_type = ?
|
|
1716
|
+
`).get(normalizedType);
|
|
1717
|
+
if (!node && normalizedType !== nodeType) {
|
|
1718
|
+
node = this.db.prepare(`
|
|
1719
|
+
SELECT node_type, display_name, documentation, description,
|
|
1720
|
+
ai_documentation_summary, ai_summary_generated_at
|
|
1721
|
+
FROM nodes
|
|
1722
|
+
WHERE node_type = ?
|
|
1723
|
+
`).get(nodeType);
|
|
1724
|
+
}
|
|
1725
|
+
if (!node) {
|
|
1726
|
+
const alternatives = (0, node_utils_1.getNodeTypeAlternatives)(normalizedType);
|
|
1727
|
+
for (const alt of alternatives) {
|
|
1728
|
+
node = this.db.prepare(`
|
|
1729
|
+
SELECT node_type, display_name, documentation, description,
|
|
1730
|
+
ai_documentation_summary, ai_summary_generated_at
|
|
1731
|
+
FROM nodes
|
|
1732
|
+
WHERE node_type = ?
|
|
1733
|
+
`).get(alt);
|
|
1734
|
+
if (node)
|
|
1735
|
+
break;
|
|
1736
|
+
}
|
|
1737
|
+
}
|
|
1738
|
+
if (!node) {
|
|
1739
|
+
throw new Error(`Node ${nodeType} not found`);
|
|
1740
|
+
}
|
|
1741
|
+
const aiDocSummary = node.ai_documentation_summary
|
|
1742
|
+
? this.safeJsonParse(node.ai_documentation_summary, null)
|
|
1743
|
+
: null;
|
|
1744
|
+
if (!node.documentation) {
|
|
1745
|
+
const essentials = await this.getNodeEssentials(nodeType);
|
|
1746
|
+
return {
|
|
1747
|
+
nodeType: node.node_type,
|
|
1748
|
+
displayName: node.display_name || 'Unknown Node',
|
|
1749
|
+
documentation: `
|
|
1750
|
+
# ${node.display_name || 'Unknown Node'}
|
|
1751
|
+
|
|
1752
|
+
${node.description || 'No description available.'}
|
|
1753
|
+
|
|
1754
|
+
## Common Properties
|
|
1755
|
+
|
|
1756
|
+
${essentials?.commonProperties?.length > 0 ?
|
|
1757
|
+
essentials.commonProperties.map((p) => `### ${p.displayName || 'Property'}\n${p.description || `Type: ${p.type || 'unknown'}`}`).join('\n\n') :
|
|
1758
|
+
'No common properties available.'}
|
|
1759
|
+
|
|
1760
|
+
## Note
|
|
1761
|
+
Full documentation is being prepared. For now, use get_node_essentials for configuration help.
|
|
1762
|
+
`,
|
|
1763
|
+
hasDocumentation: false,
|
|
1764
|
+
aiDocumentationSummary: aiDocSummary,
|
|
1765
|
+
aiSummaryGeneratedAt: node.ai_summary_generated_at || null,
|
|
1766
|
+
};
|
|
1767
|
+
}
|
|
1768
|
+
return {
|
|
1769
|
+
nodeType: node.node_type,
|
|
1770
|
+
displayName: node.display_name || 'Unknown Node',
|
|
1771
|
+
documentation: node.documentation,
|
|
1772
|
+
hasDocumentation: true,
|
|
1773
|
+
aiDocumentationSummary: aiDocSummary,
|
|
1774
|
+
aiSummaryGeneratedAt: node.ai_summary_generated_at || null,
|
|
1775
|
+
};
|
|
1776
|
+
}
|
|
1777
|
+
safeJsonParse(json, defaultValue = null) {
|
|
1778
|
+
try {
|
|
1779
|
+
return JSON.parse(json);
|
|
1780
|
+
}
|
|
1781
|
+
catch {
|
|
1782
|
+
return defaultValue;
|
|
1783
|
+
}
|
|
1784
|
+
}
|
|
1785
|
+
async getDatabaseStatistics() {
|
|
1786
|
+
await this.ensureInitialized();
|
|
1787
|
+
if (!this.db)
|
|
1788
|
+
throw new Error('Database not initialized');
|
|
1789
|
+
const stats = this.db.prepare(`
|
|
1790
|
+
SELECT
|
|
1791
|
+
COUNT(*) as total,
|
|
1792
|
+
SUM(is_ai_tool) as ai_tools,
|
|
1793
|
+
SUM(is_trigger) as triggers,
|
|
1794
|
+
SUM(is_versioned) as versioned,
|
|
1795
|
+
SUM(CASE WHEN documentation IS NOT NULL THEN 1 ELSE 0 END) as with_docs,
|
|
1796
|
+
COUNT(DISTINCT package_name) as packages,
|
|
1797
|
+
COUNT(DISTINCT category) as categories
|
|
1798
|
+
FROM nodes
|
|
1799
|
+
`).get();
|
|
1800
|
+
const packages = this.db.prepare(`
|
|
1801
|
+
SELECT package_name, COUNT(*) as count
|
|
1802
|
+
FROM nodes
|
|
1803
|
+
GROUP BY package_name
|
|
1804
|
+
`).all();
|
|
1805
|
+
const templateStats = this.db.prepare(`
|
|
1806
|
+
SELECT
|
|
1807
|
+
COUNT(*) as total_templates,
|
|
1808
|
+
AVG(views) as avg_views,
|
|
1809
|
+
MIN(views) as min_views,
|
|
1810
|
+
MAX(views) as max_views
|
|
1811
|
+
FROM templates
|
|
1812
|
+
`).get();
|
|
1813
|
+
return {
|
|
1814
|
+
totalNodes: stats.total,
|
|
1815
|
+
totalTemplates: templateStats.total_templates || 0,
|
|
1816
|
+
statistics: {
|
|
1817
|
+
aiTools: stats.ai_tools,
|
|
1818
|
+
triggers: stats.triggers,
|
|
1819
|
+
versionedNodes: stats.versioned,
|
|
1820
|
+
nodesWithDocumentation: stats.with_docs,
|
|
1821
|
+
documentationCoverage: Math.round((stats.with_docs / stats.total) * 100) + '%',
|
|
1822
|
+
uniquePackages: stats.packages,
|
|
1823
|
+
uniqueCategories: stats.categories,
|
|
1824
|
+
templates: {
|
|
1825
|
+
total: templateStats.total_templates || 0,
|
|
1826
|
+
avgViews: Math.round(templateStats.avg_views || 0),
|
|
1827
|
+
minViews: templateStats.min_views || 0,
|
|
1828
|
+
maxViews: templateStats.max_views || 0
|
|
1829
|
+
}
|
|
1830
|
+
},
|
|
1831
|
+
packageBreakdown: packages.map(pkg => ({
|
|
1832
|
+
package: pkg.package_name,
|
|
1833
|
+
nodeCount: pkg.count,
|
|
1834
|
+
})),
|
|
1835
|
+
};
|
|
1836
|
+
}
|
|
1837
|
+
async getNodeEssentials(nodeType, includeExamples) {
|
|
1838
|
+
await this.ensureInitialized();
|
|
1839
|
+
if (!this.repository)
|
|
1840
|
+
throw new Error('Repository not initialized');
|
|
1841
|
+
const cacheKey = `essentials:${nodeType}:${includeExamples ? 'withExamples' : 'basic'}`;
|
|
1842
|
+
const cached = this.cache.get(cacheKey);
|
|
1843
|
+
if (cached)
|
|
1844
|
+
return cached;
|
|
1845
|
+
const normalizedType = node_type_normalizer_1.NodeTypeNormalizer.normalizeToFullForm(nodeType);
|
|
1846
|
+
let node = this.repository.getNode(normalizedType);
|
|
1847
|
+
if (!node && normalizedType !== nodeType) {
|
|
1848
|
+
node = this.repository.getNode(nodeType);
|
|
1849
|
+
}
|
|
1850
|
+
if (!node) {
|
|
1851
|
+
const alternatives = (0, node_utils_1.getNodeTypeAlternatives)(normalizedType);
|
|
1852
|
+
for (const alt of alternatives) {
|
|
1853
|
+
const found = this.repository.getNode(alt);
|
|
1854
|
+
if (found) {
|
|
1855
|
+
node = found;
|
|
1856
|
+
break;
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
}
|
|
1860
|
+
if (!node) {
|
|
1861
|
+
throw new Error(`Node ${nodeType} not found`);
|
|
1862
|
+
}
|
|
1863
|
+
const allProperties = node.properties || [];
|
|
1864
|
+
const essentials = property_filter_1.PropertyFilter.getEssentials(allProperties, node.nodeType);
|
|
1865
|
+
const operations = node.operations || [];
|
|
1866
|
+
const latestVersion = node.version ?? '1';
|
|
1867
|
+
const result = {
|
|
1868
|
+
nodeType: node.nodeType,
|
|
1869
|
+
workflowNodeType: (0, node_utils_1.getWorkflowNodeType)(node.package ?? 'n8n-nodes-base', node.nodeType),
|
|
1870
|
+
displayName: node.displayName,
|
|
1871
|
+
description: node.description,
|
|
1872
|
+
category: node.category,
|
|
1873
|
+
version: latestVersion,
|
|
1874
|
+
isVersioned: node.isVersioned ?? false,
|
|
1875
|
+
versionNotice: `⚠️ Use typeVersion: ${latestVersion} when creating this node`,
|
|
1876
|
+
requiredProperties: essentials.required,
|
|
1877
|
+
commonProperties: essentials.common,
|
|
1878
|
+
operations: operations.map((op) => ({
|
|
1879
|
+
name: op.name || op.operation,
|
|
1880
|
+
description: op.description,
|
|
1881
|
+
action: op.action,
|
|
1882
|
+
resource: op.resource
|
|
1883
|
+
})),
|
|
1884
|
+
metadata: {
|
|
1885
|
+
totalProperties: allProperties.length,
|
|
1886
|
+
isAITool: node.isAITool ?? false,
|
|
1887
|
+
isTrigger: node.isTrigger ?? false,
|
|
1888
|
+
isWebhook: node.isWebhook ?? false,
|
|
1889
|
+
hasCredentials: node.credentials ? true : false,
|
|
1890
|
+
package: node.package ?? 'n8n-nodes-base',
|
|
1891
|
+
developmentStyle: node.developmentStyle ?? 'programmatic'
|
|
1892
|
+
}
|
|
1893
|
+
};
|
|
1894
|
+
const toolVariantInfo = this.buildToolVariantGuidance(node);
|
|
1895
|
+
if (toolVariantInfo) {
|
|
1896
|
+
result.toolVariantInfo = toolVariantInfo;
|
|
1897
|
+
}
|
|
1898
|
+
if (includeExamples) {
|
|
1899
|
+
try {
|
|
1900
|
+
const examples = this.db.prepare(`
|
|
1901
|
+
SELECT
|
|
1902
|
+
parameters_json,
|
|
1903
|
+
template_name,
|
|
1904
|
+
template_views,
|
|
1905
|
+
complexity,
|
|
1906
|
+
use_cases,
|
|
1907
|
+
has_credentials,
|
|
1908
|
+
has_expressions
|
|
1909
|
+
FROM template_node_configs
|
|
1910
|
+
WHERE node_type = ?
|
|
1911
|
+
ORDER BY rank
|
|
1912
|
+
LIMIT 3
|
|
1913
|
+
`).all(result.workflowNodeType);
|
|
1914
|
+
if (examples.length > 0) {
|
|
1915
|
+
result.examples = examples.map((ex) => ({
|
|
1916
|
+
configuration: JSON.parse(ex.parameters_json),
|
|
1917
|
+
source: {
|
|
1918
|
+
template: ex.template_name,
|
|
1919
|
+
views: ex.template_views,
|
|
1920
|
+
complexity: ex.complexity
|
|
1921
|
+
},
|
|
1922
|
+
useCases: ex.use_cases ? JSON.parse(ex.use_cases).slice(0, 2) : [],
|
|
1923
|
+
metadata: {
|
|
1924
|
+
hasCredentials: ex.has_credentials === 1,
|
|
1925
|
+
hasExpressions: ex.has_expressions === 1
|
|
1926
|
+
}
|
|
1927
|
+
}));
|
|
1928
|
+
result.examplesCount = examples.length;
|
|
1929
|
+
}
|
|
1930
|
+
else {
|
|
1931
|
+
result.examples = [];
|
|
1932
|
+
result.examplesCount = 0;
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
1935
|
+
catch (error) {
|
|
1936
|
+
logger_1.logger.warn(`Failed to fetch examples for ${nodeType}:`, error.message);
|
|
1937
|
+
result.examples = [];
|
|
1938
|
+
result.examplesCount = 0;
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
this.cache.set(cacheKey, result, 3600);
|
|
1942
|
+
return result;
|
|
1943
|
+
}
|
|
1944
|
+
async getNode(nodeType, detail = 'standard', mode = 'info', includeTypeInfo, includeExamples, fromVersion, toVersion) {
|
|
1945
|
+
await this.ensureInitialized();
|
|
1946
|
+
if (!this.repository)
|
|
1947
|
+
throw new Error('Repository not initialized');
|
|
1948
|
+
const validDetailLevels = ['minimal', 'standard', 'full'];
|
|
1949
|
+
const validModes = ['info', 'versions', 'compare', 'breaking', 'migrations'];
|
|
1950
|
+
if (!validDetailLevels.includes(detail)) {
|
|
1951
|
+
throw new Error(`get_node: Invalid detail level "${detail}". Valid options: ${validDetailLevels.join(', ')}`);
|
|
1952
|
+
}
|
|
1953
|
+
if (!validModes.includes(mode)) {
|
|
1954
|
+
throw new Error(`get_node: Invalid mode "${mode}". Valid options: ${validModes.join(', ')}`);
|
|
1955
|
+
}
|
|
1956
|
+
const normalizedType = node_type_normalizer_1.NodeTypeNormalizer.normalizeToFullForm(nodeType);
|
|
1957
|
+
if (mode !== 'info') {
|
|
1958
|
+
return this.handleVersionMode(normalizedType, mode, fromVersion, toVersion);
|
|
1959
|
+
}
|
|
1960
|
+
return this.handleInfoMode(normalizedType, detail, includeTypeInfo, includeExamples);
|
|
1961
|
+
}
|
|
1962
|
+
async handleInfoMode(nodeType, detail, includeTypeInfo, includeExamples) {
|
|
1963
|
+
switch (detail) {
|
|
1964
|
+
case 'minimal': {
|
|
1965
|
+
let node = this.repository.getNode(nodeType);
|
|
1966
|
+
if (!node) {
|
|
1967
|
+
const alternatives = (0, node_utils_1.getNodeTypeAlternatives)(nodeType);
|
|
1968
|
+
for (const alt of alternatives) {
|
|
1969
|
+
const found = this.repository.getNode(alt);
|
|
1970
|
+
if (found) {
|
|
1971
|
+
node = found;
|
|
1972
|
+
break;
|
|
1973
|
+
}
|
|
1974
|
+
}
|
|
1975
|
+
}
|
|
1976
|
+
if (!node) {
|
|
1977
|
+
throw new Error(`Node ${nodeType} not found`);
|
|
1978
|
+
}
|
|
1979
|
+
const result = {
|
|
1980
|
+
nodeType: node.nodeType,
|
|
1981
|
+
workflowNodeType: (0, node_utils_1.getWorkflowNodeType)(node.package ?? 'n8n-nodes-base', node.nodeType),
|
|
1982
|
+
displayName: node.displayName,
|
|
1983
|
+
description: node.description,
|
|
1984
|
+
category: node.category,
|
|
1985
|
+
package: node.package,
|
|
1986
|
+
isAITool: node.isAITool,
|
|
1987
|
+
isTrigger: node.isTrigger,
|
|
1988
|
+
isWebhook: node.isWebhook
|
|
1989
|
+
};
|
|
1990
|
+
const toolVariantInfo = this.buildToolVariantGuidance(node);
|
|
1991
|
+
if (toolVariantInfo) {
|
|
1992
|
+
result.toolVariantInfo = toolVariantInfo;
|
|
1993
|
+
}
|
|
1994
|
+
return result;
|
|
1995
|
+
}
|
|
1996
|
+
case 'standard': {
|
|
1997
|
+
const essentials = await this.getNodeEssentials(nodeType, includeExamples);
|
|
1998
|
+
const versionSummary = this.getVersionSummary(nodeType);
|
|
1999
|
+
if (includeTypeInfo) {
|
|
2000
|
+
essentials.requiredProperties = this.enrichPropertiesWithTypeInfo(essentials.requiredProperties);
|
|
2001
|
+
essentials.commonProperties = this.enrichPropertiesWithTypeInfo(essentials.commonProperties);
|
|
2002
|
+
}
|
|
2003
|
+
return {
|
|
2004
|
+
...essentials,
|
|
2005
|
+
versionInfo: versionSummary
|
|
2006
|
+
};
|
|
2007
|
+
}
|
|
2008
|
+
case 'full': {
|
|
2009
|
+
const fullInfo = await this.getNodeInfo(nodeType);
|
|
2010
|
+
const versionSummary = this.getVersionSummary(nodeType);
|
|
2011
|
+
if (includeTypeInfo && fullInfo.properties) {
|
|
2012
|
+
fullInfo.properties = this.enrichPropertiesWithTypeInfo(fullInfo.properties);
|
|
2013
|
+
}
|
|
2014
|
+
return {
|
|
2015
|
+
...fullInfo,
|
|
2016
|
+
versionInfo: versionSummary
|
|
2017
|
+
};
|
|
2018
|
+
}
|
|
2019
|
+
default:
|
|
2020
|
+
throw new Error(`Unknown detail level: ${detail}`);
|
|
2021
|
+
}
|
|
2022
|
+
}
|
|
2023
|
+
async handleVersionMode(nodeType, mode, fromVersion, toVersion) {
|
|
2024
|
+
switch (mode) {
|
|
2025
|
+
case 'versions':
|
|
2026
|
+
return this.getVersionHistory(nodeType);
|
|
2027
|
+
case 'compare':
|
|
2028
|
+
if (!fromVersion) {
|
|
2029
|
+
throw new Error(`get_node: fromVersion is required for compare mode (nodeType: ${nodeType})`);
|
|
2030
|
+
}
|
|
2031
|
+
return this.compareVersions(nodeType, fromVersion, toVersion);
|
|
2032
|
+
case 'breaking':
|
|
2033
|
+
if (!fromVersion) {
|
|
2034
|
+
throw new Error(`get_node: fromVersion is required for breaking mode (nodeType: ${nodeType})`);
|
|
2035
|
+
}
|
|
2036
|
+
return this.getBreakingChanges(nodeType, fromVersion, toVersion);
|
|
2037
|
+
case 'migrations':
|
|
2038
|
+
if (!fromVersion || !toVersion) {
|
|
2039
|
+
throw new Error(`get_node: Both fromVersion and toVersion are required for migrations mode (nodeType: ${nodeType})`);
|
|
2040
|
+
}
|
|
2041
|
+
return this.getMigrations(nodeType, fromVersion, toVersion);
|
|
2042
|
+
default:
|
|
2043
|
+
throw new Error(`get_node: Unknown mode: ${mode} (nodeType: ${nodeType})`);
|
|
2044
|
+
}
|
|
2045
|
+
}
|
|
2046
|
+
getVersionSummary(nodeType) {
|
|
2047
|
+
const cacheKey = `version-summary:${nodeType}`;
|
|
2048
|
+
const cached = this.cache.get(cacheKey);
|
|
2049
|
+
if (cached) {
|
|
2050
|
+
return cached;
|
|
2051
|
+
}
|
|
2052
|
+
const versions = this.repository.getNodeVersions(nodeType);
|
|
2053
|
+
const latest = this.repository.getLatestNodeVersion(nodeType);
|
|
2054
|
+
const summary = {
|
|
2055
|
+
currentVersion: latest?.version || 'unknown',
|
|
2056
|
+
totalVersions: versions.length,
|
|
2057
|
+
hasVersionHistory: versions.length > 0
|
|
2058
|
+
};
|
|
2059
|
+
this.cache.set(cacheKey, summary, 86400000);
|
|
2060
|
+
return summary;
|
|
2061
|
+
}
|
|
2062
|
+
getVersionHistory(nodeType) {
|
|
2063
|
+
const versions = this.repository.getNodeVersions(nodeType);
|
|
2064
|
+
return {
|
|
2065
|
+
nodeType,
|
|
2066
|
+
totalVersions: versions.length,
|
|
2067
|
+
versions: versions.map(v => ({
|
|
2068
|
+
version: v.version,
|
|
2069
|
+
isCurrent: v.isCurrentMax,
|
|
2070
|
+
minimumN8nVersion: v.minimumN8nVersion,
|
|
2071
|
+
releasedAt: v.releasedAt,
|
|
2072
|
+
hasBreakingChanges: (v.breakingChanges || []).length > 0,
|
|
2073
|
+
breakingChangesCount: (v.breakingChanges || []).length,
|
|
2074
|
+
deprecatedProperties: v.deprecatedProperties || [],
|
|
2075
|
+
addedProperties: v.addedProperties || []
|
|
2076
|
+
})),
|
|
2077
|
+
available: versions.length > 0,
|
|
2078
|
+
message: versions.length === 0 ?
|
|
2079
|
+
'No version history available. Version tracking may not be enabled for this node.' :
|
|
2080
|
+
undefined
|
|
2081
|
+
};
|
|
2082
|
+
}
|
|
2083
|
+
compareVersions(nodeType, fromVersion, toVersion) {
|
|
2084
|
+
const latest = this.repository.getLatestNodeVersion(nodeType);
|
|
2085
|
+
const targetVersion = toVersion || latest?.version;
|
|
2086
|
+
if (!targetVersion) {
|
|
2087
|
+
throw new Error('No target version available');
|
|
2088
|
+
}
|
|
2089
|
+
const changes = this.repository.getPropertyChanges(nodeType, fromVersion, targetVersion);
|
|
2090
|
+
return {
|
|
2091
|
+
nodeType,
|
|
2092
|
+
fromVersion,
|
|
2093
|
+
toVersion: targetVersion,
|
|
2094
|
+
totalChanges: changes.length,
|
|
2095
|
+
breakingChanges: changes.filter(c => c.isBreaking).length,
|
|
2096
|
+
changes: changes.map(c => ({
|
|
2097
|
+
property: c.propertyName,
|
|
2098
|
+
changeType: c.changeType,
|
|
2099
|
+
isBreaking: c.isBreaking,
|
|
2100
|
+
severity: c.severity,
|
|
2101
|
+
oldValue: c.oldValue,
|
|
2102
|
+
newValue: c.newValue,
|
|
2103
|
+
migrationHint: c.migrationHint,
|
|
2104
|
+
autoMigratable: c.autoMigratable
|
|
2105
|
+
}))
|
|
2106
|
+
};
|
|
2107
|
+
}
|
|
2108
|
+
getBreakingChanges(nodeType, fromVersion, toVersion) {
|
|
2109
|
+
const breakingChanges = this.repository.getBreakingChanges(nodeType, fromVersion, toVersion);
|
|
2110
|
+
return {
|
|
2111
|
+
nodeType,
|
|
2112
|
+
fromVersion,
|
|
2113
|
+
toVersion: toVersion || 'latest',
|
|
2114
|
+
totalBreakingChanges: breakingChanges.length,
|
|
2115
|
+
changes: breakingChanges.map(c => ({
|
|
2116
|
+
fromVersion: c.fromVersion,
|
|
2117
|
+
toVersion: c.toVersion,
|
|
2118
|
+
property: c.propertyName,
|
|
2119
|
+
changeType: c.changeType,
|
|
2120
|
+
severity: c.severity,
|
|
2121
|
+
migrationHint: c.migrationHint,
|
|
2122
|
+
oldValue: c.oldValue,
|
|
2123
|
+
newValue: c.newValue
|
|
2124
|
+
})),
|
|
2125
|
+
upgradeSafe: breakingChanges.length === 0
|
|
2126
|
+
};
|
|
2127
|
+
}
|
|
2128
|
+
getMigrations(nodeType, fromVersion, toVersion) {
|
|
2129
|
+
const migrations = this.repository.getAutoMigratableChanges(nodeType, fromVersion, toVersion);
|
|
2130
|
+
const allChanges = this.repository.getPropertyChanges(nodeType, fromVersion, toVersion);
|
|
2131
|
+
return {
|
|
2132
|
+
nodeType,
|
|
2133
|
+
fromVersion,
|
|
2134
|
+
toVersion,
|
|
2135
|
+
autoMigratableChanges: migrations.length,
|
|
2136
|
+
totalChanges: allChanges.length,
|
|
2137
|
+
migrations: migrations.map(m => ({
|
|
2138
|
+
property: m.propertyName,
|
|
2139
|
+
changeType: m.changeType,
|
|
2140
|
+
migrationStrategy: m.migrationStrategy,
|
|
2141
|
+
severity: m.severity
|
|
2142
|
+
})),
|
|
2143
|
+
requiresManualMigration: migrations.length < allChanges.length
|
|
2144
|
+
};
|
|
2145
|
+
}
|
|
2146
|
+
enrichPropertyWithTypeInfo(property) {
|
|
2147
|
+
if (!property || !property.type)
|
|
2148
|
+
return property;
|
|
2149
|
+
const structure = type_structure_service_1.TypeStructureService.getStructure(property.type);
|
|
2150
|
+
if (!structure)
|
|
2151
|
+
return property;
|
|
2152
|
+
return {
|
|
2153
|
+
...property,
|
|
2154
|
+
typeInfo: {
|
|
2155
|
+
category: structure.type,
|
|
2156
|
+
jsType: structure.jsType,
|
|
2157
|
+
description: structure.description,
|
|
2158
|
+
isComplex: type_structure_service_1.TypeStructureService.isComplexType(property.type),
|
|
2159
|
+
isPrimitive: type_structure_service_1.TypeStructureService.isPrimitiveType(property.type),
|
|
2160
|
+
allowsExpressions: structure.validation?.allowExpressions ?? true,
|
|
2161
|
+
allowsEmpty: structure.validation?.allowEmpty ?? false,
|
|
2162
|
+
...(structure.structure && {
|
|
2163
|
+
structureHints: {
|
|
2164
|
+
hasProperties: !!structure.structure.properties,
|
|
2165
|
+
hasItems: !!structure.structure.items,
|
|
2166
|
+
isFlexible: structure.structure.flexible ?? false,
|
|
2167
|
+
requiredFields: structure.structure.required ?? []
|
|
2168
|
+
}
|
|
2169
|
+
}),
|
|
2170
|
+
...(structure.notes && { notes: structure.notes })
|
|
2171
|
+
}
|
|
2172
|
+
};
|
|
2173
|
+
}
|
|
2174
|
+
enrichPropertiesWithTypeInfo(properties) {
|
|
2175
|
+
if (!properties || !Array.isArray(properties))
|
|
2176
|
+
return properties;
|
|
2177
|
+
return properties.map((prop) => this.enrichPropertyWithTypeInfo(prop));
|
|
2178
|
+
}
|
|
2179
|
+
async searchNodeProperties(nodeType, query, maxResults = 20) {
|
|
2180
|
+
await this.ensureInitialized();
|
|
2181
|
+
if (!this.repository)
|
|
2182
|
+
throw new Error('Repository not initialized');
|
|
2183
|
+
const normalizedType = node_type_normalizer_1.NodeTypeNormalizer.normalizeToFullForm(nodeType);
|
|
2184
|
+
let node = this.repository.getNode(normalizedType);
|
|
2185
|
+
if (!node && normalizedType !== nodeType) {
|
|
2186
|
+
node = this.repository.getNode(nodeType);
|
|
2187
|
+
}
|
|
2188
|
+
if (!node) {
|
|
2189
|
+
const alternatives = (0, node_utils_1.getNodeTypeAlternatives)(normalizedType);
|
|
2190
|
+
for (const alt of alternatives) {
|
|
2191
|
+
const found = this.repository.getNode(alt);
|
|
2192
|
+
if (found) {
|
|
2193
|
+
node = found;
|
|
2194
|
+
break;
|
|
2195
|
+
}
|
|
2196
|
+
}
|
|
2197
|
+
}
|
|
2198
|
+
if (!node) {
|
|
2199
|
+
throw new Error(`Node ${nodeType} not found`);
|
|
2200
|
+
}
|
|
2201
|
+
const allProperties = node.properties || [];
|
|
2202
|
+
const matches = property_filter_1.PropertyFilter.searchProperties(allProperties, query, maxResults);
|
|
2203
|
+
return {
|
|
2204
|
+
nodeType: node.nodeType,
|
|
2205
|
+
query,
|
|
2206
|
+
matches: matches.map((match) => ({
|
|
2207
|
+
name: match.name,
|
|
2208
|
+
displayName: match.displayName,
|
|
2209
|
+
type: match.type,
|
|
2210
|
+
description: match.description,
|
|
2211
|
+
path: match.path || match.name,
|
|
2212
|
+
required: match.required,
|
|
2213
|
+
default: match.default,
|
|
2214
|
+
options: match.options,
|
|
2215
|
+
showWhen: match.showWhen
|
|
2216
|
+
})),
|
|
2217
|
+
totalMatches: matches.length,
|
|
2218
|
+
searchedIn: allProperties.length + ' properties'
|
|
2219
|
+
};
|
|
2220
|
+
}
|
|
2221
|
+
getPropertyValue(config, path) {
|
|
2222
|
+
const parts = path.split('.');
|
|
2223
|
+
let value = config;
|
|
2224
|
+
for (const part of parts) {
|
|
2225
|
+
const arrayMatch = part.match(/^(\w+)\[(\d+)\]$/);
|
|
2226
|
+
if (arrayMatch) {
|
|
2227
|
+
value = value?.[arrayMatch[1]]?.[parseInt(arrayMatch[2])];
|
|
2228
|
+
}
|
|
2229
|
+
else {
|
|
2230
|
+
value = value?.[part];
|
|
2231
|
+
}
|
|
2232
|
+
}
|
|
2233
|
+
return value;
|
|
2234
|
+
}
|
|
2235
|
+
async listTasks(category) {
|
|
2236
|
+
if (category) {
|
|
2237
|
+
const categories = task_templates_1.TaskTemplates.getTaskCategories();
|
|
2238
|
+
const tasks = categories[category];
|
|
2239
|
+
if (!tasks) {
|
|
2240
|
+
throw new Error(`Unknown category: ${category}. Available categories: ${Object.keys(categories).join(', ')}`);
|
|
2241
|
+
}
|
|
2242
|
+
return {
|
|
2243
|
+
category,
|
|
2244
|
+
tasks: tasks.map(task => {
|
|
2245
|
+
const template = task_templates_1.TaskTemplates.getTaskTemplate(task);
|
|
2246
|
+
return {
|
|
2247
|
+
task,
|
|
2248
|
+
description: template?.description || '',
|
|
2249
|
+
nodeType: template?.nodeType || ''
|
|
2250
|
+
};
|
|
2251
|
+
})
|
|
2252
|
+
};
|
|
2253
|
+
}
|
|
2254
|
+
const categories = task_templates_1.TaskTemplates.getTaskCategories();
|
|
2255
|
+
const result = {
|
|
2256
|
+
totalTasks: task_templates_1.TaskTemplates.getAllTasks().length,
|
|
2257
|
+
categories: {}
|
|
2258
|
+
};
|
|
2259
|
+
for (const [cat, tasks] of Object.entries(categories)) {
|
|
2260
|
+
result.categories[cat] = tasks.map(task => {
|
|
2261
|
+
const template = task_templates_1.TaskTemplates.getTaskTemplate(task);
|
|
2262
|
+
return {
|
|
2263
|
+
task,
|
|
2264
|
+
description: template?.description || '',
|
|
2265
|
+
nodeType: template?.nodeType || ''
|
|
2266
|
+
};
|
|
2267
|
+
});
|
|
2268
|
+
}
|
|
2269
|
+
return result;
|
|
2270
|
+
}
|
|
2271
|
+
async validateNodeConfig(nodeType, config, mode = 'operation', profile = 'ai-friendly') {
|
|
2272
|
+
await this.ensureInitialized();
|
|
2273
|
+
if (!this.repository)
|
|
2274
|
+
throw new Error('Repository not initialized');
|
|
2275
|
+
const normalizedType = node_type_normalizer_1.NodeTypeNormalizer.normalizeToFullForm(nodeType);
|
|
2276
|
+
let node = this.repository.getNode(normalizedType);
|
|
2277
|
+
if (!node && normalizedType !== nodeType) {
|
|
2278
|
+
node = this.repository.getNode(nodeType);
|
|
2279
|
+
}
|
|
2280
|
+
if (!node) {
|
|
2281
|
+
const alternatives = (0, node_utils_1.getNodeTypeAlternatives)(normalizedType);
|
|
2282
|
+
for (const alt of alternatives) {
|
|
2283
|
+
const found = this.repository.getNode(alt);
|
|
2284
|
+
if (found) {
|
|
2285
|
+
node = found;
|
|
2286
|
+
break;
|
|
2287
|
+
}
|
|
2288
|
+
}
|
|
2289
|
+
}
|
|
2290
|
+
if (!node) {
|
|
2291
|
+
throw new Error(`Node ${nodeType} not found`);
|
|
2292
|
+
}
|
|
2293
|
+
const properties = node.properties || [];
|
|
2294
|
+
const configWithVersion = {
|
|
2295
|
+
'@version': node.version || 1,
|
|
2296
|
+
...config
|
|
2297
|
+
};
|
|
2298
|
+
const validationResult = enhanced_config_validator_1.EnhancedConfigValidator.validateWithMode(node.nodeType, configWithVersion, properties, mode, profile);
|
|
2299
|
+
return {
|
|
2300
|
+
nodeType: node.nodeType,
|
|
2301
|
+
workflowNodeType: (0, node_utils_1.getWorkflowNodeType)(node.package, node.nodeType),
|
|
2302
|
+
displayName: node.displayName,
|
|
2303
|
+
...validationResult,
|
|
2304
|
+
summary: {
|
|
2305
|
+
hasErrors: !validationResult.valid,
|
|
2306
|
+
errorCount: validationResult.errors.length,
|
|
2307
|
+
warningCount: validationResult.warnings.length,
|
|
2308
|
+
suggestionCount: validationResult.suggestions.length
|
|
2309
|
+
}
|
|
2310
|
+
};
|
|
2311
|
+
}
|
|
2312
|
+
async getPropertyDependencies(nodeType, config) {
|
|
2313
|
+
await this.ensureInitialized();
|
|
2314
|
+
if (!this.repository)
|
|
2315
|
+
throw new Error('Repository not initialized');
|
|
2316
|
+
const normalizedType = node_type_normalizer_1.NodeTypeNormalizer.normalizeToFullForm(nodeType);
|
|
2317
|
+
let node = this.repository.getNode(normalizedType);
|
|
2318
|
+
if (!node && normalizedType !== nodeType) {
|
|
2319
|
+
node = this.repository.getNode(nodeType);
|
|
2320
|
+
}
|
|
2321
|
+
if (!node) {
|
|
2322
|
+
const alternatives = (0, node_utils_1.getNodeTypeAlternatives)(normalizedType);
|
|
2323
|
+
for (const alt of alternatives) {
|
|
2324
|
+
const found = this.repository.getNode(alt);
|
|
2325
|
+
if (found) {
|
|
2326
|
+
node = found;
|
|
2327
|
+
break;
|
|
2328
|
+
}
|
|
2329
|
+
}
|
|
2330
|
+
}
|
|
2331
|
+
if (!node) {
|
|
2332
|
+
throw new Error(`Node ${nodeType} not found`);
|
|
2333
|
+
}
|
|
2334
|
+
const properties = node.properties || [];
|
|
2335
|
+
const analysis = property_dependencies_1.PropertyDependencies.analyze(properties);
|
|
2336
|
+
let visibilityImpact = null;
|
|
2337
|
+
if (config) {
|
|
2338
|
+
visibilityImpact = property_dependencies_1.PropertyDependencies.getVisibilityImpact(properties, config);
|
|
2339
|
+
}
|
|
2340
|
+
return {
|
|
2341
|
+
nodeType: node.nodeType,
|
|
2342
|
+
displayName: node.displayName,
|
|
2343
|
+
...analysis,
|
|
2344
|
+
currentConfig: config ? {
|
|
2345
|
+
providedValues: config,
|
|
2346
|
+
visibilityImpact
|
|
2347
|
+
} : undefined
|
|
2348
|
+
};
|
|
2349
|
+
}
|
|
2350
|
+
async getNodeAsToolInfo(nodeType) {
|
|
2351
|
+
await this.ensureInitialized();
|
|
2352
|
+
if (!this.repository)
|
|
2353
|
+
throw new Error('Repository not initialized');
|
|
2354
|
+
const normalizedType = node_type_normalizer_1.NodeTypeNormalizer.normalizeToFullForm(nodeType);
|
|
2355
|
+
let node = this.repository.getNode(normalizedType);
|
|
2356
|
+
if (!node && normalizedType !== nodeType) {
|
|
2357
|
+
node = this.repository.getNode(nodeType);
|
|
2358
|
+
}
|
|
2359
|
+
if (!node) {
|
|
2360
|
+
const alternatives = (0, node_utils_1.getNodeTypeAlternatives)(normalizedType);
|
|
2361
|
+
for (const alt of alternatives) {
|
|
2362
|
+
const found = this.repository.getNode(alt);
|
|
2363
|
+
if (found) {
|
|
2364
|
+
node = found;
|
|
2365
|
+
break;
|
|
2366
|
+
}
|
|
2367
|
+
}
|
|
2368
|
+
}
|
|
2369
|
+
if (!node) {
|
|
2370
|
+
throw new Error(`Node ${nodeType} not found`);
|
|
2371
|
+
}
|
|
2372
|
+
const commonUseCases = this.getCommonAIToolUseCases(node.nodeType);
|
|
2373
|
+
const aiToolCapabilities = {
|
|
2374
|
+
canBeUsedAsTool: true,
|
|
2375
|
+
hasUsableAsToolProperty: node.isAITool,
|
|
2376
|
+
requiresEnvironmentVariable: !node.isAITool && node.package !== 'n8n-nodes-base',
|
|
2377
|
+
connectionType: 'ai_tool',
|
|
2378
|
+
commonUseCases,
|
|
2379
|
+
requirements: {
|
|
2380
|
+
connection: 'Connect to the "ai_tool" port of an AI Agent node',
|
|
2381
|
+
environment: node.package !== 'n8n-nodes-base' ?
|
|
2382
|
+
'Set N8N_COMMUNITY_PACKAGES_ALLOW_TOOL_USAGE=true for community nodes' :
|
|
2383
|
+
'No special environment variables needed for built-in nodes'
|
|
2384
|
+
},
|
|
2385
|
+
examples: this.getAIToolExamples(node.nodeType),
|
|
2386
|
+
tips: [
|
|
2387
|
+
'Give the tool a clear, descriptive name in the AI Agent settings',
|
|
2388
|
+
'Write a detailed tool description to help the AI understand when to use it',
|
|
2389
|
+
'Test the node independently before connecting it as a tool',
|
|
2390
|
+
node.isAITool ?
|
|
2391
|
+
'This node is optimized for AI tool usage' :
|
|
2392
|
+
'This is a regular node that can be used as an AI tool'
|
|
2393
|
+
]
|
|
2394
|
+
};
|
|
2395
|
+
return {
|
|
2396
|
+
nodeType: node.nodeType,
|
|
2397
|
+
workflowNodeType: (0, node_utils_1.getWorkflowNodeType)(node.package, node.nodeType),
|
|
2398
|
+
displayName: node.displayName,
|
|
2399
|
+
description: node.description,
|
|
2400
|
+
package: node.package,
|
|
2401
|
+
isMarkedAsAITool: node.isAITool,
|
|
2402
|
+
aiToolCapabilities
|
|
2403
|
+
};
|
|
2404
|
+
}
|
|
2405
|
+
getOutputDescriptions(nodeType, outputName, index) {
|
|
2406
|
+
if (nodeType === 'nodes-base.splitInBatches') {
|
|
2407
|
+
if (outputName === 'done' && index === 0) {
|
|
2408
|
+
return {
|
|
2409
|
+
description: 'Final processed data after all iterations complete',
|
|
2410
|
+
connectionGuidance: 'Connect to nodes that should run AFTER the loop completes'
|
|
2411
|
+
};
|
|
2412
|
+
}
|
|
2413
|
+
else if (outputName === 'loop' && index === 1) {
|
|
2414
|
+
return {
|
|
2415
|
+
description: 'Current batch data for this iteration',
|
|
2416
|
+
connectionGuidance: 'Connect to nodes that process items INSIDE the loop (and connect their output back to this node)'
|
|
2417
|
+
};
|
|
2418
|
+
}
|
|
2419
|
+
}
|
|
2420
|
+
if (nodeType === 'nodes-base.if') {
|
|
2421
|
+
if (outputName === 'true' && index === 0) {
|
|
2422
|
+
return {
|
|
2423
|
+
description: 'Items that match the condition',
|
|
2424
|
+
connectionGuidance: 'Connect to nodes that handle the TRUE case'
|
|
2425
|
+
};
|
|
2426
|
+
}
|
|
2427
|
+
else if (outputName === 'false' && index === 1) {
|
|
2428
|
+
return {
|
|
2429
|
+
description: 'Items that do not match the condition',
|
|
2430
|
+
connectionGuidance: 'Connect to nodes that handle the FALSE case'
|
|
2431
|
+
};
|
|
2432
|
+
}
|
|
2433
|
+
}
|
|
2434
|
+
if (nodeType === 'nodes-base.switch') {
|
|
2435
|
+
return {
|
|
2436
|
+
description: `Output ${index}: ${outputName || 'Route ' + index}`,
|
|
2437
|
+
connectionGuidance: `Connect to nodes for the "${outputName || 'route ' + index}" case`
|
|
2438
|
+
};
|
|
2439
|
+
}
|
|
2440
|
+
return {
|
|
2441
|
+
description: outputName || `Output ${index}`,
|
|
2442
|
+
connectionGuidance: `Connect to downstream nodes`
|
|
2443
|
+
};
|
|
2444
|
+
}
|
|
2445
|
+
getCommonAIToolUseCases(nodeType) {
|
|
2446
|
+
const useCaseMap = {
|
|
2447
|
+
'nodes-base.slack': [
|
|
2448
|
+
'Send notifications about task completion',
|
|
2449
|
+
'Post updates to channels',
|
|
2450
|
+
'Send direct messages',
|
|
2451
|
+
'Create alerts and reminders'
|
|
2452
|
+
],
|
|
2453
|
+
'nodes-base.googleSheets': [
|
|
2454
|
+
'Read data for analysis',
|
|
2455
|
+
'Log results and outputs',
|
|
2456
|
+
'Update spreadsheet records',
|
|
2457
|
+
'Create reports'
|
|
2458
|
+
],
|
|
2459
|
+
'nodes-base.gmail': [
|
|
2460
|
+
'Send email notifications',
|
|
2461
|
+
'Read and process emails',
|
|
2462
|
+
'Send reports and summaries',
|
|
2463
|
+
'Handle email-based workflows'
|
|
2464
|
+
],
|
|
2465
|
+
'nodes-base.httpRequest': [
|
|
2466
|
+
'Call external APIs',
|
|
2467
|
+
'Fetch data from web services',
|
|
2468
|
+
'Send webhooks',
|
|
2469
|
+
'Integrate with any REST API'
|
|
2470
|
+
],
|
|
2471
|
+
'nodes-base.postgres': [
|
|
2472
|
+
'Query database for information',
|
|
2473
|
+
'Store analysis results',
|
|
2474
|
+
'Update records based on AI decisions',
|
|
2475
|
+
'Generate reports from data'
|
|
2476
|
+
],
|
|
2477
|
+
'nodes-base.webhook': [
|
|
2478
|
+
'Receive external triggers',
|
|
2479
|
+
'Create callback endpoints',
|
|
2480
|
+
'Handle incoming data',
|
|
2481
|
+
'Integrate with external systems'
|
|
2482
|
+
]
|
|
2483
|
+
};
|
|
2484
|
+
for (const [key, useCases] of Object.entries(useCaseMap)) {
|
|
2485
|
+
if (nodeType.includes(key)) {
|
|
2486
|
+
return useCases;
|
|
2487
|
+
}
|
|
2488
|
+
}
|
|
2489
|
+
return [
|
|
2490
|
+
'Perform automated actions',
|
|
2491
|
+
'Integrate with external services',
|
|
2492
|
+
'Process and transform data',
|
|
2493
|
+
'Extend AI agent capabilities'
|
|
2494
|
+
];
|
|
2495
|
+
}
|
|
2496
|
+
buildToolVariantGuidance(node) {
|
|
2497
|
+
const isToolVariant = !!node.isToolVariant;
|
|
2498
|
+
const hasToolVariant = !!node.hasToolVariant;
|
|
2499
|
+
const toolVariantOf = node.toolVariantOf;
|
|
2500
|
+
if (!isToolVariant && !hasToolVariant) {
|
|
2501
|
+
return undefined;
|
|
2502
|
+
}
|
|
2503
|
+
if (isToolVariant) {
|
|
2504
|
+
return {
|
|
2505
|
+
isToolVariant: true,
|
|
2506
|
+
toolVariantOf,
|
|
2507
|
+
hasToolVariant: false,
|
|
2508
|
+
guidance: `This is the Tool variant for AI Agent integration. Use this node type when connecting to AI Agents. The base node is: ${toolVariantOf}`
|
|
2509
|
+
};
|
|
2510
|
+
}
|
|
2511
|
+
if (hasToolVariant && node.nodeType) {
|
|
2512
|
+
const toolVariantNodeType = `${node.nodeType}Tool`;
|
|
2513
|
+
return {
|
|
2514
|
+
isToolVariant: false,
|
|
2515
|
+
hasToolVariant: true,
|
|
2516
|
+
toolVariantNodeType,
|
|
2517
|
+
guidance: `To use this node with AI Agents, use the Tool variant: ${toolVariantNodeType}. The Tool variant has an additional 'toolDescription' property and outputs 'ai_tool' instead of 'main'.`
|
|
2518
|
+
};
|
|
2519
|
+
}
|
|
2520
|
+
return undefined;
|
|
2521
|
+
}
|
|
2522
|
+
getAIToolExamples(nodeType) {
|
|
2523
|
+
const exampleMap = {
|
|
2524
|
+
'nodes-base.slack': {
|
|
2525
|
+
toolName: 'Send Slack Message',
|
|
2526
|
+
toolDescription: 'Sends a message to a specified Slack channel or user. Use this to notify team members about important events or results.',
|
|
2527
|
+
nodeConfig: {
|
|
2528
|
+
resource: 'message',
|
|
2529
|
+
operation: 'post',
|
|
2530
|
+
channel: '={{ $fromAI("channel", "The Slack channel to send to, e.g. #general") }}',
|
|
2531
|
+
text: '={{ $fromAI("message", "The message content to send") }}'
|
|
2532
|
+
}
|
|
2533
|
+
},
|
|
2534
|
+
'nodes-base.googleSheets': {
|
|
2535
|
+
toolName: 'Update Google Sheet',
|
|
2536
|
+
toolDescription: 'Reads or updates data in a Google Sheets spreadsheet. Use this to log information, retrieve data, or update records.',
|
|
2537
|
+
nodeConfig: {
|
|
2538
|
+
operation: 'append',
|
|
2539
|
+
sheetId: 'your-sheet-id',
|
|
2540
|
+
range: 'A:Z',
|
|
2541
|
+
dataMode: 'autoMap'
|
|
2542
|
+
}
|
|
2543
|
+
},
|
|
2544
|
+
'nodes-base.httpRequest': {
|
|
2545
|
+
toolName: 'Call API',
|
|
2546
|
+
toolDescription: 'Makes HTTP requests to external APIs. Use this to fetch data, trigger webhooks, or integrate with any web service.',
|
|
2547
|
+
nodeConfig: {
|
|
2548
|
+
method: '={{ $fromAI("method", "HTTP method: GET, POST, PUT, DELETE") }}',
|
|
2549
|
+
url: '={{ $fromAI("url", "The complete API endpoint URL") }}',
|
|
2550
|
+
sendBody: true,
|
|
2551
|
+
bodyContentType: 'json',
|
|
2552
|
+
jsonBody: '={{ $fromAI("body", "Request body as JSON object") }}'
|
|
2553
|
+
}
|
|
2554
|
+
}
|
|
2555
|
+
};
|
|
2556
|
+
for (const [key, example] of Object.entries(exampleMap)) {
|
|
2557
|
+
if (nodeType.includes(key)) {
|
|
2558
|
+
return example;
|
|
2559
|
+
}
|
|
2560
|
+
}
|
|
2561
|
+
return {
|
|
2562
|
+
toolName: 'Custom Tool',
|
|
2563
|
+
toolDescription: 'Performs specific operations. Describe what this tool does and when to use it.',
|
|
2564
|
+
nodeConfig: {
|
|
2565
|
+
note: 'Configure the node based on its specific requirements'
|
|
2566
|
+
}
|
|
2567
|
+
};
|
|
2568
|
+
}
|
|
2569
|
+
async validateNodeMinimal(nodeType, config) {
|
|
2570
|
+
await this.ensureInitialized();
|
|
2571
|
+
if (!this.repository)
|
|
2572
|
+
throw new Error('Repository not initialized');
|
|
2573
|
+
const normalizedType = node_type_normalizer_1.NodeTypeNormalizer.normalizeToFullForm(nodeType);
|
|
2574
|
+
let node = this.repository.getNode(normalizedType);
|
|
2575
|
+
if (!node && normalizedType !== nodeType) {
|
|
2576
|
+
node = this.repository.getNode(nodeType);
|
|
2577
|
+
}
|
|
2578
|
+
if (!node) {
|
|
2579
|
+
const alternatives = (0, node_utils_1.getNodeTypeAlternatives)(normalizedType);
|
|
2580
|
+
for (const alt of alternatives) {
|
|
2581
|
+
const found = this.repository.getNode(alt);
|
|
2582
|
+
if (found) {
|
|
2583
|
+
node = found;
|
|
2584
|
+
break;
|
|
2585
|
+
}
|
|
2586
|
+
}
|
|
2587
|
+
}
|
|
2588
|
+
if (!node) {
|
|
2589
|
+
throw new Error(`Node ${nodeType} not found`);
|
|
2590
|
+
}
|
|
2591
|
+
const properties = node.properties || [];
|
|
2592
|
+
const configWithVersion = {
|
|
2593
|
+
'@version': node.version || 1,
|
|
2594
|
+
...(config || {})
|
|
2595
|
+
};
|
|
2596
|
+
const missingFields = [];
|
|
2597
|
+
for (const prop of properties) {
|
|
2598
|
+
if (!prop.required)
|
|
2599
|
+
continue;
|
|
2600
|
+
if (prop.displayOptions && !config_validator_1.ConfigValidator.isPropertyVisible(prop, configWithVersion)) {
|
|
2601
|
+
continue;
|
|
2602
|
+
}
|
|
2603
|
+
if (!config || !(prop.name in config)) {
|
|
2604
|
+
missingFields.push(prop.displayName || prop.name);
|
|
2605
|
+
}
|
|
2606
|
+
}
|
|
2607
|
+
return {
|
|
2608
|
+
nodeType: node.nodeType,
|
|
2609
|
+
displayName: node.displayName,
|
|
2610
|
+
valid: missingFields.length === 0,
|
|
2611
|
+
missingRequiredFields: missingFields
|
|
2612
|
+
};
|
|
2613
|
+
}
|
|
2614
|
+
async getToolsDocumentation(topic, depth = 'essentials') {
|
|
2615
|
+
if (!topic || topic === 'overview') {
|
|
2616
|
+
return (0, tools_documentation_1.getToolsOverview)(depth);
|
|
2617
|
+
}
|
|
2618
|
+
return (0, tools_documentation_1.getToolDocumentation)(topic, depth);
|
|
2619
|
+
}
|
|
2620
|
+
async connect(transport) {
|
|
2621
|
+
await this.ensureInitialized();
|
|
2622
|
+
await this.server.connect(transport);
|
|
2623
|
+
logger_1.logger.info('MCP Server connected', {
|
|
2624
|
+
transportType: transport.constructor.name
|
|
2625
|
+
});
|
|
2626
|
+
}
|
|
2627
|
+
async listTemplates(limit = 10, offset = 0, sortBy = 'views', includeMetadata = false) {
|
|
2628
|
+
await this.ensureInitialized();
|
|
2629
|
+
if (!this.templateService)
|
|
2630
|
+
throw new Error('Template service not initialized');
|
|
2631
|
+
const result = await this.templateService.listTemplates(limit, offset, sortBy, includeMetadata);
|
|
2632
|
+
return {
|
|
2633
|
+
...result,
|
|
2634
|
+
tip: result.items.length > 0 ?
|
|
2635
|
+
`Use get_template(templateId) to get full workflow details. Total: ${result.total} templates available.` :
|
|
2636
|
+
"No templates found. Run 'npm run fetch:templates' to update template database"
|
|
2637
|
+
};
|
|
2638
|
+
}
|
|
2639
|
+
async listNodeTemplates(nodeTypes, limit = 10, offset = 0) {
|
|
2640
|
+
await this.ensureInitialized();
|
|
2641
|
+
if (!this.templateService)
|
|
2642
|
+
throw new Error('Template service not initialized');
|
|
2643
|
+
const result = await this.templateService.listNodeTemplates(nodeTypes, limit, offset);
|
|
2644
|
+
if (result.items.length === 0 && offset === 0) {
|
|
2645
|
+
return {
|
|
2646
|
+
...result,
|
|
2647
|
+
message: `No templates found using nodes: ${nodeTypes.join(', ')}`,
|
|
2648
|
+
tip: "Try searching with more common nodes or run 'npm run fetch:templates' to update template database"
|
|
2649
|
+
};
|
|
2650
|
+
}
|
|
2651
|
+
return {
|
|
2652
|
+
...result,
|
|
2653
|
+
tip: `Showing ${result.items.length} of ${result.total} templates. Use offset for pagination.`
|
|
2654
|
+
};
|
|
2655
|
+
}
|
|
2656
|
+
async getTemplate(templateId, mode = 'full') {
|
|
2657
|
+
await this.ensureInitialized();
|
|
2658
|
+
if (!this.templateService)
|
|
2659
|
+
throw new Error('Template service not initialized');
|
|
2660
|
+
const template = await this.templateService.getTemplate(templateId, mode);
|
|
2661
|
+
if (!template) {
|
|
2662
|
+
return {
|
|
2663
|
+
error: `Template ${templateId} not found`,
|
|
2664
|
+
tip: "Use list_templates, list_node_templates or search_templates to find available templates"
|
|
2665
|
+
};
|
|
2666
|
+
}
|
|
2667
|
+
const usage = mode === 'nodes_only' ? "Node list for quick overview" :
|
|
2668
|
+
mode === 'structure' ? "Workflow structure without full details" :
|
|
2669
|
+
"Complete workflow JSON ready to import into n8n";
|
|
2670
|
+
return {
|
|
2671
|
+
mode,
|
|
2672
|
+
template,
|
|
2673
|
+
usage
|
|
2674
|
+
};
|
|
2675
|
+
}
|
|
2676
|
+
async searchTemplates(query, limit = 20, offset = 0, fields) {
|
|
2677
|
+
await this.ensureInitialized();
|
|
2678
|
+
if (!this.templateService)
|
|
2679
|
+
throw new Error('Template service not initialized');
|
|
2680
|
+
const result = await this.templateService.searchTemplates(query, limit, offset, fields);
|
|
2681
|
+
if (result.items.length === 0 && offset === 0) {
|
|
2682
|
+
return {
|
|
2683
|
+
...result,
|
|
2684
|
+
message: `No templates found matching: "${query}"`,
|
|
2685
|
+
tip: "Try different keywords or run 'npm run fetch:templates' to update template database"
|
|
2686
|
+
};
|
|
2687
|
+
}
|
|
2688
|
+
return {
|
|
2689
|
+
...result,
|
|
2690
|
+
query,
|
|
2691
|
+
tip: `Found ${result.total} templates matching "${query}". Showing ${result.items.length}.`
|
|
2692
|
+
};
|
|
2693
|
+
}
|
|
2694
|
+
async getTemplatesForTask(task, limit = 10, offset = 0) {
|
|
2695
|
+
await this.ensureInitialized();
|
|
2696
|
+
if (!this.templateService)
|
|
2697
|
+
throw new Error('Template service not initialized');
|
|
2698
|
+
const result = await this.templateService.getTemplatesForTask(task, limit, offset);
|
|
2699
|
+
const availableTasks = this.templateService.listAvailableTasks();
|
|
2700
|
+
if (result.items.length === 0 && offset === 0) {
|
|
2701
|
+
return {
|
|
2702
|
+
...result,
|
|
2703
|
+
message: `No templates found for task: ${task}`,
|
|
2704
|
+
availableTasks,
|
|
2705
|
+
tip: "Try a different task or use search_templates for custom searches"
|
|
2706
|
+
};
|
|
2707
|
+
}
|
|
2708
|
+
return {
|
|
2709
|
+
...result,
|
|
2710
|
+
task,
|
|
2711
|
+
description: this.getTaskDescription(task),
|
|
2712
|
+
tip: `${result.total} templates available for ${task}. Showing ${result.items.length}.`
|
|
2713
|
+
};
|
|
2714
|
+
}
|
|
2715
|
+
async searchTemplatesByMetadata(filters, limit = 20, offset = 0) {
|
|
2716
|
+
await this.ensureInitialized();
|
|
2717
|
+
if (!this.templateService)
|
|
2718
|
+
throw new Error('Template service not initialized');
|
|
2719
|
+
const result = await this.templateService.searchTemplatesByMetadata(filters, limit, offset);
|
|
2720
|
+
const filterSummary = [];
|
|
2721
|
+
if (filters.category)
|
|
2722
|
+
filterSummary.push(`category: ${filters.category}`);
|
|
2723
|
+
if (filters.complexity)
|
|
2724
|
+
filterSummary.push(`complexity: ${filters.complexity}`);
|
|
2725
|
+
if (filters.maxSetupMinutes)
|
|
2726
|
+
filterSummary.push(`max setup: ${filters.maxSetupMinutes} min`);
|
|
2727
|
+
if (filters.minSetupMinutes)
|
|
2728
|
+
filterSummary.push(`min setup: ${filters.minSetupMinutes} min`);
|
|
2729
|
+
if (filters.requiredService)
|
|
2730
|
+
filterSummary.push(`service: ${filters.requiredService}`);
|
|
2731
|
+
if (filters.targetAudience)
|
|
2732
|
+
filterSummary.push(`audience: ${filters.targetAudience}`);
|
|
2733
|
+
if (result.items.length === 0 && offset === 0) {
|
|
2734
|
+
const availableCategories = await this.templateService.getAvailableCategories();
|
|
2735
|
+
const availableAudiences = await this.templateService.getAvailableTargetAudiences();
|
|
2736
|
+
return {
|
|
2737
|
+
...result,
|
|
2738
|
+
message: `No templates found with filters: ${filterSummary.join(', ')}`,
|
|
2739
|
+
availableCategories: availableCategories.slice(0, 10),
|
|
2740
|
+
availableAudiences: availableAudiences.slice(0, 5),
|
|
2741
|
+
tip: "Try broader filters or different categories. Use list_templates to see all templates."
|
|
2742
|
+
};
|
|
2743
|
+
}
|
|
2744
|
+
return {
|
|
2745
|
+
...result,
|
|
2746
|
+
filters,
|
|
2747
|
+
filterSummary: filterSummary.join(', '),
|
|
2748
|
+
tip: `Found ${result.total} templates matching filters. Showing ${result.items.length}. Each includes AI-generated metadata.`
|
|
2749
|
+
};
|
|
2750
|
+
}
|
|
2751
|
+
getTaskDescription(task) {
|
|
2752
|
+
const descriptions = {
|
|
2753
|
+
'ai_automation': 'AI-powered workflows using OpenAI, LangChain, and other AI tools',
|
|
2754
|
+
'data_sync': 'Synchronize data between databases, spreadsheets, and APIs',
|
|
2755
|
+
'webhook_processing': 'Process incoming webhooks and trigger automated actions',
|
|
2756
|
+
'email_automation': 'Send, receive, and process emails automatically',
|
|
2757
|
+
'slack_integration': 'Integrate with Slack for notifications and bot interactions',
|
|
2758
|
+
'data_transformation': 'Transform, clean, and manipulate data',
|
|
2759
|
+
'file_processing': 'Handle file uploads, downloads, and transformations',
|
|
2760
|
+
'scheduling': 'Schedule recurring tasks and time-based automations',
|
|
2761
|
+
'api_integration': 'Connect to external APIs and web services',
|
|
2762
|
+
'database_operations': 'Query, insert, update, and manage database records'
|
|
2763
|
+
};
|
|
2764
|
+
return descriptions[task] || 'Workflow templates for this task';
|
|
2765
|
+
}
|
|
2766
|
+
async validateWorkflow(workflow, options) {
|
|
2767
|
+
await this.ensureInitialized();
|
|
2768
|
+
if (!this.repository)
|
|
2769
|
+
throw new Error('Repository not initialized');
|
|
2770
|
+
logger_1.logger.info('Workflow validation requested', {
|
|
2771
|
+
hasWorkflow: !!workflow,
|
|
2772
|
+
workflowType: typeof workflow,
|
|
2773
|
+
hasNodes: workflow?.nodes !== undefined,
|
|
2774
|
+
nodesType: workflow?.nodes ? typeof workflow.nodes : 'undefined',
|
|
2775
|
+
nodesIsArray: Array.isArray(workflow?.nodes),
|
|
2776
|
+
nodesCount: Array.isArray(workflow?.nodes) ? workflow.nodes.length : 0,
|
|
2777
|
+
hasConnections: workflow?.connections !== undefined,
|
|
2778
|
+
connectionsType: workflow?.connections ? typeof workflow.connections : 'undefined',
|
|
2779
|
+
options: options
|
|
2780
|
+
});
|
|
2781
|
+
if (!workflow || typeof workflow !== 'object') {
|
|
2782
|
+
return {
|
|
2783
|
+
valid: false,
|
|
2784
|
+
errors: [{
|
|
2785
|
+
node: 'workflow',
|
|
2786
|
+
message: 'Workflow must be an object with nodes and connections',
|
|
2787
|
+
details: 'Expected format: ' + (0, workflow_examples_1.getWorkflowExampleString)()
|
|
2788
|
+
}],
|
|
2789
|
+
summary: { errorCount: 1 }
|
|
2790
|
+
};
|
|
2791
|
+
}
|
|
2792
|
+
if (!workflow.nodes || !Array.isArray(workflow.nodes)) {
|
|
2793
|
+
return {
|
|
2794
|
+
valid: false,
|
|
2795
|
+
errors: [{
|
|
2796
|
+
node: 'workflow',
|
|
2797
|
+
message: 'Workflow must have a nodes array',
|
|
2798
|
+
details: 'Expected: workflow.nodes = [array of node objects]. ' + (0, workflow_examples_1.getWorkflowExampleString)()
|
|
2799
|
+
}],
|
|
2800
|
+
summary: { errorCount: 1 }
|
|
2801
|
+
};
|
|
2802
|
+
}
|
|
2803
|
+
if (!workflow.connections || typeof workflow.connections !== 'object') {
|
|
2804
|
+
return {
|
|
2805
|
+
valid: false,
|
|
2806
|
+
errors: [{
|
|
2807
|
+
node: 'workflow',
|
|
2808
|
+
message: 'Workflow must have a connections object',
|
|
2809
|
+
details: 'Expected: workflow.connections = {} (can be empty object). ' + (0, workflow_examples_1.getWorkflowExampleString)()
|
|
2810
|
+
}],
|
|
2811
|
+
summary: { errorCount: 1 }
|
|
2812
|
+
};
|
|
2813
|
+
}
|
|
2814
|
+
const validator = new workflow_validator_1.WorkflowValidator(this.repository, enhanced_config_validator_1.EnhancedConfigValidator);
|
|
2815
|
+
try {
|
|
2816
|
+
const result = await validator.validateWorkflow(workflow, options);
|
|
2817
|
+
const response = {
|
|
2818
|
+
valid: result.valid,
|
|
2819
|
+
summary: {
|
|
2820
|
+
totalNodes: result.statistics.totalNodes,
|
|
2821
|
+
enabledNodes: result.statistics.enabledNodes,
|
|
2822
|
+
triggerNodes: result.statistics.triggerNodes,
|
|
2823
|
+
validConnections: result.statistics.validConnections,
|
|
2824
|
+
invalidConnections: result.statistics.invalidConnections,
|
|
2825
|
+
expressionsValidated: result.statistics.expressionsValidated,
|
|
2826
|
+
errorCount: result.errors.length,
|
|
2827
|
+
warningCount: result.warnings.length
|
|
2828
|
+
},
|
|
2829
|
+
errors: result.errors.map(e => ({
|
|
2830
|
+
node: e.nodeName || 'workflow',
|
|
2831
|
+
message: e.message,
|
|
2832
|
+
details: e.details
|
|
2833
|
+
})),
|
|
2834
|
+
warnings: result.warnings.map(w => ({
|
|
2835
|
+
node: w.nodeName || 'workflow',
|
|
2836
|
+
message: w.message,
|
|
2837
|
+
details: w.details
|
|
2838
|
+
}))
|
|
2839
|
+
};
|
|
2840
|
+
if (result.suggestions.length > 0) {
|
|
2841
|
+
response.suggestions = result.suggestions;
|
|
2842
|
+
}
|
|
2843
|
+
if (!result.valid && result.errors.length > 0) {
|
|
2844
|
+
result.errors.forEach(error => {
|
|
2845
|
+
telemetry_1.telemetry.trackValidationDetails(error.nodeName || 'workflow', error.type || 'validation_error', {
|
|
2846
|
+
message: error.message,
|
|
2847
|
+
nodeCount: workflow.nodes?.length ?? 0,
|
|
2848
|
+
hasConnections: Object.keys(workflow.connections || {}).length > 0
|
|
2849
|
+
});
|
|
2850
|
+
});
|
|
2851
|
+
}
|
|
2852
|
+
if (result.valid) {
|
|
2853
|
+
telemetry_1.telemetry.trackWorkflowCreation(workflow, true);
|
|
2854
|
+
}
|
|
2855
|
+
return response;
|
|
2856
|
+
}
|
|
2857
|
+
catch (error) {
|
|
2858
|
+
logger_1.logger.error('Error validating workflow:', error);
|
|
2859
|
+
return {
|
|
2860
|
+
valid: false,
|
|
2861
|
+
error: error instanceof Error ? error.message : 'Unknown error validating workflow',
|
|
2862
|
+
tip: 'Ensure the workflow JSON includes nodes array and connections object'
|
|
2863
|
+
};
|
|
2864
|
+
}
|
|
2865
|
+
}
|
|
2866
|
+
async validateWorkflowConnections(workflow) {
|
|
2867
|
+
await this.ensureInitialized();
|
|
2868
|
+
if (!this.repository)
|
|
2869
|
+
throw new Error('Repository not initialized');
|
|
2870
|
+
const validator = new workflow_validator_1.WorkflowValidator(this.repository, enhanced_config_validator_1.EnhancedConfigValidator);
|
|
2871
|
+
try {
|
|
2872
|
+
const result = await validator.validateWorkflow(workflow, {
|
|
2873
|
+
validateNodes: false,
|
|
2874
|
+
validateConnections: true,
|
|
2875
|
+
validateExpressions: false
|
|
2876
|
+
});
|
|
2877
|
+
const response = {
|
|
2878
|
+
valid: result.errors.length === 0,
|
|
2879
|
+
statistics: {
|
|
2880
|
+
totalNodes: result.statistics.totalNodes,
|
|
2881
|
+
triggerNodes: result.statistics.triggerNodes,
|
|
2882
|
+
validConnections: result.statistics.validConnections,
|
|
2883
|
+
invalidConnections: result.statistics.invalidConnections
|
|
2884
|
+
}
|
|
2885
|
+
};
|
|
2886
|
+
const connectionErrors = result.errors.filter(e => e.message.includes('connection') ||
|
|
2887
|
+
e.message.includes('cycle') ||
|
|
2888
|
+
e.message.includes('orphaned'));
|
|
2889
|
+
const connectionWarnings = result.warnings.filter(w => w.message.includes('connection') ||
|
|
2890
|
+
w.message.includes('orphaned') ||
|
|
2891
|
+
w.message.includes('trigger'));
|
|
2892
|
+
if (connectionErrors.length > 0) {
|
|
2893
|
+
response.errors = connectionErrors.map(e => ({
|
|
2894
|
+
node: e.nodeName || 'workflow',
|
|
2895
|
+
message: e.message
|
|
2896
|
+
}));
|
|
2897
|
+
}
|
|
2898
|
+
if (connectionWarnings.length > 0) {
|
|
2899
|
+
response.warnings = connectionWarnings.map(w => ({
|
|
2900
|
+
node: w.nodeName || 'workflow',
|
|
2901
|
+
message: w.message
|
|
2902
|
+
}));
|
|
2903
|
+
}
|
|
2904
|
+
return response;
|
|
2905
|
+
}
|
|
2906
|
+
catch (error) {
|
|
2907
|
+
logger_1.logger.error('Error validating workflow connections:', error);
|
|
2908
|
+
return {
|
|
2909
|
+
valid: false,
|
|
2910
|
+
error: error instanceof Error ? error.message : 'Unknown error validating connections'
|
|
2911
|
+
};
|
|
2912
|
+
}
|
|
2913
|
+
}
|
|
2914
|
+
async validateWorkflowExpressions(workflow) {
|
|
2915
|
+
await this.ensureInitialized();
|
|
2916
|
+
if (!this.repository)
|
|
2917
|
+
throw new Error('Repository not initialized');
|
|
2918
|
+
const validator = new workflow_validator_1.WorkflowValidator(this.repository, enhanced_config_validator_1.EnhancedConfigValidator);
|
|
2919
|
+
try {
|
|
2920
|
+
const result = await validator.validateWorkflow(workflow, {
|
|
2921
|
+
validateNodes: false,
|
|
2922
|
+
validateConnections: false,
|
|
2923
|
+
validateExpressions: true
|
|
2924
|
+
});
|
|
2925
|
+
const response = {
|
|
2926
|
+
valid: result.errors.length === 0,
|
|
2927
|
+
statistics: {
|
|
2928
|
+
totalNodes: result.statistics.totalNodes,
|
|
2929
|
+
expressionsValidated: result.statistics.expressionsValidated
|
|
2930
|
+
}
|
|
2931
|
+
};
|
|
2932
|
+
const expressionErrors = result.errors.filter(e => e.message.includes('Expression') ||
|
|
2933
|
+
e.message.includes('$') ||
|
|
2934
|
+
e.message.includes('{{'));
|
|
2935
|
+
const expressionWarnings = result.warnings.filter(w => w.message.includes('Expression') ||
|
|
2936
|
+
w.message.includes('$') ||
|
|
2937
|
+
w.message.includes('{{'));
|
|
2938
|
+
if (expressionErrors.length > 0) {
|
|
2939
|
+
response.errors = expressionErrors.map(e => ({
|
|
2940
|
+
node: e.nodeName || 'workflow',
|
|
2941
|
+
message: e.message
|
|
2942
|
+
}));
|
|
2943
|
+
}
|
|
2944
|
+
if (expressionWarnings.length > 0) {
|
|
2945
|
+
response.warnings = expressionWarnings.map(w => ({
|
|
2946
|
+
node: w.nodeName || 'workflow',
|
|
2947
|
+
message: w.message
|
|
2948
|
+
}));
|
|
2949
|
+
}
|
|
2950
|
+
if (expressionErrors.length > 0 || expressionWarnings.length > 0) {
|
|
2951
|
+
response.tips = [
|
|
2952
|
+
'Use {{ }} to wrap expressions',
|
|
2953
|
+
'Reference data with $json.propertyName',
|
|
2954
|
+
'Reference other nodes with $node["Node Name"].json',
|
|
2955
|
+
'Use $input.item for input data in loops'
|
|
2956
|
+
];
|
|
2957
|
+
}
|
|
2958
|
+
return response;
|
|
2959
|
+
}
|
|
2960
|
+
catch (error) {
|
|
2961
|
+
logger_1.logger.error('Error validating workflow expressions:', error);
|
|
2962
|
+
return {
|
|
2963
|
+
valid: false,
|
|
2964
|
+
error: error instanceof Error ? error.message : 'Unknown error validating expressions'
|
|
2965
|
+
};
|
|
2966
|
+
}
|
|
2967
|
+
}
|
|
2968
|
+
async run() {
|
|
2969
|
+
await this.ensureInitialized();
|
|
2970
|
+
const transport = new stdio_js_1.StdioServerTransport();
|
|
2971
|
+
await this.server.connect(transport);
|
|
2972
|
+
if (!process.stdout.isTTY || process.env.IS_DOCKER) {
|
|
2973
|
+
const originalWrite = process.stdout.write.bind(process.stdout);
|
|
2974
|
+
process.stdout.write = function (chunk, encoding, callback) {
|
|
2975
|
+
const result = originalWrite(chunk, encoding, callback);
|
|
2976
|
+
process.stdout.emit('drain');
|
|
2977
|
+
return result;
|
|
2978
|
+
};
|
|
2979
|
+
}
|
|
2980
|
+
logger_1.logger.info('n8n Documentation MCP Server running on stdio transport');
|
|
2981
|
+
process.stdin.resume();
|
|
2982
|
+
}
|
|
2983
|
+
async shutdown() {
|
|
2984
|
+
logger_1.logger.info('Shutting down MCP server...');
|
|
2985
|
+
if (this.cache) {
|
|
2986
|
+
try {
|
|
2987
|
+
this.cache.destroy();
|
|
2988
|
+
logger_1.logger.info('Cache timers cleaned up');
|
|
2989
|
+
}
|
|
2990
|
+
catch (error) {
|
|
2991
|
+
logger_1.logger.error('Error cleaning up cache:', error);
|
|
2992
|
+
}
|
|
2993
|
+
}
|
|
2994
|
+
if (this.db) {
|
|
2995
|
+
try {
|
|
2996
|
+
await this.db.close();
|
|
2997
|
+
logger_1.logger.info('Database connection closed');
|
|
2998
|
+
}
|
|
2999
|
+
catch (error) {
|
|
3000
|
+
logger_1.logger.error('Error closing database:', error);
|
|
3001
|
+
}
|
|
3002
|
+
}
|
|
3003
|
+
}
|
|
3004
|
+
}
|
|
3005
|
+
exports.N8NDocumentationMCPServer = N8NDocumentationMCPServer;
|
|
3006
|
+
//# sourceMappingURL=server.js.map
|