#
tokens: 49302/50000 44/628 files (page 3/63)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 3 of 63. Use http://codebase.md/czlonkowski/n8n-mcp?lines=true&page={x} to view the full context.

# Directory Structure

```
├── _config.yml
├── .claude
│   └── agents
│       ├── code-reviewer.md
│       ├── context-manager.md
│       ├── debugger.md
│       ├── deployment-engineer.md
│       ├── mcp-backend-engineer.md
│       ├── n8n-mcp-tester.md
│       ├── technical-researcher.md
│       └── test-automator.md
├── .dockerignore
├── .env.docker
├── .env.example
├── .env.n8n.example
├── .env.test
├── .env.test.example
├── .github
│   ├── ABOUT.md
│   ├── BENCHMARK_THRESHOLDS.md
│   ├── FUNDING.yml
│   ├── gh-pages.yml
│   ├── secret_scanning.yml
│   └── workflows
│       ├── benchmark-pr.yml
│       ├── benchmark.yml
│       ├── docker-build-fast.yml
│       ├── docker-build-n8n.yml
│       ├── docker-build.yml
│       ├── release.yml
│       ├── test.yml
│       └── update-n8n-deps.yml
├── .gitignore
├── .npmignore
├── ATTRIBUTION.md
├── CHANGELOG.md
├── CLAUDE.md
├── codecov.yml
├── coverage.json
├── data
│   ├── .gitkeep
│   ├── nodes.db
│   ├── nodes.db-shm
│   ├── nodes.db-wal
│   └── templates.db
├── deploy
│   └── quick-deploy-n8n.sh
├── docker
│   ├── docker-entrypoint.sh
│   ├── n8n-mcp
│   ├── parse-config.js
│   └── README.md
├── docker-compose.buildkit.yml
├── docker-compose.extract.yml
├── docker-compose.n8n.yml
├── docker-compose.override.yml.example
├── docker-compose.test-n8n.yml
├── docker-compose.yml
├── Dockerfile
├── Dockerfile.railway
├── Dockerfile.test
├── docs
│   ├── AUTOMATED_RELEASES.md
│   ├── BENCHMARKS.md
│   ├── bugfix-onSessionCreated-event.md
│   ├── CHANGELOG.md
│   ├── CLAUDE_CODE_SETUP.md
│   ├── CLAUDE_INTERVIEW.md
│   ├── CODECOV_SETUP.md
│   ├── CODEX_SETUP.md
│   ├── CURSOR_SETUP.md
│   ├── DEPENDENCY_UPDATES.md
│   ├── DOCKER_README.md
│   ├── DOCKER_TROUBLESHOOTING.md
│   ├── FINAL_AI_VALIDATION_SPEC.md
│   ├── FLEXIBLE_INSTANCE_CONFIGURATION.md
│   ├── HTTP_DEPLOYMENT.md
│   ├── img
│   │   ├── cc_command.png
│   │   ├── cc_connected.png
│   │   ├── codex_connected.png
│   │   ├── cursor_tut.png
│   │   ├── Railway_api.png
│   │   ├── Railway_server_address.png
│   │   ├── vsc_ghcp_chat_agent_mode.png
│   │   ├── vsc_ghcp_chat_instruction_files.png
│   │   ├── vsc_ghcp_chat_thinking_tool.png
│   │   └── windsurf_tut.png
│   ├── INSTALLATION.md
│   ├── LIBRARY_USAGE.md
│   ├── local
│   │   ├── DEEP_DIVE_ANALYSIS_2025-10-02.md
│   │   ├── DEEP_DIVE_ANALYSIS_README.md
│   │   ├── Deep_dive_p1_p2.md
│   │   ├── integration-testing-plan.md
│   │   ├── integration-tests-phase1-summary.md
│   │   ├── N8N_AI_WORKFLOW_BUILDER_ANALYSIS.md
│   │   ├── P0_IMPLEMENTATION_PLAN.md
│   │   └── TEMPLATE_MINING_ANALYSIS.md
│   ├── MCP_ESSENTIALS_README.md
│   ├── MCP_QUICK_START_GUIDE.md
│   ├── N8N_DEPLOYMENT.md
│   ├── RAILWAY_DEPLOYMENT.md
│   ├── README_CLAUDE_SETUP.md
│   ├── README.md
│   ├── tools-documentation-usage.md
│   ├── VS_CODE_PROJECT_SETUP.md
│   ├── WINDSURF_SETUP.md
│   └── workflow-diff-examples.md
├── examples
│   └── enhanced-documentation-demo.js
├── fetch_log.txt
├── IMPLEMENTATION_GUIDE.md
├── LICENSE
├── MEMORY_N8N_UPDATE.md
├── MEMORY_TEMPLATE_UPDATE.md
├── monitor_fetch.sh
├── MVP_DEPLOYMENT_PLAN.md
├── N8N_HTTP_STREAMABLE_SETUP.md
├── n8n-nodes.db
├── P0-R3-TEST-PLAN.md
├── package-lock.json
├── package.json
├── package.runtime.json
├── PRIVACY.md
├── railway.json
├── README.md
├── renovate.json
├── scripts
│   ├── analyze-optimization.sh
│   ├── audit-schema-coverage.ts
│   ├── build-optimized.sh
│   ├── compare-benchmarks.js
│   ├── demo-optimization.sh
│   ├── deploy-http.sh
│   ├── deploy-to-vm.sh
│   ├── export-webhook-workflows.ts
│   ├── extract-changelog.js
│   ├── extract-from-docker.js
│   ├── extract-nodes-docker.sh
│   ├── extract-nodes-simple.sh
│   ├── format-benchmark-results.js
│   ├── generate-benchmark-stub.js
│   ├── generate-detailed-reports.js
│   ├── generate-test-summary.js
│   ├── http-bridge.js
│   ├── mcp-http-client.js
│   ├── migrate-nodes-fts.ts
│   ├── migrate-tool-docs.ts
│   ├── n8n-docs-mcp.service
│   ├── nginx-n8n-mcp.conf
│   ├── prebuild-fts5.ts
│   ├── prepare-release.js
│   ├── publish-npm-quick.sh
│   ├── publish-npm.sh
│   ├── quick-test.ts
│   ├── run-benchmarks-ci.js
│   ├── sync-runtime-version.js
│   ├── test-ai-validation-debug.ts
│   ├── test-code-node-enhancements.ts
│   ├── test-code-node-fixes.ts
│   ├── test-docker-config.sh
│   ├── test-docker-fingerprint.ts
│   ├── test-docker-optimization.sh
│   ├── test-docker.sh
│   ├── test-empty-connection-validation.ts
│   ├── test-error-message-tracking.ts
│   ├── test-error-output-validation.ts
│   ├── test-error-validation.js
│   ├── test-essentials.ts
│   ├── test-expression-code-validation.ts
│   ├── test-expression-format-validation.js
│   ├── test-fts5-search.ts
│   ├── test-fuzzy-fix.ts
│   ├── test-fuzzy-simple.ts
│   ├── test-helpers-validation.ts
│   ├── test-http-search.ts
│   ├── test-http.sh
│   ├── test-jmespath-validation.ts
│   ├── test-multi-tenant-simple.ts
│   ├── test-multi-tenant.ts
│   ├── test-n8n-integration.sh
│   ├── test-node-info.js
│   ├── test-node-type-validation.ts
│   ├── test-nodes-base-prefix.ts
│   ├── test-operation-validation.ts
│   ├── test-optimized-docker.sh
│   ├── test-release-automation.js
│   ├── test-search-improvements.ts
│   ├── test-security.ts
│   ├── test-single-session.sh
│   ├── test-sqljs-triggers.ts
│   ├── test-telemetry-debug.ts
│   ├── test-telemetry-direct.ts
│   ├── test-telemetry-env.ts
│   ├── test-telemetry-integration.ts
│   ├── test-telemetry-no-select.ts
│   ├── test-telemetry-security.ts
│   ├── test-telemetry-simple.ts
│   ├── test-typeversion-validation.ts
│   ├── test-url-configuration.ts
│   ├── test-user-id-persistence.ts
│   ├── test-webhook-validation.ts
│   ├── test-workflow-insert.ts
│   ├── test-workflow-sanitizer.ts
│   ├── test-workflow-tracking-debug.ts
│   ├── update-and-publish-prep.sh
│   ├── update-n8n-deps.js
│   ├── update-readme-version.js
│   ├── vitest-benchmark-json-reporter.js
│   └── vitest-benchmark-reporter.ts
├── SECURITY.md
├── src
│   ├── config
│   │   └── n8n-api.ts
│   ├── data
│   │   └── canonical-ai-tool-examples.json
│   ├── database
│   │   ├── database-adapter.ts
│   │   ├── migrations
│   │   │   └── add-template-node-configs.sql
│   │   ├── node-repository.ts
│   │   ├── nodes.db
│   │   ├── schema-optimized.sql
│   │   └── schema.sql
│   ├── errors
│   │   └── validation-service-error.ts
│   ├── http-server-single-session.ts
│   ├── http-server.ts
│   ├── index.ts
│   ├── loaders
│   │   └── node-loader.ts
│   ├── mappers
│   │   └── docs-mapper.ts
│   ├── mcp
│   │   ├── handlers-n8n-manager.ts
│   │   ├── handlers-workflow-diff.ts
│   │   ├── index.ts
│   │   ├── server.ts
│   │   ├── stdio-wrapper.ts
│   │   ├── tool-docs
│   │   │   ├── configuration
│   │   │   │   ├── get-node-as-tool-info.ts
│   │   │   │   ├── get-node-documentation.ts
│   │   │   │   ├── get-node-essentials.ts
│   │   │   │   ├── get-node-info.ts
│   │   │   │   ├── get-property-dependencies.ts
│   │   │   │   ├── index.ts
│   │   │   │   └── search-node-properties.ts
│   │   │   ├── discovery
│   │   │   │   ├── get-database-statistics.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── list-ai-tools.ts
│   │   │   │   ├── list-nodes.ts
│   │   │   │   └── search-nodes.ts
│   │   │   ├── guides
│   │   │   │   ├── ai-agents-guide.ts
│   │   │   │   └── index.ts
│   │   │   ├── index.ts
│   │   │   ├── system
│   │   │   │   ├── index.ts
│   │   │   │   ├── n8n-diagnostic.ts
│   │   │   │   ├── n8n-health-check.ts
│   │   │   │   ├── n8n-list-available-tools.ts
│   │   │   │   └── tools-documentation.ts
│   │   │   ├── templates
│   │   │   │   ├── get-template.ts
│   │   │   │   ├── get-templates-for-task.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── list-node-templates.ts
│   │   │   │   ├── list-tasks.ts
│   │   │   │   ├── search-templates-by-metadata.ts
│   │   │   │   └── search-templates.ts
│   │   │   ├── types.ts
│   │   │   ├── validation
│   │   │   │   ├── index.ts
│   │   │   │   ├── validate-node-minimal.ts
│   │   │   │   ├── validate-node-operation.ts
│   │   │   │   ├── validate-workflow-connections.ts
│   │   │   │   ├── validate-workflow-expressions.ts
│   │   │   │   └── validate-workflow.ts
│   │   │   └── workflow_management
│   │   │       ├── index.ts
│   │   │       ├── n8n-autofix-workflow.ts
│   │   │       ├── n8n-create-workflow.ts
│   │   │       ├── n8n-delete-execution.ts
│   │   │       ├── n8n-delete-workflow.ts
│   │   │       ├── n8n-get-execution.ts
│   │   │       ├── n8n-get-workflow-details.ts
│   │   │       ├── n8n-get-workflow-minimal.ts
│   │   │       ├── n8n-get-workflow-structure.ts
│   │   │       ├── n8n-get-workflow.ts
│   │   │       ├── n8n-list-executions.ts
│   │   │       ├── n8n-list-workflows.ts
│   │   │       ├── n8n-trigger-webhook-workflow.ts
│   │   │       ├── n8n-update-full-workflow.ts
│   │   │       ├── n8n-update-partial-workflow.ts
│   │   │       └── n8n-validate-workflow.ts
│   │   ├── tools-documentation.ts
│   │   ├── tools-n8n-friendly.ts
│   │   ├── tools-n8n-manager.ts
│   │   ├── tools.ts
│   │   └── workflow-examples.ts
│   ├── mcp-engine.ts
│   ├── mcp-tools-engine.ts
│   ├── n8n
│   │   ├── MCPApi.credentials.ts
│   │   └── MCPNode.node.ts
│   ├── parsers
│   │   ├── node-parser.ts
│   │   ├── property-extractor.ts
│   │   └── simple-parser.ts
│   ├── scripts
│   │   ├── debug-http-search.ts
│   │   ├── extract-from-docker.ts
│   │   ├── fetch-templates-robust.ts
│   │   ├── fetch-templates.ts
│   │   ├── rebuild-database.ts
│   │   ├── rebuild-optimized.ts
│   │   ├── rebuild.ts
│   │   ├── sanitize-templates.ts
│   │   ├── seed-canonical-ai-examples.ts
│   │   ├── test-autofix-documentation.ts
│   │   ├── test-autofix-workflow.ts
│   │   ├── test-execution-filtering.ts
│   │   ├── test-node-suggestions.ts
│   │   ├── test-protocol-negotiation.ts
│   │   ├── test-summary.ts
│   │   ├── test-webhook-autofix.ts
│   │   ├── validate.ts
│   │   └── validation-summary.ts
│   ├── services
│   │   ├── ai-node-validator.ts
│   │   ├── ai-tool-validators.ts
│   │   ├── confidence-scorer.ts
│   │   ├── config-validator.ts
│   │   ├── enhanced-config-validator.ts
│   │   ├── example-generator.ts
│   │   ├── execution-processor.ts
│   │   ├── expression-format-validator.ts
│   │   ├── expression-validator.ts
│   │   ├── n8n-api-client.ts
│   │   ├── n8n-validation.ts
│   │   ├── node-documentation-service.ts
│   │   ├── node-similarity-service.ts
│   │   ├── node-specific-validators.ts
│   │   ├── operation-similarity-service.ts
│   │   ├── property-dependencies.ts
│   │   ├── property-filter.ts
│   │   ├── resource-similarity-service.ts
│   │   ├── sqlite-storage-service.ts
│   │   ├── task-templates.ts
│   │   ├── universal-expression-validator.ts
│   │   ├── workflow-auto-fixer.ts
│   │   ├── workflow-diff-engine.ts
│   │   └── workflow-validator.ts
│   ├── telemetry
│   │   ├── batch-processor.ts
│   │   ├── config-manager.ts
│   │   ├── early-error-logger.ts
│   │   ├── error-sanitization-utils.ts
│   │   ├── error-sanitizer.ts
│   │   ├── event-tracker.ts
│   │   ├── event-validator.ts
│   │   ├── index.ts
│   │   ├── performance-monitor.ts
│   │   ├── rate-limiter.ts
│   │   ├── startup-checkpoints.ts
│   │   ├── telemetry-error.ts
│   │   ├── telemetry-manager.ts
│   │   ├── telemetry-types.ts
│   │   └── workflow-sanitizer.ts
│   ├── templates
│   │   ├── batch-processor.ts
│   │   ├── metadata-generator.ts
│   │   ├── README.md
│   │   ├── template-fetcher.ts
│   │   ├── template-repository.ts
│   │   └── template-service.ts
│   ├── types
│   │   ├── index.ts
│   │   ├── instance-context.ts
│   │   ├── n8n-api.ts
│   │   ├── node-types.ts
│   │   ├── session-restoration.ts
│   │   └── workflow-diff.ts
│   └── utils
│       ├── auth.ts
│       ├── bridge.ts
│       ├── cache-utils.ts
│       ├── console-manager.ts
│       ├── documentation-fetcher.ts
│       ├── enhanced-documentation-fetcher.ts
│       ├── error-handler.ts
│       ├── example-generator.ts
│       ├── fixed-collection-validator.ts
│       ├── logger.ts
│       ├── mcp-client.ts
│       ├── n8n-errors.ts
│       ├── node-source-extractor.ts
│       ├── node-type-normalizer.ts
│       ├── node-type-utils.ts
│       ├── node-utils.ts
│       ├── npm-version-checker.ts
│       ├── protocol-version.ts
│       ├── simple-cache.ts
│       ├── ssrf-protection.ts
│       ├── template-node-resolver.ts
│       ├── template-sanitizer.ts
│       ├── url-detector.ts
│       ├── validation-schemas.ts
│       └── version.ts
├── supabase-telemetry-aggregation.sql
├── TELEMETRY_PRUNING_GUIDE.md
├── telemetry-pruning-analysis.md
├── test-output.txt
├── test-reinit-fix.sh
├── tests
│   ├── __snapshots__
│   │   └── .gitkeep
│   ├── auth.test.ts
│   ├── benchmarks
│   │   ├── database-queries.bench.ts
│   │   ├── index.ts
│   │   ├── mcp-tools.bench.ts
│   │   ├── mcp-tools.bench.ts.disabled
│   │   ├── mcp-tools.bench.ts.skip
│   │   ├── node-loading.bench.ts.disabled
│   │   ├── README.md
│   │   ├── search-operations.bench.ts.disabled
│   │   └── validation-performance.bench.ts.disabled
│   ├── bridge.test.ts
│   ├── comprehensive-extraction-test.js
│   ├── data
│   │   └── .gitkeep
│   ├── debug-slack-doc.js
│   ├── demo-enhanced-documentation.js
│   ├── docker-tests-README.md
│   ├── error-handler.test.ts
│   ├── examples
│   │   └── using-database-utils.test.ts
│   ├── extracted-nodes-db
│   │   ├── database-import.json
│   │   ├── extraction-report.json
│   │   ├── insert-nodes.sql
│   │   ├── n8n-nodes-base__Airtable.json
│   │   ├── n8n-nodes-base__Discord.json
│   │   ├── n8n-nodes-base__Function.json
│   │   ├── n8n-nodes-base__HttpRequest.json
│   │   ├── n8n-nodes-base__If.json
│   │   ├── n8n-nodes-base__Slack.json
│   │   ├── n8n-nodes-base__SplitInBatches.json
│   │   └── n8n-nodes-base__Webhook.json
│   ├── factories
│   │   ├── node-factory.ts
│   │   └── property-definition-factory.ts
│   ├── fixtures
│   │   ├── .gitkeep
│   │   ├── database
│   │   │   └── test-nodes.json
│   │   ├── factories
│   │   │   ├── node.factory.ts
│   │   │   └── parser-node.factory.ts
│   │   └── template-configs.ts
│   ├── helpers
│   │   └── env-helpers.ts
│   ├── http-server-auth.test.ts
│   ├── integration
│   │   ├── ai-validation
│   │   │   ├── ai-agent-validation.test.ts
│   │   │   ├── ai-tool-validation.test.ts
│   │   │   ├── chat-trigger-validation.test.ts
│   │   │   ├── e2e-validation.test.ts
│   │   │   ├── helpers.ts
│   │   │   ├── llm-chain-validation.test.ts
│   │   │   ├── README.md
│   │   │   └── TEST_REPORT.md
│   │   ├── ci
│   │   │   └── database-population.test.ts
│   │   ├── database
│   │   │   ├── connection-management.test.ts
│   │   │   ├── empty-database.test.ts
│   │   │   ├── fts5-search.test.ts
│   │   │   ├── node-fts5-search.test.ts
│   │   │   ├── node-repository.test.ts
│   │   │   ├── performance.test.ts
│   │   │   ├── template-node-configs.test.ts
│   │   │   ├── template-repository.test.ts
│   │   │   ├── test-utils.ts
│   │   │   └── transactions.test.ts
│   │   ├── database-integration.test.ts
│   │   ├── docker
│   │   │   ├── docker-config.test.ts
│   │   │   ├── docker-entrypoint.test.ts
│   │   │   └── test-helpers.ts
│   │   ├── flexible-instance-config.test.ts
│   │   ├── mcp
│   │   │   └── template-examples-e2e.test.ts
│   │   ├── mcp-protocol
│   │   │   ├── basic-connection.test.ts
│   │   │   ├── error-handling.test.ts
│   │   │   ├── performance.test.ts
│   │   │   ├── protocol-compliance.test.ts
│   │   │   ├── README.md
│   │   │   ├── session-management.test.ts
│   │   │   ├── test-helpers.ts
│   │   │   ├── tool-invocation.test.ts
│   │   │   └── workflow-error-validation.test.ts
│   │   ├── msw-setup.test.ts
│   │   ├── n8n-api
│   │   │   ├── executions
│   │   │   │   ├── delete-execution.test.ts
│   │   │   │   ├── get-execution.test.ts
│   │   │   │   ├── list-executions.test.ts
│   │   │   │   └── trigger-webhook.test.ts
│   │   │   ├── scripts
│   │   │   │   └── cleanup-orphans.ts
│   │   │   ├── system
│   │   │   │   ├── diagnostic.test.ts
│   │   │   │   ├── health-check.test.ts
│   │   │   │   └── list-tools.test.ts
│   │   │   ├── test-connection.ts
│   │   │   ├── types
│   │   │   │   └── mcp-responses.ts
│   │   │   ├── utils
│   │   │   │   ├── cleanup-helpers.ts
│   │   │   │   ├── credentials.ts
│   │   │   │   ├── factories.ts
│   │   │   │   ├── fixtures.ts
│   │   │   │   ├── mcp-context.ts
│   │   │   │   ├── n8n-client.ts
│   │   │   │   ├── node-repository.ts
│   │   │   │   ├── response-types.ts
│   │   │   │   ├── test-context.ts
│   │   │   │   └── webhook-workflows.ts
│   │   │   └── workflows
│   │   │       ├── autofix-workflow.test.ts
│   │   │       ├── create-workflow.test.ts
│   │   │       ├── delete-workflow.test.ts
│   │   │       ├── get-workflow-details.test.ts
│   │   │       ├── get-workflow-minimal.test.ts
│   │   │       ├── get-workflow-structure.test.ts
│   │   │       ├── get-workflow.test.ts
│   │   │       ├── list-workflows.test.ts
│   │   │       ├── smart-parameters.test.ts
│   │   │       ├── update-partial-workflow.test.ts
│   │   │       ├── update-workflow.test.ts
│   │   │       └── validate-workflow.test.ts
│   │   ├── security
│   │   │   ├── command-injection-prevention.test.ts
│   │   │   └── rate-limiting.test.ts
│   │   ├── session
│   │   │   └── test-onSessionCreated-event.ts
│   │   ├── session-lifecycle-retry.test.ts
│   │   ├── session-persistence.test.ts
│   │   ├── setup
│   │   │   ├── integration-setup.ts
│   │   │   └── msw-test-server.ts
│   │   ├── telemetry
│   │   │   ├── docker-user-id-stability.test.ts
│   │   │   └── mcp-telemetry.test.ts
│   │   ├── templates
│   │   │   └── metadata-operations.test.ts
│   │   └── workflow-creation-node-type-format.test.ts
│   ├── logger.test.ts
│   ├── MOCKING_STRATEGY.md
│   ├── mocks
│   │   ├── n8n-api
│   │   │   ├── data
│   │   │   │   ├── credentials.ts
│   │   │   │   ├── executions.ts
│   │   │   │   └── workflows.ts
│   │   │   ├── handlers.ts
│   │   │   └── index.ts
│   │   └── README.md
│   ├── node-storage-export.json
│   ├── setup
│   │   ├── global-setup.ts
│   │   ├── msw-setup.ts
│   │   ├── TEST_ENV_DOCUMENTATION.md
│   │   └── test-env.ts
│   ├── test-database-extraction.js
│   ├── test-direct-extraction.js
│   ├── test-enhanced-documentation.js
│   ├── test-enhanced-integration.js
│   ├── test-mcp-extraction.js
│   ├── test-mcp-server-extraction.js
│   ├── test-mcp-tools-integration.js
│   ├── test-node-documentation-service.js
│   ├── test-node-list.js
│   ├── test-package-info.js
│   ├── test-parsing-operations.js
│   ├── test-slack-node-complete.js
│   ├── test-small-rebuild.js
│   ├── test-sqlite-search.js
│   ├── test-storage-system.js
│   ├── unit
│   │   ├── __mocks__
│   │   │   ├── n8n-nodes-base.test.ts
│   │   │   ├── n8n-nodes-base.ts
│   │   │   └── README.md
│   │   ├── database
│   │   │   ├── __mocks__
│   │   │   │   └── better-sqlite3.ts
│   │   │   ├── database-adapter-unit.test.ts
│   │   │   ├── node-repository-core.test.ts
│   │   │   ├── node-repository-operations.test.ts
│   │   │   ├── node-repository-outputs.test.ts
│   │   │   ├── README.md
│   │   │   └── template-repository-core.test.ts
│   │   ├── docker
│   │   │   ├── config-security.test.ts
│   │   │   ├── edge-cases.test.ts
│   │   │   ├── parse-config.test.ts
│   │   │   └── serve-command.test.ts
│   │   ├── errors
│   │   │   └── validation-service-error.test.ts
│   │   ├── examples
│   │   │   └── using-n8n-nodes-base-mock.test.ts
│   │   ├── flexible-instance-security-advanced.test.ts
│   │   ├── flexible-instance-security.test.ts
│   │   ├── http-server
│   │   │   └── multi-tenant-support.test.ts
│   │   ├── http-server-n8n-mode.test.ts
│   │   ├── http-server-n8n-reinit.test.ts
│   │   ├── http-server-session-management.test.ts
│   │   ├── loaders
│   │   │   └── node-loader.test.ts
│   │   ├── mappers
│   │   │   └── docs-mapper.test.ts
│   │   ├── mcp
│   │   │   ├── get-node-essentials-examples.test.ts
│   │   │   ├── handlers-n8n-manager-simple.test.ts
│   │   │   ├── handlers-n8n-manager.test.ts
│   │   │   ├── handlers-workflow-diff.test.ts
│   │   │   ├── lru-cache-behavior.test.ts
│   │   │   ├── multi-tenant-tool-listing.test.ts.disabled
│   │   │   ├── parameter-validation.test.ts
│   │   │   ├── search-nodes-examples.test.ts
│   │   │   ├── tools-documentation.test.ts
│   │   │   └── tools.test.ts
│   │   ├── monitoring
│   │   │   └── cache-metrics.test.ts
│   │   ├── MULTI_TENANT_TEST_COVERAGE.md
│   │   ├── multi-tenant-integration.test.ts
│   │   ├── parsers
│   │   │   ├── node-parser-outputs.test.ts
│   │   │   ├── node-parser.test.ts
│   │   │   ├── property-extractor.test.ts
│   │   │   └── simple-parser.test.ts
│   │   ├── scripts
│   │   │   └── fetch-templates-extraction.test.ts
│   │   ├── services
│   │   │   ├── ai-node-validator.test.ts
│   │   │   ├── ai-tool-validators.test.ts
│   │   │   ├── confidence-scorer.test.ts
│   │   │   ├── config-validator-basic.test.ts
│   │   │   ├── config-validator-edge-cases.test.ts
│   │   │   ├── config-validator-node-specific.test.ts
│   │   │   ├── config-validator-security.test.ts
│   │   │   ├── debug-validator.test.ts
│   │   │   ├── enhanced-config-validator-integration.test.ts
│   │   │   ├── enhanced-config-validator-operations.test.ts
│   │   │   ├── enhanced-config-validator.test.ts
│   │   │   ├── example-generator.test.ts
│   │   │   ├── execution-processor.test.ts
│   │   │   ├── expression-format-validator.test.ts
│   │   │   ├── expression-validator-edge-cases.test.ts
│   │   │   ├── expression-validator.test.ts
│   │   │   ├── fixed-collection-validation.test.ts
│   │   │   ├── loop-output-edge-cases.test.ts
│   │   │   ├── n8n-api-client.test.ts
│   │   │   ├── n8n-validation.test.ts
│   │   │   ├── node-similarity-service.test.ts
│   │   │   ├── node-specific-validators.test.ts
│   │   │   ├── operation-similarity-service-comprehensive.test.ts
│   │   │   ├── operation-similarity-service.test.ts
│   │   │   ├── property-dependencies.test.ts
│   │   │   ├── property-filter-edge-cases.test.ts
│   │   │   ├── property-filter.test.ts
│   │   │   ├── resource-similarity-service-comprehensive.test.ts
│   │   │   ├── resource-similarity-service.test.ts
│   │   │   ├── task-templates.test.ts
│   │   │   ├── template-service.test.ts
│   │   │   ├── universal-expression-validator.test.ts
│   │   │   ├── validation-fixes.test.ts
│   │   │   ├── workflow-auto-fixer.test.ts
│   │   │   ├── workflow-diff-engine.test.ts
│   │   │   ├── workflow-fixed-collection-validation.test.ts
│   │   │   ├── workflow-validator-comprehensive.test.ts
│   │   │   ├── workflow-validator-edge-cases.test.ts
│   │   │   ├── workflow-validator-error-outputs.test.ts
│   │   │   ├── workflow-validator-expression-format.test.ts
│   │   │   ├── workflow-validator-loops-simple.test.ts
│   │   │   ├── workflow-validator-loops.test.ts
│   │   │   ├── workflow-validator-mocks.test.ts
│   │   │   ├── workflow-validator-performance.test.ts
│   │   │   ├── workflow-validator-with-mocks.test.ts
│   │   │   └── workflow-validator.test.ts
│   │   ├── session-lifecycle-events.test.ts
│   │   ├── session-management-api.test.ts
│   │   ├── session-restoration-retry.test.ts
│   │   ├── session-restoration.test.ts
│   │   ├── telemetry
│   │   │   ├── batch-processor.test.ts
│   │   │   ├── config-manager.test.ts
│   │   │   ├── event-tracker.test.ts
│   │   │   ├── event-validator.test.ts
│   │   │   ├── rate-limiter.test.ts
│   │   │   ├── telemetry-error.test.ts
│   │   │   ├── telemetry-manager.test.ts
│   │   │   ├── v2.18.3-fixes-verification.test.ts
│   │   │   └── workflow-sanitizer.test.ts
│   │   ├── templates
│   │   │   ├── batch-processor.test.ts
│   │   │   ├── metadata-generator.test.ts
│   │   │   ├── template-repository-metadata.test.ts
│   │   │   └── template-repository-security.test.ts
│   │   ├── test-env-example.test.ts
│   │   ├── test-infrastructure.test.ts
│   │   ├── types
│   │   │   ├── instance-context-coverage.test.ts
│   │   │   └── instance-context-multi-tenant.test.ts
│   │   ├── utils
│   │   │   ├── auth-timing-safe.test.ts
│   │   │   ├── cache-utils.test.ts
│   │   │   ├── console-manager.test.ts
│   │   │   ├── database-utils.test.ts
│   │   │   ├── fixed-collection-validator.test.ts
│   │   │   ├── n8n-errors.test.ts
│   │   │   ├── node-type-normalizer.test.ts
│   │   │   ├── node-type-utils.test.ts
│   │   │   ├── node-utils.test.ts
│   │   │   ├── simple-cache-memory-leak-fix.test.ts
│   │   │   ├── ssrf-protection.test.ts
│   │   │   └── template-node-resolver.test.ts
│   │   └── validation-fixes.test.ts
│   └── utils
│       ├── assertions.ts
│       ├── builders
│       │   └── workflow.builder.ts
│       ├── data-generators.ts
│       ├── database-utils.ts
│       ├── README.md
│       └── test-helpers.ts
├── thumbnail.png
├── tsconfig.build.json
├── tsconfig.json
├── types
│   ├── mcp.d.ts
│   └── test-env.d.ts
├── verify-telemetry-fix.js
├── versioned-nodes.md
├── vitest.config.benchmark.ts
├── vitest.config.integration.ts
└── vitest.config.ts
```

# Files

--------------------------------------------------------------------------------
/scripts/test-helpers-validation.ts:
--------------------------------------------------------------------------------

```typescript
 1 | #!/usr/bin/env npx tsx
 2 | 
 3 | import { EnhancedConfigValidator } from '../src/services/enhanced-config-validator.js';
 4 | 
 5 | console.log('🧪 Testing $helpers Validation\n');
 6 | 
 7 | const testCases = [
 8 |   {
 9 |     name: 'Incorrect $helpers.getWorkflowStaticData',
10 |     config: {
11 |       language: 'javaScript',
12 |       jsCode: `const data = $helpers.getWorkflowStaticData('global');
13 | data.counter = 1;
14 | return [{json: {counter: data.counter}}];`
15 |     }
16 |   },
17 |   {
18 |     name: 'Correct $getWorkflowStaticData',
19 |     config: {
20 |       language: 'javaScript',
21 |       jsCode: `const data = $getWorkflowStaticData('global');
22 | data.counter = 1;
23 | return [{json: {counter: data.counter}}];`
24 |     }
25 |   },
26 |   {
27 |     name: '$helpers without check',
28 |     config: {
29 |       language: 'javaScript',
30 |       jsCode: `const response = await $helpers.httpRequest({
31 |   method: 'GET',
32 |   url: 'https://api.example.com'
33 | });
34 | return [{json: response}];`
35 |     }
36 |   },
37 |   {
38 |     name: '$helpers with proper check',
39 |     config: {
40 |       language: 'javaScript',
41 |       jsCode: `if (typeof $helpers !== 'undefined' && $helpers.httpRequest) {
42 |   const response = await $helpers.httpRequest({
43 |     method: 'GET',
44 |     url: 'https://api.example.com'
45 |   });
46 |   return [{json: response}];
47 | }
48 | return [{json: {error: 'HTTP not available'}}];`
49 |     }
50 |   },
51 |   {
52 |     name: 'Crypto without require',
53 |     config: {
54 |       language: 'javaScript',
55 |       jsCode: `const token = crypto.randomBytes(32).toString('hex');
56 | return [{json: {token}}];`
57 |     }
58 |   },
59 |   {
60 |     name: 'Crypto with require',
61 |     config: {
62 |       language: 'javaScript',
63 |       jsCode: `const crypto = require('crypto');
64 | const token = crypto.randomBytes(32).toString('hex');
65 | return [{json: {token}}];`
66 |     }
67 |   }
68 | ];
69 | 
70 | for (const test of testCases) {
71 |   console.log(`Test: ${test.name}`);
72 |   const result = EnhancedConfigValidator.validateWithMode(
73 |     'nodes-base.code',
74 |     test.config,
75 |     [
76 |       { name: 'language', type: 'options', options: ['javaScript', 'python'] },
77 |       { name: 'jsCode', type: 'string' }
78 |     ],
79 |     'operation',
80 |     'ai-friendly'
81 |   );
82 |   
83 |   console.log(`  Valid: ${result.valid}`);
84 |   if (result.errors.length > 0) {
85 |     console.log(`  Errors: ${result.errors.map(e => e.message).join(', ')}`);
86 |   }
87 |   if (result.warnings.length > 0) {
88 |     console.log(`  Warnings: ${result.warnings.map(w => w.message).join(', ')}`);
89 |   }
90 |   console.log();
91 | }
92 | 
93 | console.log('✅ $helpers validation tests completed!');
```

--------------------------------------------------------------------------------
/scripts/nginx-n8n-mcp.conf:
--------------------------------------------------------------------------------

```
 1 | server {
 2 |     listen 80;
 3 |     server_name n8ndocumentation.aiservices.pl;
 4 | 
 5 |     # Redirect HTTP to HTTPS
 6 |     location / {
 7 |         return 301 https://$server_name$request_uri;
 8 |     }
 9 | }
10 | 
11 | server {
12 |     listen 443 ssl http2;
13 |     server_name n8ndocumentation.aiservices.pl;
14 | 
15 |     # SSL configuration (managed by Certbot)
16 |     ssl_certificate /etc/letsencrypt/live/n8ndocumentation.aiservices.pl/fullchain.pem;
17 |     ssl_certificate_key /etc/letsencrypt/live/n8ndocumentation.aiservices.pl/privkey.pem;
18 |     
19 |     # SSL security settings
20 |     ssl_protocols TLSv1.2 TLSv1.3;
21 |     ssl_ciphers HIGH:!aNULL:!MD5;
22 |     ssl_prefer_server_ciphers on;
23 |     
24 |     # Security headers
25 |     add_header X-Content-Type-Options nosniff;
26 |     add_header X-Frame-Options DENY;
27 |     add_header X-XSS-Protection "1; mode=block";
28 |     add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
29 |     
30 |     # Logging
31 |     access_log /var/log/nginx/n8n-mcp-access.log;
32 |     error_log /var/log/nginx/n8n-mcp-error.log;
33 |     
34 |     # Proxy settings
35 |     location / {
36 |         proxy_pass http://localhost:3000;
37 |         proxy_http_version 1.1;
38 |         proxy_set_header Upgrade $http_upgrade;
39 |         proxy_set_header Connection 'upgrade';
40 |         proxy_set_header Host $host;
41 |         proxy_set_header X-Real-IP $remote_addr;
42 |         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
43 |         proxy_set_header X-Forwarded-Proto $scheme;
44 |         proxy_cache_bypass $http_upgrade;
45 |         
46 |         # Timeouts for MCP operations
47 |         proxy_connect_timeout 60s;
48 |         proxy_send_timeout 60s;
49 |         proxy_read_timeout 60s;
50 |         
51 |         # Increase buffer sizes for large responses
52 |         proxy_buffer_size 16k;
53 |         proxy_buffers 8 16k;
54 |         proxy_busy_buffers_size 32k;
55 |     }
56 |     
57 |     # Rate limiting for API endpoints
58 |     location /mcp {
59 |         limit_req zone=mcp_limit burst=10 nodelay;
60 |         proxy_pass http://localhost:3000;
61 |         proxy_http_version 1.1;
62 |         proxy_set_header Host $host;
63 |         proxy_set_header X-Real-IP $remote_addr;
64 |         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
65 |         proxy_set_header X-Forwarded-Proto $scheme;
66 |         
67 |         # Larger timeouts for MCP
68 |         proxy_connect_timeout 120s;
69 |         proxy_send_timeout 120s;
70 |         proxy_read_timeout 120s;
71 |     }
72 | }
73 | 
74 | # Rate limiting zone
75 | limit_req_zone $binary_remote_addr zone=mcp_limit:10m rate=10r/s;
```

--------------------------------------------------------------------------------
/src/utils/error-handler.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { logger } from './logger';
 2 | 
 3 | export class MCPError extends Error {
 4 |   public code: string;
 5 |   public statusCode?: number;
 6 |   public data?: any;
 7 | 
 8 |   constructor(message: string, code: string, statusCode?: number, data?: any) {
 9 |     super(message);
10 |     this.name = 'MCPError';
11 |     this.code = code;
12 |     this.statusCode = statusCode;
13 |     this.data = data;
14 |   }
15 | }
16 | 
17 | export class N8NConnectionError extends MCPError {
18 |   constructor(message: string, data?: any) {
19 |     super(message, 'N8N_CONNECTION_ERROR', 503, data);
20 |     this.name = 'N8NConnectionError';
21 |   }
22 | }
23 | 
24 | export class AuthenticationError extends MCPError {
25 |   constructor(message: string = 'Authentication failed') {
26 |     super(message, 'AUTH_ERROR', 401);
27 |     this.name = 'AuthenticationError';
28 |   }
29 | }
30 | 
31 | export class ValidationError extends MCPError {
32 |   constructor(message: string, data?: any) {
33 |     super(message, 'VALIDATION_ERROR', 400, data);
34 |     this.name = 'ValidationError';
35 |   }
36 | }
37 | 
38 | export class ToolNotFoundError extends MCPError {
39 |   constructor(toolName: string) {
40 |     super(`Tool '${toolName}' not found`, 'TOOL_NOT_FOUND', 404);
41 |     this.name = 'ToolNotFoundError';
42 |   }
43 | }
44 | 
45 | export class ResourceNotFoundError extends MCPError {
46 |   constructor(resourceUri: string) {
47 |     super(`Resource '${resourceUri}' not found`, 'RESOURCE_NOT_FOUND', 404);
48 |     this.name = 'ResourceNotFoundError';
49 |   }
50 | }
51 | 
52 | export function handleError(error: any): MCPError {
53 |   if (error instanceof MCPError) {
54 |     return error;
55 |   }
56 | 
57 |   if (error.response) {
58 |     // HTTP error from n8n API
59 |     const status = error.response.status;
60 |     const message = error.response.data?.message || error.message;
61 |     
62 |     if (status === 401) {
63 |       return new AuthenticationError(message);
64 |     } else if (status === 404) {
65 |       return new MCPError(message, 'NOT_FOUND', 404);
66 |     } else if (status >= 500) {
67 |       return new N8NConnectionError(message);
68 |     }
69 |     
70 |     return new MCPError(message, 'API_ERROR', status);
71 |   }
72 | 
73 |   if (error.code === 'ECONNREFUSED') {
74 |     return new N8NConnectionError('Cannot connect to n8n API');
75 |   }
76 | 
77 |   // Generic error
78 |   return new MCPError(
79 |     error.message || 'An unexpected error occurred',
80 |     'UNKNOWN_ERROR',
81 |     500
82 |   );
83 | }
84 | 
85 | export async function withErrorHandling<T>(
86 |   operation: () => Promise<T>,
87 |   context: string
88 | ): Promise<T> {
89 |   try {
90 |     return await operation();
91 |   } catch (error) {
92 |     logger.error(`Error in ${context}:`, error);
93 |     throw handleError(error);
94 |   }
95 | }
```

--------------------------------------------------------------------------------
/scripts/test-telemetry-integration.ts:
--------------------------------------------------------------------------------

```typescript
 1 | #!/usr/bin/env npx tsx
 2 | /**
 3 |  * Integration test for the telemetry manager
 4 |  */
 5 | 
 6 | import { telemetry } from '../src/telemetry/telemetry-manager';
 7 | 
 8 | async function testIntegration() {
 9 |   console.log('🧪 Testing Telemetry Manager Integration\n');
10 | 
11 |   // Check status
12 |   console.log('Status:', telemetry.getStatus());
13 | 
14 |   // Track session start
15 |   console.log('\nTracking session start...');
16 |   telemetry.trackSessionStart();
17 | 
18 |   // Track tool usage
19 |   console.log('Tracking tool usage...');
20 |   telemetry.trackToolUsage('search_nodes', true, 150);
21 |   telemetry.trackToolUsage('get_node_info', true, 75);
22 |   telemetry.trackToolUsage('validate_workflow', false, 200);
23 | 
24 |   // Track errors
25 |   console.log('Tracking errors...');
26 |   telemetry.trackError('ValidationError', 'workflow_validation', 'validate_workflow', 'Required field missing: nodes array is empty');
27 | 
28 |   // Track a test workflow
29 |   console.log('Tracking workflow creation...');
30 |   const testWorkflow = {
31 |     nodes: [
32 |       {
33 |         id: '1',
34 |         type: 'n8n-nodes-base.webhook',
35 |         name: 'Webhook',
36 |         position: [0, 0],
37 |         parameters: {
38 |           path: '/test-webhook',
39 |           httpMethod: 'POST'
40 |         }
41 |       },
42 |       {
43 |         id: '2',
44 |         type: 'n8n-nodes-base.httpRequest',
45 |         name: 'HTTP Request',
46 |         position: [250, 0],
47 |         parameters: {
48 |           url: 'https://api.example.com/endpoint',
49 |           method: 'POST',
50 |           authentication: 'genericCredentialType',
51 |           genericAuthType: 'httpHeaderAuth',
52 |           sendHeaders: true,
53 |           headerParameters: {
54 |             parameters: [
55 |               {
56 |                 name: 'Authorization',
57 |                 value: 'Bearer sk-1234567890abcdef'
58 |               }
59 |             ]
60 |           }
61 |         }
62 |       },
63 |       {
64 |         id: '3',
65 |         type: 'n8n-nodes-base.slack',
66 |         name: 'Slack',
67 |         position: [500, 0],
68 |         parameters: {
69 |           channel: '#notifications',
70 |           text: 'Workflow completed!'
71 |         }
72 |       }
73 |     ],
74 |     connections: {
75 |       '1': {
76 |         main: [[{ node: '2', type: 'main', index: 0 }]]
77 |       },
78 |       '2': {
79 |         main: [[{ node: '3', type: 'main', index: 0 }]]
80 |       }
81 |     }
82 |   };
83 | 
84 |   telemetry.trackWorkflowCreation(testWorkflow, true);
85 | 
86 |   // Force flush
87 |   console.log('\nFlushing telemetry data...');
88 |   await telemetry.flush();
89 | 
90 |   console.log('\n✅ Telemetry integration test completed!');
91 |   console.log('Check your Supabase dashboard for the telemetry data.');
92 | }
93 | 
94 | testIntegration().catch(console.error);
95 | 
```

--------------------------------------------------------------------------------
/scripts/test-security.ts:
--------------------------------------------------------------------------------

```typescript
 1 | #!/usr/bin/env node
 2 | import axios from 'axios';
 3 | import { spawn } from 'child_process';
 4 | 
 5 | async function testMaliciousHeaders() {
 6 |   console.log('🔒 Testing Security Fixes...\n');
 7 |   
 8 |   // Start server with TRUST_PROXY enabled
 9 |   const serverProcess = spawn('node', ['dist/mcp/index.js'], {
10 |     env: {
11 |       ...process.env,
12 |       MCP_MODE: 'http',
13 |       AUTH_TOKEN: 'test-security-token-32-characters-long',
14 |       PORT: '3999',
15 |       TRUST_PROXY: '1'
16 |     }
17 |   });
18 | 
19 |   // Wait for server to start
20 |   await new Promise(resolve => {
21 |     serverProcess.stdout.on('data', (data) => {
22 |       if (data.toString().includes('Press Ctrl+C to stop')) {
23 |         resolve(undefined);
24 |       }
25 |     });
26 |   });
27 | 
28 |   const testCases = [
29 |     {
30 |       name: 'Valid proxy headers',
31 |       headers: {
32 |         'X-Forwarded-Host': 'example.com',
33 |         'X-Forwarded-Proto': 'https'
34 |       }
35 |     },
36 |     {
37 |       name: 'Malicious host header (with path)',
38 |       headers: {
39 |         'X-Forwarded-Host': 'evil.com/path/to/evil',
40 |         'X-Forwarded-Proto': 'https'
41 |       }
42 |     },
43 |     {
44 |       name: 'Malicious host header (with @)',
45 |       headers: {
46 |         'X-Forwarded-Host': '[email protected]',
47 |         'X-Forwarded-Proto': 'https'
48 |       }
49 |     },
50 |     {
51 |       name: 'Invalid hostname (multiple dots)',
52 |       headers: {
53 |         'X-Forwarded-Host': '.....',
54 |         'X-Forwarded-Proto': 'https'
55 |       }
56 |     },
57 |     {
58 |       name: 'IPv6 address',
59 |       headers: {
60 |         'X-Forwarded-Host': '[::1]:3000',
61 |         'X-Forwarded-Proto': 'https'
62 |       }
63 |     }
64 |   ];
65 | 
66 |   for (const testCase of testCases) {
67 |     try {
68 |       const response = await axios.get('http://localhost:3999/', {
69 |         headers: testCase.headers,
70 |         timeout: 2000
71 |       });
72 |       
73 |       const endpoints = response.data.endpoints;
74 |       const healthUrl = endpoints?.health?.url || 'N/A';
75 |       
76 |       console.log(`✅ ${testCase.name}`);
77 |       console.log(`   Response: ${healthUrl}`);
78 |       
79 |       // Check if malicious headers were blocked
80 |       if (testCase.name.includes('Malicious') || testCase.name.includes('Invalid')) {
81 |         if (healthUrl.includes('evil.com') || healthUrl.includes('@') || healthUrl.includes('.....')) {
82 |           console.log('   ❌ SECURITY ISSUE: Malicious header was not blocked!');
83 |         } else {
84 |           console.log('   ✅ Malicious header was blocked');
85 |         }
86 |       }
87 |     } catch (error) {
88 |       console.log(`❌ ${testCase.name} - Request failed`);
89 |     }
90 |     console.log('');
91 |   }
92 | 
93 |   serverProcess.kill();
94 | }
95 | 
96 | testMaliciousHeaders().catch(console.error);
```

--------------------------------------------------------------------------------
/src/services/sqlite-storage-service.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * SQLiteStorageService - A simple wrapper around DatabaseAdapter for benchmarks
 3 |  */
 4 | import { DatabaseAdapter, createDatabaseAdapter } from '../database/database-adapter';
 5 | 
 6 | export class SQLiteStorageService {
 7 |   private adapter: DatabaseAdapter | null = null;
 8 |   private dbPath: string;
 9 | 
10 |   constructor(dbPath: string = ':memory:') {
11 |     this.dbPath = dbPath;
12 |     this.initSync();
13 |   }
14 | 
15 |   private initSync() {
16 |     // For benchmarks, we'll use synchronous initialization
17 |     // In real usage, this should be async
18 |     const Database = require('better-sqlite3');
19 |     const db = new Database(this.dbPath);
20 |     
21 |     // Create a simple adapter
22 |     this.adapter = {
23 |       prepare: (sql: string) => db.prepare(sql),
24 |       exec: (sql: string) => db.exec(sql),
25 |       close: () => db.close(),
26 |       pragma: (key: string, value?: any) => db.pragma(`${key}${value !== undefined ? ` = ${value}` : ''}`),
27 |       inTransaction: db.inTransaction,
28 |       transaction: (fn: () => any) => db.transaction(fn)(),
29 |       checkFTS5Support: () => {
30 |         try {
31 |           db.exec("CREATE VIRTUAL TABLE test_fts USING fts5(content)");
32 |           db.exec("DROP TABLE test_fts");
33 |           return true;
34 |         } catch {
35 |           return false;
36 |         }
37 |       }
38 |     };
39 |     
40 |     // Initialize schema
41 |     this.initializeSchema();
42 |   }
43 |   
44 |   private initializeSchema() {
45 |     const schema = `
46 |       CREATE TABLE IF NOT EXISTS nodes (
47 |         node_type TEXT PRIMARY KEY,
48 |         package_name TEXT NOT NULL,
49 |         display_name TEXT NOT NULL,
50 |         description TEXT,
51 |         category TEXT,
52 |         development_style TEXT CHECK(development_style IN ('declarative', 'programmatic')),
53 |         is_ai_tool INTEGER DEFAULT 0,
54 |         is_trigger INTEGER DEFAULT 0,
55 |         is_webhook INTEGER DEFAULT 0,
56 |         is_versioned INTEGER DEFAULT 0,
57 |         version TEXT,
58 |         documentation TEXT,
59 |         properties_schema TEXT,
60 |         operations TEXT,
61 |         credentials_required TEXT,
62 |         updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
63 |       );
64 |       
65 |       CREATE INDEX IF NOT EXISTS idx_package ON nodes(package_name);
66 |       CREATE INDEX IF NOT EXISTS idx_ai_tool ON nodes(is_ai_tool);
67 |       CREATE INDEX IF NOT EXISTS idx_category ON nodes(category);
68 |     `;
69 |     
70 |     this.adapter!.exec(schema);
71 |   }
72 | 
73 |   get db(): DatabaseAdapter {
74 |     if (!this.adapter) {
75 |       throw new Error('Database not initialized');
76 |     }
77 |     return this.adapter;
78 |   }
79 | 
80 |   close() {
81 |     if (this.adapter) {
82 |       this.adapter.close();
83 |       this.adapter = null;
84 |     }
85 |   }
86 | }
```

--------------------------------------------------------------------------------
/tests/test-parsing-operations.js:
--------------------------------------------------------------------------------

```javascript
 1 | #!/usr/bin/env node
 2 | 
 3 | const markdown = `
 4 | ## Operations
 5 | 
 6 | * **Channel**
 7 |     * **Archive** a channel.
 8 |     * **Close** a direct message or multi-person direct message.
 9 |     * **Create** a public or private channel-based conversation.
10 |     * **Get** information about a channel.
11 |     * **Get Many**: Get a list of channels in Slack.
12 | * **File**
13 |     * **Get** a file.
14 |     * **Get Many**: Get and filter team files.
15 |     * **Upload**: Create or upload an existing file.
16 | 
17 | ## Templates and examples
18 | `;
19 | 
20 | function extractOperations(markdown) {
21 |   const operations = [];
22 |   
23 |   // Find operations section
24 |   const operationsMatch = markdown.match(/##\s+Operations\s*\n([\s\S]*?)(?=\n##|\n#|$)/i);
25 |   if (!operationsMatch) {
26 |     console.log('No operations section found');
27 |     return operations;
28 |   }
29 |   
30 |   const operationsText = operationsMatch[1];
31 |   console.log('Operations text:', operationsText.substring(0, 200));
32 |   
33 |   // Parse operation structure
34 |   let currentResource = null;
35 |   const lines = operationsText.split('\n');
36 |   
37 |   for (let i = 0; i < lines.length; i++) {
38 |     const line = lines[i];
39 |     const trimmedLine = line.trim();
40 |     
41 |     // Resource level (e.g., "* **Channel**")
42 |     if (trimmedLine.match(/^\*\s+\*\*([^*]+)\*\*/)) {
43 |       currentResource = trimmedLine.match(/^\*\s+\*\*([^*]+)\*\*/)[1].trim();
44 |       console.log(`Found resource: ${currentResource}`);
45 |       continue;
46 |     }
47 |     
48 |     // Skip if we don't have a current resource
49 |     if (!currentResource) continue;
50 |     
51 |     // Operation level - look for indented bullets (4 spaces + *)
52 |     if (line.match(/^\s{4}\*\s+/)) {
53 |       console.log(`Found operation line: "${line}"`);
54 |       
55 |       // Extract operation name and description
56 |       const operationMatch = trimmedLine.match(/^\*\s+\*\*([^*]+)\*\*(.*)$/);
57 |       if (operationMatch) {
58 |         const operation = operationMatch[1].trim();
59 |         let description = operationMatch[2].trim();
60 |         
61 |         // Clean up description
62 |         description = description.replace(/^:\s*/, '').replace(/\.$/, '').trim();
63 |         
64 |         operations.push({
65 |           resource: currentResource,
66 |           operation,
67 |           description: description || operation,
68 |         });
69 |         console.log(`  Parsed: ${operation} - ${description}`);
70 |       }
71 |     }
72 |   }
73 |   
74 |   return operations;
75 | }
76 | 
77 | const operations = extractOperations(markdown);
78 | console.log('\nTotal operations found:', operations.length);
79 | console.log('\nOperations:');
80 | operations.forEach(op => {
81 |   console.log(`- ${op.resource}.${op.operation}: ${op.description}`);
82 | });
```

--------------------------------------------------------------------------------
/docs/CLAUDE_CODE_SETUP.md:
--------------------------------------------------------------------------------

```markdown
 1 | # Claude Code Setup
 2 | 
 3 | Connect n8n-MCP to Claude Code CLI for enhanced n8n workflow development from the command line.
 4 | 
 5 | ## Quick Setup via CLI
 6 | 
 7 | ### Basic configuration (documentation tools only):
 8 | ```bash
 9 | claude mcp add n8n-mcp \
10 |   -e MCP_MODE=stdio \
11 |   -e LOG_LEVEL=error \
12 |   -e DISABLE_CONSOLE_OUTPUT=true \
13 |   -- npx n8n-mcp
14 | ```
15 | 
16 | ![Adding n8n-MCP server in Claude Code](./img/cc_command.png)
17 | 
18 | ### Full configuration (with n8n management tools):
19 | ```bash
20 | claude mcp add n8n-mcp \
21 |   -e MCP_MODE=stdio \
22 |   -e LOG_LEVEL=error \
23 |   -e DISABLE_CONSOLE_OUTPUT=true \
24 |   -e N8N_API_URL=https://your-n8n-instance.com \
25 |   -e N8N_API_KEY=your-api-key \
26 |   -- npx n8n-mcp
27 | ```
28 | 
29 | Make sure to replace `https://your-n8n-instance.com` with your actual n8n URL and `your-api-key` with your n8n API key.
30 | 
31 | ## Alternative Setup Methods
32 | 
33 | ### Option 1: Import from Claude Desktop
34 | 
35 | If you already have n8n-MCP configured in Claude Desktop:
36 | ```bash
37 | claude mcp add-from-claude-desktop
38 | ```
39 | 
40 | ### Option 2: Project Configuration
41 | 
42 | For team sharing, add to `.mcp.json` in your project root:
43 | ```json
44 | {
45 |   "mcpServers": {
46 |     "n8n-mcp": {
47 |       "command": "npx",
48 |       "args": ["n8n-mcp"],
49 |       "env": {
50 |         "MCP_MODE": "stdio",
51 |         "LOG_LEVEL": "error",
52 |         "DISABLE_CONSOLE_OUTPUT": "true",
53 |         "N8N_API_URL": "https://your-n8n-instance.com",
54 |         "N8N_API_KEY": "your-api-key"
55 |       }
56 |     }
57 |   }
58 | }
59 | ```
60 | 
61 | Then use with scope flag:
62 | ```bash
63 | claude mcp add n8n-mcp --scope project
64 | ```
65 | 
66 | ## Managing Your MCP Server
67 | 
68 | Check server status:
69 | ```bash
70 | claude mcp list
71 | claude mcp get n8n-mcp
72 | ```
73 | 
74 | During a conversation, use the `/mcp` command to see server status and available tools.
75 | 
76 | ![n8n-MCP connected and showing 39 tools available](./img/cc_connected.png)
77 | 
78 | Remove the server:
79 | ```bash
80 | claude mcp remove n8n-mcp
81 | ```
82 | 
83 | ## Project Instructions
84 | 
85 | For optimal results, create a `CLAUDE.md` file in your project root with the instructions from the [main README's Claude Project Setup section](../README.md#-claude-project-setup).
86 | 
87 | ## Tips
88 | 
89 | - If you're running n8n locally, use `http://localhost:5678` as the N8N_API_URL
90 | - The n8n API credentials are optional - without them, you'll have documentation and validation tools only
91 | - With API credentials, you'll get full workflow management capabilities
92 | - Use `--scope local` (default) to keep your API credentials private
93 | - Use `--scope project` to share configuration with your team (put credentials in environment variables)
94 | - Claude Code will automatically start the MCP server when you begin a conversation
95 | 
```

--------------------------------------------------------------------------------
/scripts/test-telemetry-security.ts:
--------------------------------------------------------------------------------

```typescript
 1 | #!/usr/bin/env npx tsx
 2 | /**
 3 |  * Test that RLS properly protects data
 4 |  */
 5 | 
 6 | import { createClient } from '@supabase/supabase-js';
 7 | import dotenv from 'dotenv';
 8 | 
 9 | dotenv.config();
10 | 
11 | async function testSecurity() {
12 |   const supabaseUrl = process.env.SUPABASE_URL!;
13 |   const supabaseAnonKey = process.env.SUPABASE_ANON_KEY!;
14 | 
15 |   console.log('🔒 Testing Telemetry Security (RLS)\n');
16 | 
17 |   const supabase = createClient(supabaseUrl, supabaseAnonKey, {
18 |     auth: {
19 |       persistSession: false,
20 |       autoRefreshToken: false,
21 |     }
22 |   });
23 | 
24 |   // Test 1: Verify anon can INSERT
25 |   console.log('Test 1: Anonymous INSERT (should succeed)...');
26 |   const testData = {
27 |     user_id: 'security-test-' + Date.now(),
28 |     event: 'security_test',
29 |     properties: { test: true }
30 |   };
31 | 
32 |   const { error: insertError } = await supabase
33 |     .from('telemetry_events')
34 |     .insert([testData]);
35 | 
36 |   if (insertError) {
37 |     console.error('❌ Insert failed:', insertError.message);
38 |   } else {
39 |     console.log('✅ Insert succeeded (as expected)');
40 |   }
41 | 
42 |   // Test 2: Verify anon CANNOT SELECT
43 |   console.log('\nTest 2: Anonymous SELECT (should fail)...');
44 |   const { data, error: selectError } = await supabase
45 |     .from('telemetry_events')
46 |     .select('*')
47 |     .limit(1);
48 | 
49 |   if (selectError) {
50 |     console.log('✅ Select blocked by RLS (as expected):', selectError.message);
51 |   } else if (data && data.length > 0) {
52 |     console.error('❌ SECURITY ISSUE: Anon can read data!', data);
53 |   } else if (data && data.length === 0) {
54 |     console.log('⚠️  Select returned empty array (might be RLS working)');
55 |   }
56 | 
57 |   // Test 3: Verify anon CANNOT UPDATE
58 |   console.log('\nTest 3: Anonymous UPDATE (should fail)...');
59 |   const { error: updateError } = await supabase
60 |     .from('telemetry_events')
61 |     .update({ event: 'hacked' })
62 |     .eq('user_id', 'test');
63 | 
64 |   if (updateError) {
65 |     console.log('✅ Update blocked (as expected):', updateError.message);
66 |   } else {
67 |     console.error('❌ SECURITY ISSUE: Anon can update data!');
68 |   }
69 | 
70 |   // Test 4: Verify anon CANNOT DELETE
71 |   console.log('\nTest 4: Anonymous DELETE (should fail)...');
72 |   const { error: deleteError } = await supabase
73 |     .from('telemetry_events')
74 |     .delete()
75 |     .eq('user_id', 'test');
76 | 
77 |   if (deleteError) {
78 |     console.log('✅ Delete blocked (as expected):', deleteError.message);
79 |   } else {
80 |     console.error('❌ SECURITY ISSUE: Anon can delete data!');
81 |   }
82 | 
83 |   console.log('\n✨ Security test completed!');
84 |   console.log('Summary: Anonymous users can INSERT (for telemetry) but cannot READ/UPDATE/DELETE');
85 | }
86 | 
87 | testSecurity().catch(console.error);
```

--------------------------------------------------------------------------------
/scripts/format-benchmark-results.js:
--------------------------------------------------------------------------------

```javascript
 1 | #!/usr/bin/env node
 2 | 
 3 | const fs = require('fs');
 4 | const path = require('path');
 5 | 
 6 | /**
 7 |  * Formats Vitest benchmark results for github-action-benchmark
 8 |  * Converts from Vitest format to the expected format
 9 |  */
10 | function formatBenchmarkResults() {
11 |   const resultsPath = path.join(process.cwd(), 'benchmark-results.json');
12 |   
13 |   if (!fs.existsSync(resultsPath)) {
14 |     console.error('benchmark-results.json not found');
15 |     process.exit(1);
16 |   }
17 | 
18 |   const vitestResults = JSON.parse(fs.readFileSync(resultsPath, 'utf8'));
19 |   
20 |   // Convert to github-action-benchmark format
21 |   const formattedResults = [];
22 | 
23 |   // Vitest benchmark JSON reporter format
24 |   if (vitestResults.files) {
25 |     for (const file of vitestResults.files) {
26 |       const suiteName = path.basename(file.filepath, '.bench.ts');
27 |       
28 |       // Process each suite in the file
29 |       if (file.groups) {
30 |         for (const group of file.groups) {
31 |           for (const benchmark of group.benchmarks || []) {
32 |             if (benchmark.result) {
33 |               formattedResults.push({
34 |                 name: `${suiteName} - ${benchmark.name}`,
35 |                 unit: 'ms',
36 |                 value: benchmark.result.mean || 0,
37 |                 range: (benchmark.result.max - benchmark.result.min) || 0,
38 |                 extra: `${benchmark.result.hz?.toFixed(0) || 0} ops/sec`
39 |               });
40 |             }
41 |           }
42 |         }
43 |       }
44 |     }
45 |   } else if (Array.isArray(vitestResults)) {
46 |     // Alternative format handling
47 |     for (const result of vitestResults) {
48 |       if (result.name && result.result) {
49 |         formattedResults.push({
50 |           name: result.name,
51 |           unit: 'ms',
52 |           value: result.result.mean || 0,
53 |           range: (result.result.max - result.result.min) || 0,
54 |           extra: `${result.result.hz?.toFixed(0) || 0} ops/sec`
55 |         });
56 |       }
57 |     }
58 |   }
59 | 
60 |   // Write formatted results
61 |   const outputPath = path.join(process.cwd(), 'benchmark-results-formatted.json');
62 |   fs.writeFileSync(outputPath, JSON.stringify(formattedResults, null, 2));
63 |   
64 |   // Also create a summary for PR comments
65 |   const summary = {
66 |     timestamp: new Date().toISOString(),
67 |     benchmarks: formattedResults.map(b => ({
68 |       name: b.name,
69 |       time: `${b.value.toFixed(3)}ms`,
70 |       opsPerSec: b.extra,
71 |       range: `±${(b.range / 2).toFixed(3)}ms`
72 |     }))
73 |   };
74 |   
75 |   fs.writeFileSync(
76 |     path.join(process.cwd(), 'benchmark-summary.json'),
77 |     JSON.stringify(summary, null, 2)
78 |   );
79 | 
80 |   console.log(`Formatted ${formattedResults.length} benchmark results`);
81 | }
82 | 
83 | // Run if called directly
84 | if (require.main === module) {
85 |   formatBenchmarkResults();
86 | }
```

--------------------------------------------------------------------------------
/scripts/vitest-benchmark-reporter.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import type { Task, TaskResult, BenchmarkResult } from 'vitest';
  2 | import { writeFileSync } from 'fs';
  3 | import { resolve } from 'path';
  4 | 
  5 | interface BenchmarkJsonResult {
  6 |   timestamp: string;
  7 |   files: Array<{
  8 |     filepath: string;
  9 |     groups: Array<{
 10 |       name: string;
 11 |       benchmarks: Array<{
 12 |         name: string;
 13 |         result: {
 14 |           mean: number;
 15 |           min: number;
 16 |           max: number;
 17 |           hz: number;
 18 |           p75: number;
 19 |           p99: number;
 20 |           p995: number;
 21 |           p999: number;
 22 |           rme: number;
 23 |           samples: number;
 24 |         };
 25 |       }>;
 26 |     }>;
 27 |   }>;
 28 | }
 29 | 
 30 | export class BenchmarkJsonReporter {
 31 |   private results: BenchmarkJsonResult = {
 32 |     timestamp: new Date().toISOString(),
 33 |     files: []
 34 |   };
 35 | 
 36 |   onInit() {
 37 |     console.log('[BenchmarkJsonReporter] Initialized');
 38 |   }
 39 | 
 40 |   onFinished(files?: Task[]) {
 41 |     console.log('[BenchmarkJsonReporter] onFinished called');
 42 |     
 43 |     if (!files) {
 44 |       console.log('[BenchmarkJsonReporter] No files provided');
 45 |       return;
 46 |     }
 47 | 
 48 |     for (const file of files) {
 49 |       const fileResult = {
 50 |         filepath: file.filepath || 'unknown',
 51 |         groups: [] as any[]
 52 |       };
 53 | 
 54 |       this.processTask(file, fileResult);
 55 | 
 56 |       if (fileResult.groups.length > 0) {
 57 |         this.results.files.push(fileResult);
 58 |       }
 59 |     }
 60 | 
 61 |     // Write results
 62 |     const outputPath = resolve(process.cwd(), 'benchmark-results.json');
 63 |     writeFileSync(outputPath, JSON.stringify(this.results, null, 2));
 64 |     console.log(`[BenchmarkJsonReporter] Results written to ${outputPath}`);
 65 |   }
 66 | 
 67 |   private processTask(task: Task, fileResult: any) {
 68 |     if (task.type === 'suite' && task.tasks) {
 69 |       const group = {
 70 |         name: task.name,
 71 |         benchmarks: [] as any[]
 72 |       };
 73 | 
 74 |       for (const benchmark of task.tasks) {
 75 |         const result = benchmark.result as TaskResult & { benchmark?: BenchmarkResult };
 76 |         if (result?.benchmark) {
 77 |           group.benchmarks.push({
 78 |             name: benchmark.name,
 79 |             result: {
 80 |               mean: result.benchmark.mean || 0,
 81 |               min: result.benchmark.min || 0,
 82 |               max: result.benchmark.max || 0,
 83 |               hz: result.benchmark.hz || 0,
 84 |               p75: result.benchmark.p75 || 0,
 85 |               p99: result.benchmark.p99 || 0,
 86 |               p995: result.benchmark.p995 || 0,
 87 |               p999: result.benchmark.p999 || 0,
 88 |               rme: result.benchmark.rme || 0,
 89 |               samples: result.benchmark.samples?.length || 0
 90 |             }
 91 |           });
 92 |         }
 93 |       }
 94 | 
 95 |       if (group.benchmarks.length > 0) {
 96 |         fileResult.groups.push(group);
 97 |       }
 98 |     }
 99 |   }
100 | }
```

--------------------------------------------------------------------------------
/tests/fixtures/factories/node.factory.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { Factory } from 'fishery';
  2 | import { faker } from '@faker-js/faker';
  3 | 
  4 | interface NodeDefinition {
  5 |   name: string;
  6 |   displayName: string;
  7 |   description: string;
  8 |   version: number;
  9 |   defaults: { name: string };
 10 |   inputs: string[];
 11 |   outputs: string[];
 12 |   properties: any[];
 13 |   credentials?: any[];
 14 |   group?: string[];
 15 | }
 16 | 
 17 | export const nodeFactory = Factory.define<NodeDefinition>(() => ({
 18 |   name: faker.helpers.slugify(faker.word.noun()),
 19 |   displayName: faker.company.name(),
 20 |   description: faker.lorem.sentence(),
 21 |   version: faker.number.int({ min: 1, max: 5 }),
 22 |   defaults: {
 23 |     name: faker.word.noun()
 24 |   },
 25 |   inputs: ['main'],
 26 |   outputs: ['main'],
 27 |   group: [faker.helpers.arrayElement(['transform', 'trigger', 'output'])],
 28 |   properties: [
 29 |     {
 30 |       displayName: 'Resource',
 31 |       name: 'resource',
 32 |       type: 'options',
 33 |       default: 'user',
 34 |       options: [
 35 |         { name: 'User', value: 'user' },
 36 |         { name: 'Post', value: 'post' }
 37 |       ]
 38 |     }
 39 |   ],
 40 |   credentials: []
 41 | }));
 42 | 
 43 | // Specific node factories
 44 | export const webhookNodeFactory = nodeFactory.params({
 45 |   name: 'webhook',
 46 |   displayName: 'Webhook',
 47 |   description: 'Starts the workflow when a webhook is called',
 48 |   group: ['trigger'],
 49 |   properties: [
 50 |     {
 51 |       displayName: 'Path',
 52 |       name: 'path',
 53 |       type: 'string',
 54 |       default: 'webhook',
 55 |       required: true
 56 |     },
 57 |     {
 58 |       displayName: 'Method',
 59 |       name: 'method',
 60 |       type: 'options',
 61 |       default: 'GET',
 62 |       options: [
 63 |         { name: 'GET', value: 'GET' },
 64 |         { name: 'POST', value: 'POST' }
 65 |       ]
 66 |     }
 67 |   ]
 68 | });
 69 | 
 70 | export const slackNodeFactory = nodeFactory.params({
 71 |   name: 'slack',
 72 |   displayName: 'Slack',
 73 |   description: 'Send messages to Slack',
 74 |   group: ['output'],
 75 |   credentials: [
 76 |     {
 77 |       name: 'slackApi',
 78 |       required: true
 79 |     }
 80 |   ],
 81 |   properties: [
 82 |     {
 83 |       displayName: 'Resource',
 84 |       name: 'resource',
 85 |       type: 'options',
 86 |       default: 'message',
 87 |       options: [
 88 |         { name: 'Message', value: 'message' },
 89 |         { name: 'Channel', value: 'channel' }
 90 |       ]
 91 |     },
 92 |     {
 93 |       displayName: 'Operation',
 94 |       name: 'operation',
 95 |       type: 'options',
 96 |       displayOptions: {
 97 |         show: {
 98 |           resource: ['message']
 99 |         }
100 |       },
101 |       default: 'post',
102 |       options: [
103 |         { name: 'Post', value: 'post' },
104 |         { name: 'Update', value: 'update' }
105 |       ]
106 |     },
107 |     {
108 |       displayName: 'Channel',
109 |       name: 'channel',
110 |       type: 'string',
111 |       required: true,
112 |       displayOptions: {
113 |         show: {
114 |           resource: ['message'],
115 |           operation: ['post']
116 |         }
117 |       },
118 |       default: ''
119 |     }
120 |   ]
121 | });
```

--------------------------------------------------------------------------------
/types/test-env.d.ts:
--------------------------------------------------------------------------------

```typescript
  1 | /**
  2 |  * Type definitions for test environment variables
  3 |  */
  4 | 
  5 | declare global {
  6 |   namespace NodeJS {
  7 |     interface ProcessEnv {
  8 |       // Core Environment
  9 |       NODE_ENV: 'test' | 'development' | 'production';
 10 |       MCP_MODE?: 'test' | 'http' | 'stdio';
 11 |       TEST_ENVIRONMENT?: string;
 12 | 
 13 |       // Database Configuration
 14 |       NODE_DB_PATH?: string;
 15 |       REBUILD_ON_START?: string;
 16 |       TEST_SEED_DATABASE?: string;
 17 |       TEST_SEED_TEMPLATES?: string;
 18 | 
 19 |       // API Configuration
 20 |       N8N_API_URL?: string;
 21 |       N8N_API_KEY?: string;
 22 |       N8N_WEBHOOK_BASE_URL?: string;
 23 |       N8N_WEBHOOK_TEST_URL?: string;
 24 | 
 25 |       // Server Configuration
 26 |       PORT?: string;
 27 |       HOST?: string;
 28 |       CORS_ORIGIN?: string;
 29 | 
 30 |       // Authentication
 31 |       AUTH_TOKEN?: string;
 32 |       MCP_AUTH_TOKEN?: string;
 33 | 
 34 |       // Logging
 35 |       LOG_LEVEL?: 'debug' | 'info' | 'warn' | 'error';
 36 |       DEBUG?: string;
 37 |       TEST_LOG_VERBOSE?: string;
 38 |       ERROR_SHOW_STACK?: string;
 39 |       ERROR_SHOW_DETAILS?: string;
 40 | 
 41 |       // Test Timeouts
 42 |       TEST_TIMEOUT_UNIT?: string;
 43 |       TEST_TIMEOUT_INTEGRATION?: string;
 44 |       TEST_TIMEOUT_E2E?: string;
 45 |       TEST_TIMEOUT_GLOBAL?: string;
 46 | 
 47 |       // Test Execution
 48 |       TEST_RETRY_ATTEMPTS?: string;
 49 |       TEST_RETRY_DELAY?: string;
 50 |       TEST_PARALLEL?: string;
 51 |       TEST_MAX_WORKERS?: string;
 52 | 
 53 |       // Feature Flags
 54 |       FEATURE_TEST_COVERAGE?: string;
 55 |       FEATURE_TEST_SCREENSHOTS?: string;
 56 |       FEATURE_TEST_VIDEOS?: string;
 57 |       FEATURE_TEST_TRACE?: string;
 58 |       FEATURE_MOCK_EXTERNAL_APIS?: string;
 59 |       FEATURE_USE_TEST_CONTAINERS?: string;
 60 | 
 61 |       // Mock Services
 62 |       MSW_ENABLED?: string;
 63 |       MSW_API_DELAY?: string;
 64 |       REDIS_MOCK_ENABLED?: string;
 65 |       REDIS_MOCK_PORT?: string;
 66 |       ELASTICSEARCH_MOCK_ENABLED?: string;
 67 |       ELASTICSEARCH_MOCK_PORT?: string;
 68 | 
 69 |       // Test Paths
 70 |       TEST_FIXTURES_PATH?: string;
 71 |       TEST_DATA_PATH?: string;
 72 |       TEST_SNAPSHOTS_PATH?: string;
 73 | 
 74 |       // Performance Thresholds
 75 |       PERF_THRESHOLD_API_RESPONSE?: string;
 76 |       PERF_THRESHOLD_DB_QUERY?: string;
 77 |       PERF_THRESHOLD_NODE_PARSE?: string;
 78 | 
 79 |       // Rate Limiting
 80 |       RATE_LIMIT_MAX?: string;
 81 |       RATE_LIMIT_WINDOW?: string;
 82 | 
 83 |       // Caching
 84 |       CACHE_TTL?: string;
 85 |       CACHE_ENABLED?: string;
 86 | 
 87 |       // Cleanup
 88 |       TEST_CLEANUP_ENABLED?: string;
 89 |       TEST_CLEANUP_ON_FAILURE?: string;
 90 | 
 91 |       // Network
 92 |       NETWORK_TIMEOUT?: string;
 93 |       NETWORK_RETRY_COUNT?: string;
 94 | 
 95 |       // Memory
 96 |       TEST_MEMORY_LIMIT?: string;
 97 | 
 98 |       // Coverage
 99 |       COVERAGE_DIR?: string;
100 |       COVERAGE_REPORTER?: string;
101 |     }
102 |   }
103 | }
104 | 
105 | // Export empty object to make this a module
106 | export {};
```

--------------------------------------------------------------------------------
/scripts/http-bridge.js:
--------------------------------------------------------------------------------

```javascript
  1 | #!/usr/bin/env node
  2 | 
  3 | /**
  4 |  * HTTP-to-stdio bridge for n8n-MCP
  5 |  * Connects to n8n-MCP HTTP server and bridges stdio communication
  6 |  */
  7 | 
  8 | const http = require('http');
  9 | const readline = require('readline');
 10 | 
 11 | // Use MCP_URL from environment or construct from HOST/PORT if available
 12 | const defaultHost = process.env.HOST || 'localhost';
 13 | const defaultPort = process.env.PORT || '3000';
 14 | const MCP_URL = process.env.MCP_URL || `http://${defaultHost}:${defaultPort}/mcp`;
 15 | const AUTH_TOKEN = process.env.AUTH_TOKEN || process.argv[2];
 16 | 
 17 | if (!AUTH_TOKEN) {
 18 |   console.error('Error: AUTH_TOKEN environment variable or first argument required');
 19 |   process.exit(1);
 20 | }
 21 | 
 22 | // Parse URL
 23 | const url = new URL(MCP_URL);
 24 | 
 25 | // Create readline interface for stdio
 26 | const rl = readline.createInterface({
 27 |   input: process.stdin,
 28 |   output: process.stdout,
 29 |   terminal: false
 30 | });
 31 | 
 32 | // Buffer for incomplete JSON messages
 33 | let buffer = '';
 34 | 
 35 | // Handle incoming stdio messages
 36 | rl.on('line', async (line) => {
 37 |   try {
 38 |     const message = JSON.parse(line);
 39 |     
 40 |     // Forward to HTTP server
 41 |     const options = {
 42 |       hostname: url.hostname,
 43 |       port: url.port || 80,
 44 |       path: url.pathname,
 45 |       method: 'POST',
 46 |       headers: {
 47 |         'Content-Type': 'application/json',
 48 |         'Authorization': `Bearer ${AUTH_TOKEN}`,
 49 |         'Accept': 'application/json, text/event-stream'
 50 |       }
 51 |     };
 52 | 
 53 |     const req = http.request(options, (res) => {
 54 |       let responseData = '';
 55 | 
 56 |       res.on('data', (chunk) => {
 57 |         responseData += chunk;
 58 |       });
 59 | 
 60 |       res.on('end', () => {
 61 |         try {
 62 |           // Try to parse as JSON
 63 |           const response = JSON.parse(responseData);
 64 |           console.log(JSON.stringify(response));
 65 |         } catch (e) {
 66 |           // Handle SSE format
 67 |           const lines = responseData.split('\n');
 68 |           for (const line of lines) {
 69 |             if (line.startsWith('data: ')) {
 70 |               try {
 71 |                 const data = JSON.parse(line.substring(6));
 72 |                 console.log(JSON.stringify(data));
 73 |               } catch (e) {
 74 |                 // Ignore parse errors
 75 |               }
 76 |             }
 77 |           }
 78 |         }
 79 |       });
 80 |     });
 81 | 
 82 |     req.on('error', (error) => {
 83 |       console.error(JSON.stringify({
 84 |         jsonrpc: '2.0',
 85 |         error: {
 86 |           code: -32000,
 87 |           message: `HTTP request failed: ${error.message}`
 88 |         },
 89 |         id: message.id || null
 90 |       }));
 91 |     });
 92 | 
 93 |     req.write(JSON.stringify(message));
 94 |     req.end();
 95 |   } catch (error) {
 96 |     // Not valid JSON, ignore
 97 |   }
 98 | });
 99 | 
100 | // Handle process termination
101 | process.on('SIGTERM', () => {
102 |   process.exit(0);
103 | });
104 | 
105 | process.on('SIGINT', () => {
106 |   process.exit(0);
107 | });
```

--------------------------------------------------------------------------------
/tests/integration/setup/integration-setup.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { beforeAll, afterAll, afterEach } from 'vitest';
 2 | import { setupServer } from 'msw/node';
 3 | import { handlers as defaultHandlers } from '../../mocks/n8n-api/handlers';
 4 | 
 5 | // Create the MSW server instance with default handlers
 6 | export const server = setupServer(...defaultHandlers);
 7 | 
 8 | // Enable request logging in development/debugging
 9 | if (process.env.MSW_DEBUG === 'true' || process.env.TEST_DEBUG === 'true') {
10 |   server.events.on('request:start', ({ request }) => {
11 |     console.log('[MSW] %s %s', request.method, request.url);
12 |   });
13 | 
14 |   server.events.on('request:match', ({ request }) => {
15 |     console.log('[MSW] Request matched:', request.method, request.url);
16 |   });
17 | 
18 |   server.events.on('request:unhandled', ({ request }) => {
19 |     console.warn('[MSW] Unhandled request:', request.method, request.url);
20 |   });
21 | 
22 |   server.events.on('response:mocked', ({ request, response }) => {
23 |     console.log('[MSW] Mocked response for %s %s: %d', 
24 |       request.method, 
25 |       request.url, 
26 |       response.status
27 |     );
28 |   });
29 | }
30 | 
31 | // Start server before all tests
32 | beforeAll(() => {
33 |   server.listen({
34 |     onUnhandledRequest: process.env.CI === 'true' ? 'error' : 'warn',
35 |   });
36 | });
37 | 
38 | // Reset handlers after each test (important for test isolation)
39 | afterEach(() => {
40 |   server.resetHandlers();
41 | });
42 | 
43 | // Clean up after all tests
44 | afterAll(() => {
45 |   // Remove all event listeners to prevent memory leaks
46 |   server.events.removeAllListeners();
47 |   
48 |   // Close the server
49 |   server.close();
50 | });
51 | 
52 | // Export the server and utility functions for use in integration tests
53 | export { server as integrationServer };
54 | export { http, HttpResponse } from 'msw';
55 | 
56 | /**
57 |  * Utility function to add temporary handlers for specific tests
58 |  * @param handlers Array of MSW request handlers
59 |  */
60 | export function useHandlers(...handlers: any[]) {
61 |   server.use(...handlers);
62 | }
63 | 
64 | /**
65 |  * Utility to wait for a specific request to be made
66 |  * Useful for testing async operations
67 |  */
68 | export function waitForRequest(method: string, url: string | RegExp, timeout = 5000): Promise<Request> {
69 |   return new Promise((resolve, reject) => {
70 |     let timeoutId: NodeJS.Timeout;
71 |     
72 |     const handler = ({ request }: { request: Request }) => {
73 |       if (request.method === method && 
74 |           (typeof url === 'string' ? request.url === url : url.test(request.url))) {
75 |         clearTimeout(timeoutId);
76 |         server.events.removeListener('request:match', handler);
77 |         resolve(request);
78 |       }
79 |     };
80 |     
81 |     // Set timeout
82 |     timeoutId = setTimeout(() => {
83 |       server.events.removeListener('request:match', handler);
84 |       reject(new Error(`Timeout waiting for ${method} request to ${url}`));
85 |     }, timeout);
86 |     
87 |     server.events.on('request:match', handler);
88 |   });
89 | }
```

--------------------------------------------------------------------------------
/tests/test-direct-extraction.js:
--------------------------------------------------------------------------------

```javascript
 1 | #!/usr/bin/env node
 2 | 
 3 | const fs = require('fs');
 4 | const path = require('path');
 5 | 
 6 | // Import the NodeSourceExtractor
 7 | const { NodeSourceExtractor } = require('../dist/utils/node-source-extractor');
 8 | 
 9 | async function testExtraction() {
10 |   console.log('=== Direct Node Extraction Test ===\n');
11 |   
12 |   const extractor = new NodeSourceExtractor();
13 |   
14 |   // Test extraction of AI Agent node
15 |   const nodeType = '@n8n/n8n-nodes-langchain.Agent';
16 |   
17 |   console.log(`Testing extraction of: ${nodeType}`);
18 |   
19 |   // First, let's debug what paths are being searched
20 |   console.log('\nSearching in paths:');
21 |   const searchPaths = [
22 |     '/usr/local/lib/node_modules/n8n/node_modules',
23 |     '/app/node_modules',
24 |     '/home/node/.n8n/custom/nodes',
25 |     './node_modules'
26 |   ];
27 |   
28 |   for (const basePath of searchPaths) {
29 |     console.log(`- ${basePath}`);
30 |     try {
31 |       const exists = fs.existsSync(basePath);
32 |       console.log(`  Exists: ${exists}`);
33 |       if (exists) {
34 |         const items = fs.readdirSync(basePath).slice(0, 5);
35 |         console.log(`  Sample items: ${items.join(', ')}...`);
36 |       }
37 |     } catch (e) {
38 |       console.log(`  Error: ${e.message}`);
39 |     }
40 |   }
41 |   
42 |   try {
43 |     const result = await extractor.extractNodeSource(nodeType, true);
44 |     
45 |     console.log('\n✅ Extraction successful!');
46 |     console.log(`Source file: ${result.location}`);
47 |     console.log(`Code length: ${result.sourceCode.length} characters`);
48 |     console.log(`Credential code found: ${result.credentialCode ? 'Yes' : 'No'}`);
49 |     console.log(`Package.json found: ${result.packageInfo ? 'Yes' : 'No'}`);
50 |     
51 |     // Show first 500 characters of the code
52 |     console.log('\nFirst 500 characters of code:');
53 |     console.log('=' .repeat(60));
54 |     console.log(result.sourceCode.substring(0, 500) + '...');
55 |     console.log('=' .repeat(60));
56 |     
57 |     // Show credential code if found
58 |     if (result.credentialCode) {
59 |       console.log('\nCredential code found!');
60 |       console.log('First 200 characters of credential code:');
61 |       console.log(result.credentialCode.substring(0, 200) + '...');
62 |     }
63 |     
64 |     // Check if we can find it in Docker volume
65 |     const dockerPath = '/usr/local/lib/node_modules/n8n/node_modules/.pnpm/@n8n+n8n-nodes-langchain@file+packages+@n8n+nodes-langchain_f35e7d377a7fe4d08dc2766706b5dbff/node_modules/@n8n/n8n-nodes-langchain/dist/nodes/agents/Agent/Agent.node.js';
66 |     
67 |     if (fs.existsSync(dockerPath)) {
68 |       console.log('\n✅ File also found in expected Docker path');
69 |       const dockerContent = fs.readFileSync(dockerPath, 'utf8');
70 |       console.log(`Docker file size: ${dockerContent.length} bytes`);
71 |     }
72 |     
73 |   } catch (error) {
74 |     console.error('\n❌ Extraction failed:', error.message);
75 |     console.error('Stack trace:', error.stack);
76 |   }
77 | }
78 | 
79 | testExtraction().catch(console.error);
```

--------------------------------------------------------------------------------
/tests/integration/mcp-protocol/basic-connection.test.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { describe, it, expect } from 'vitest';
 2 | import { N8NDocumentationMCPServer } from '../../../src/mcp/server';
 3 | 
 4 | describe('Basic MCP Connection', () => {
 5 |   it('should initialize MCP server', async () => {
 6 |     const server = new N8NDocumentationMCPServer();
 7 |     
 8 |     // Test executeTool directly - it returns raw data
 9 |     const result = await server.executeTool('get_database_statistics', {});
10 |     expect(result).toBeDefined();
11 |     expect(typeof result).toBe('object');
12 |     expect(result.totalNodes).toBeDefined();
13 |     expect(result.statistics).toBeDefined();
14 |     
15 |     await server.shutdown();
16 |   });
17 |   
18 |   it('should execute list_nodes tool', async () => {
19 |     const server = new N8NDocumentationMCPServer();
20 |     
21 |     // First check if we have any nodes in the database
22 |     const stats = await server.executeTool('get_database_statistics', {});
23 |     const hasNodes = stats.totalNodes > 0;
24 |     
25 |     const result = await server.executeTool('list_nodes', { limit: 5 });
26 |     expect(result).toBeDefined();
27 |     expect(typeof result).toBe('object');
28 |     expect(result.nodes).toBeDefined();
29 |     expect(Array.isArray(result.nodes)).toBe(true);
30 |     
31 |     if (hasNodes) {
32 |       // If database has nodes, we should get up to 5
33 |       expect(result.nodes.length).toBeLessThanOrEqual(5);
34 |       expect(result.nodes.length).toBeGreaterThan(0);
35 |       expect(result.nodes[0]).toHaveProperty('nodeType');
36 |       expect(result.nodes[0]).toHaveProperty('displayName');
37 |     } else {
38 |       // In test environment with empty database, we expect empty results
39 |       expect(result.nodes).toHaveLength(0);
40 |     }
41 |     
42 |     await server.shutdown();
43 |   });
44 |   
45 |   it('should search nodes', async () => {
46 |     const server = new N8NDocumentationMCPServer();
47 |     
48 |     // First check if we have any nodes in the database
49 |     const stats = await server.executeTool('get_database_statistics', {});
50 |     const hasNodes = stats.totalNodes > 0;
51 |     
52 |     const result = await server.executeTool('search_nodes', { query: 'webhook' });
53 |     expect(result).toBeDefined();
54 |     expect(typeof result).toBe('object');
55 |     expect(result.results).toBeDefined();
56 |     expect(Array.isArray(result.results)).toBe(true);
57 |     
58 |     // Only expect results if the database has nodes
59 |     if (hasNodes) {
60 |       expect(result.results.length).toBeGreaterThan(0);
61 |       expect(result.totalCount).toBeGreaterThan(0);
62 |       
63 |       // Should find webhook node
64 |       const webhookNode = result.results.find((n: any) => n.nodeType === 'nodes-base.webhook');
65 |       expect(webhookNode).toBeDefined();
66 |       expect(webhookNode.displayName).toContain('Webhook');
67 |     } else {
68 |       // In test environment with empty database, we expect empty results
69 |       expect(result.results).toHaveLength(0);
70 |       expect(result.totalCount).toBe(0);
71 |     }
72 |     
73 |     await server.shutdown();
74 |   });
75 | });
```

--------------------------------------------------------------------------------
/src/mcp/tool-docs/workflow_management/n8n-update-full-workflow.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { ToolDocumentation } from '../types';
 2 | 
 3 | export const n8nUpdateFullWorkflowDoc: ToolDocumentation = {
 4 |   name: 'n8n_update_full_workflow',
 5 |   category: 'workflow_management',
 6 |   essentials: {
 7 |     description: 'Full workflow update. Requires complete nodes[] and connections{}. For incremental use n8n_update_partial_workflow.',
 8 |     keyParameters: ['id', 'nodes', 'connections'],
 9 |     example: 'n8n_update_full_workflow({id: "wf_123", nodes: [...], connections: {...}})',
10 |     performance: 'Network-dependent',
11 |     tips: [
12 |       'Must provide complete workflow',
13 |       'Use update_partial for small changes',
14 |       'Validate before updating'
15 |     ]
16 |   },
17 |   full: {
18 |     description: 'Performs a complete workflow update by replacing the entire workflow definition. Requires providing the complete nodes array and connections object, even for small changes. This is a full replacement operation - any nodes or connections not included will be removed.',
19 |     parameters: {
20 |       id: { type: 'string', required: true, description: 'Workflow ID to update' },
21 |       name: { type: 'string', description: 'New workflow name (optional)' },
22 |       nodes: { type: 'array', description: 'Complete array of workflow nodes (required if modifying structure)' },
23 |       connections: { type: 'object', description: 'Complete connections object (required if modifying structure)' },
24 |       settings: { type: 'object', description: 'Workflow settings to update (timezone, error handling, etc.)' }
25 |     },
26 |     returns: 'Updated workflow object with all fields including the changes applied',
27 |     examples: [
28 |       'n8n_update_full_workflow({id: "abc", name: "New Name"}) - Rename only',
29 |       'n8n_update_full_workflow({id: "xyz", nodes: [...], connections: {...}}) - Full structure update',
30 |       'const wf = n8n_get_workflow({id}); wf.nodes.push(newNode); n8n_update_full_workflow(wf); // Add node'
31 |     ],
32 |     useCases: [
33 |       'Major workflow restructuring',
34 |       'Bulk node updates',
35 |       'Workflow imports/cloning',
36 |       'Complete workflow replacement',
37 |       'Settings changes'
38 |     ],
39 |     performance: 'Network-dependent - typically 200-500ms. Larger workflows take longer. Consider update_partial for better performance.',
40 |     bestPractices: [
41 |       'Get workflow first, modify, then update',
42 |       'Validate with validate_workflow before updating',
43 |       'Use update_partial for small changes',
44 |       'Test updates in non-production first'
45 |     ],
46 |     pitfalls: [
47 |       'Requires N8N_API_URL and N8N_API_KEY configured',
48 |       'Must include ALL nodes/connections',
49 |       'Missing nodes will be deleted',
50 |       'Can break active workflows',
51 |       'No partial updates - use update_partial instead'
52 |     ],
53 |     relatedTools: ['n8n_get_workflow', 'n8n_update_partial_workflow', 'validate_workflow', 'n8n_create_workflow']
54 |   }
55 | };
```

--------------------------------------------------------------------------------
/src/telemetry/error-sanitization-utils.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Shared Error Sanitization Utilities
 3 |  * Used by both error-sanitizer.ts and event-tracker.ts to avoid code duplication
 4 |  *
 5 |  * Security patterns from v2.15.3 with ReDoS fix from v2.18.3
 6 |  */
 7 | 
 8 | import { logger } from '../utils/logger';
 9 | 
10 | /**
11 |  * Core error message sanitization with security-focused patterns
12 |  *
13 |  * Sanitization order (critical for preventing leakage):
14 |  * 1. Early truncation (ReDoS prevention)
15 |  * 2. Stack trace limitation
16 |  * 3. URLs (most encompassing) - fully redact
17 |  * 4. Specific credentials (AWS, GitHub, JWT, Bearer)
18 |  * 5. Emails (after URLs)
19 |  * 6. Long keys and tokens
20 |  * 7. Generic credential patterns
21 |  * 8. Final truncation
22 |  *
23 |  * @param errorMessage - Raw error message to sanitize
24 |  * @returns Sanitized error message safe for telemetry
25 |  */
26 | export function sanitizeErrorMessageCore(errorMessage: string): string {
27 |   try {
28 |     // Early truncate to prevent ReDoS and performance issues
29 |     const maxLength = 1500;
30 |     const trimmed = errorMessage.length > maxLength
31 |       ? errorMessage.substring(0, maxLength)
32 |       : errorMessage;
33 | 
34 |     // Handle stack traces - keep only first 3 lines (message + top stack frames)
35 |     const lines = trimmed.split('\n');
36 |     let sanitized = lines.slice(0, 3).join('\n');
37 | 
38 |     // Sanitize sensitive data in correct order to prevent leakage
39 | 
40 |     // 1. URLs first (most encompassing) - fully redact to prevent path leakage
41 |     sanitized = sanitized.replace(/https?:\/\/\S+/gi, '[URL]');
42 | 
43 |     // 2. Specific credential patterns (before generic patterns)
44 |     sanitized = sanitized
45 |       .replace(/AKIA[A-Z0-9]{16}/g, '[AWS_KEY]')
46 |       .replace(/ghp_[a-zA-Z0-9]{36,}/g, '[GITHUB_TOKEN]')
47 |       .replace(/eyJ[a-zA-Z0-9_-]+\.eyJ[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+/g, '[JWT]')
48 |       .replace(/Bearer\s+[^\s]+/gi, 'Bearer [TOKEN]');
49 | 
50 |     // 3. Emails (after URLs to avoid partial matches)
51 |     sanitized = sanitized.replace(/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g, '[EMAIL]');
52 | 
53 |     // 4. Long keys and quoted tokens
54 |     sanitized = sanitized
55 |       .replace(/\b[a-zA-Z0-9_-]{32,}\b/g, '[KEY]')
56 |       .replace(/(['"])[a-zA-Z0-9_-]{16,}\1/g, '$1[TOKEN]$1');
57 | 
58 |     // 5. Generic credential patterns (after specific ones to avoid conflicts)
59 |     // FIX (v2.18.3): Replaced negative lookbehind with simpler regex to prevent ReDoS
60 |     sanitized = sanitized
61 |       .replace(/password\s*[=:]\s*\S+/gi, 'password=[REDACTED]')
62 |       .replace(/api[_-]?key\s*[=:]\s*\S+/gi, 'api_key=[REDACTED]')
63 |       .replace(/\btoken\s*[=:]\s*[^\s;,)]+/gi, 'token=[REDACTED]'); // Simplified regex (no negative lookbehind)
64 | 
65 |     // Final truncate to 500 chars
66 |     if (sanitized.length > 500) {
67 |       sanitized = sanitized.substring(0, 500) + '...';
68 |     }
69 | 
70 |     return sanitized;
71 |   } catch (error) {
72 |     logger.debug('Error message sanitization failed:', error);
73 |     return '[SANITIZATION_FAILED]';
74 |   }
75 | }
76 | 
```

--------------------------------------------------------------------------------
/src/scripts/rebuild-database.ts:
--------------------------------------------------------------------------------

```typescript
 1 | #!/usr/bin/env node
 2 | 
 3 | import * as dotenv from 'dotenv';
 4 | import { NodeDocumentationService } from '../services/node-documentation-service';
 5 | import { logger } from '../utils/logger';
 6 | 
 7 | // Load environment variables
 8 | dotenv.config();
 9 | 
10 | /**
11 |  * Rebuild the enhanced documentation database
12 |  */
13 | async function rebuildDocumentationDatabase() {
14 |   console.log('🔄 Starting enhanced documentation database rebuild...\n');
15 |   
16 |   const startTime = Date.now();
17 |   const service = new NodeDocumentationService();
18 |   
19 |   try {
20 |     // Run the rebuild
21 |     const results = await service.rebuildDatabase();
22 |     
23 |     const duration = ((Date.now() - startTime) / 1000).toFixed(2);
24 |     
25 |     console.log('\n✅ Enhanced documentation database rebuild completed!\n');
26 |     console.log('📊 Results:');
27 |     console.log(`   Total nodes found: ${results.total}`);
28 |     console.log(`   Successfully processed: ${results.successful}`);
29 |     console.log(`   Failed: ${results.failed}`);
30 |     console.log(`   Duration: ${duration}s`);
31 |     
32 |     if (results.errors.length > 0) {
33 |       console.log(`\n⚠️  First ${Math.min(5, results.errors.length)} errors:`);
34 |       results.errors.slice(0, 5).forEach(err => {
35 |         console.log(`   - ${err}`);
36 |       });
37 |       
38 |       if (results.errors.length > 5) {
39 |         console.log(`   ... and ${results.errors.length - 5} more errors`);
40 |       }
41 |     }
42 |     
43 |     // Get and display statistics
44 |     const stats = await service.getStatistics();
45 |     console.log('\n📈 Database Statistics:');
46 |     console.log(`   Total nodes: ${stats.totalNodes}`);
47 |     console.log(`   Nodes with documentation: ${stats.nodesWithDocs}`);
48 |     console.log(`   Nodes with examples: ${stats.nodesWithExamples}`);
49 |     console.log(`   Nodes with credentials: ${stats.nodesWithCredentials}`);
50 |     console.log(`   Trigger nodes: ${stats.triggerNodes}`);
51 |     console.log(`   Webhook nodes: ${stats.webhookNodes}`);
52 |     
53 |     console.log('\n📦 Package distribution:');
54 |     stats.packageDistribution.slice(0, 10).forEach((pkg: any) => {
55 |       console.log(`   ${pkg.package}: ${pkg.count} nodes`);
56 |     });
57 |     
58 |     // Close database connection
59 |     await service.close();
60 |     
61 |     console.log('\n✨ Enhanced documentation database is ready!');
62 |     console.log('💡 The database now includes:');
63 |     console.log('   - Complete node source code');
64 |     console.log('   - Enhanced documentation with operations and API methods');
65 |     console.log('   - Code examples and templates');
66 |     console.log('   - Related resources and required scopes');
67 |     
68 |   } catch (error) {
69 |     console.error('\n❌ Documentation database rebuild failed:', error);
70 |     service.close();
71 |     process.exit(1);
72 |   }
73 | }
74 | 
75 | // Run if called directly
76 | if (require.main === module) {
77 |   rebuildDocumentationDatabase().catch(error => {
78 |     console.error('Fatal error:', error);
79 |     process.exit(1);
80 |   });
81 | }
82 | 
83 | export { rebuildDocumentationDatabase };
```

--------------------------------------------------------------------------------
/src/mcp/tool-docs/discovery/get-database-statistics.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { ToolDocumentation } from '../types';
 2 | 
 3 | export const getDatabaseStatisticsDoc: ToolDocumentation = {
 4 |   name: 'get_database_statistics',
 5 |   category: 'discovery',
 6 |   essentials: {
 7 |     description: 'Returns database health metrics and node inventory. Shows 525 total nodes, 263 AI-capable nodes, 104 triggers, with 87% documentation coverage. Primary use: verify MCP connection is working correctly.',
 8 |     keyParameters: [],
 9 |     example: 'get_database_statistics()',
10 |     performance: 'Instant',
11 |     tips: [
12 |       'First tool to call when testing MCP connection',
13 |       'Shows exact counts for all node categories',
14 |       'Documentation coverage indicates data quality'
15 |     ]
16 |   },
17 |   full: {
18 |     description: 'Returns comprehensive database statistics showing the complete inventory of n8n nodes, their categories, documentation coverage, and package distribution. Essential for verifying MCP connectivity and understanding available resources.',
19 |     parameters: {},
20 |     returns: `Object containing:
21 | {
22 |   "total_nodes": 525,              // All nodes in database
23 |   "nodes_with_properties": 520,    // Nodes with extracted properties (99%)
24 |   "nodes_with_operations": 334,    // Nodes with multiple operations (64%)
25 |   "ai_tools": 263,                 // AI-capable nodes
26 |   "triggers": 104,                 // Workflow trigger nodes
27 |   "documentation_coverage": "87%", // Nodes with official docs
28 |   "packages": {
29 |     "n8n-nodes-base": 456,         // Core n8n nodes
30 |     "@n8n/n8n-nodes-langchain": 69 // AI/LangChain nodes
31 |   },
32 |   "categories": {
33 |     "trigger": 104,
34 |     "transform": 250,
35 |     "output": 45,
36 |     "input": 38,
37 |     "AI": 88
38 |   }
39 | }`,
40 |     examples: [
41 |       'get_database_statistics() - Returns complete statistics object',
42 |       '// Common check:',
43 |       'const stats = get_database_statistics();',
44 |       'if (stats.total_nodes < 500) console.error("Database incomplete!");'
45 |     ],
46 |     useCases: [
47 |       'Verify MCP server is connected and responding',
48 |       'Check if database rebuild is needed (low node count)',
49 |       'Monitor documentation coverage improvements',
50 |       'Validate AI tools availability for workflows',
51 |       'Audit node distribution across packages'
52 |     ],
53 |     performance: 'Instant (<1ms) - Statistics are pre-calculated and cached',
54 |     bestPractices: [
55 |       'Call this first to verify MCP connection before other operations',
56 |       'Check total_nodes >= 500 to ensure complete database',
57 |       'Monitor documentation_coverage for data quality',
58 |       'Use ai_tools count to verify AI capabilities'
59 |     ],
60 |     pitfalls: [
61 |       'Statistics are cached at database build time, not real-time',
62 |       'Won\'t reflect changes until database is rebuilt',
63 |       'Package counts may vary with n8n version updates'
64 |     ],
65 |     relatedTools: ['list_nodes for detailed node listing', 'list_ai_tools for AI nodes', 'n8n_health_check for API connectivity']
66 |   }
67 | };
```

--------------------------------------------------------------------------------
/src/scripts/test-summary.ts:
--------------------------------------------------------------------------------

```typescript
 1 | #!/usr/bin/env npx tsx
 2 | 
 3 | import { createDatabaseAdapter } from '../database/database-adapter';
 4 | import { NodeRepository } from '../database/node-repository';
 5 | import { NodeSimilarityService } from '../services/node-similarity-service';
 6 | import path from 'path';
 7 | 
 8 | async function testSummary() {
 9 |   const dbPath = path.join(process.cwd(), 'data/nodes.db');
10 |   const db = await createDatabaseAdapter(dbPath);
11 |   const repository = new NodeRepository(db);
12 |   const similarityService = new NodeSimilarityService(repository);
13 | 
14 |   const testCases = [
15 |     { invalid: 'HttpRequest', expected: 'nodes-base.httpRequest' },
16 |     { invalid: 'HTTPRequest', expected: 'nodes-base.httpRequest' },
17 |     { invalid: 'Webhook', expected: 'nodes-base.webhook' },
18 |     { invalid: 'WebHook', expected: 'nodes-base.webhook' },
19 |     { invalid: 'slack', expected: 'nodes-base.slack' },
20 |     { invalid: 'googleSheets', expected: 'nodes-base.googleSheets' },
21 |     { invalid: 'telegram', expected: 'nodes-base.telegram' },
22 |     { invalid: 'htpRequest', expected: 'nodes-base.httpRequest' },
23 |     { invalid: 'webook', expected: 'nodes-base.webhook' },
24 |     { invalid: 'slak', expected: 'nodes-base.slack' },
25 |     { invalid: 'http', expected: 'nodes-base.httpRequest' },
26 |     { invalid: 'sheet', expected: 'nodes-base.googleSheets' },
27 |     { invalid: 'nodes-base.openai', expected: 'nodes-langchain.openAi' },
28 |     { invalid: 'n8n-nodes-base.httpRequest', expected: 'nodes-base.httpRequest' },
29 |     { invalid: 'foobar', expected: null },
30 |     { invalid: 'xyz123', expected: null },
31 |   ];
32 | 
33 |   let passed = 0;
34 |   let failed = 0;
35 | 
36 |   console.log('Test Results Summary:');
37 |   console.log('='.repeat(60));
38 | 
39 |   for (const testCase of testCases) {
40 |     const suggestions = await similarityService.findSimilarNodes(testCase.invalid, 3);
41 | 
42 |     let result = '❌';
43 |     let status = 'FAILED';
44 | 
45 |     if (testCase.expected === null) {
46 |       // Should have no suggestions
47 |       if (suggestions.length === 0) {
48 |         result = '✅';
49 |         status = 'PASSED';
50 |         passed++;
51 |       } else {
52 |         failed++;
53 |       }
54 |     } else {
55 |       // Should have the expected suggestion
56 |       const found = suggestions.some(s => s.nodeType === testCase.expected);
57 |       if (found) {
58 |         const suggestion = suggestions.find(s => s.nodeType === testCase.expected);
59 |         const isAutoFixable = suggestion && suggestion.confidence >= 0.9;
60 |         result = '✅';
61 |         status = isAutoFixable ? 'PASSED (auto-fixable)' : 'PASSED';
62 |         passed++;
63 |       } else {
64 |         failed++;
65 |       }
66 |     }
67 | 
68 |     console.log(`${result} "${testCase.invalid}" → ${testCase.expected || 'no suggestions'}: ${status}`);
69 |   }
70 | 
71 |   console.log('='.repeat(60));
72 |   console.log(`\nTotal: ${passed}/${testCases.length} tests passed`);
73 | 
74 |   if (failed === 0) {
75 |     console.log('🎉 All tests passed!');
76 |   } else {
77 |     console.log(`⚠️  ${failed} tests failed`);
78 |   }
79 | }
80 | 
81 | testSummary().catch(console.error);
```

--------------------------------------------------------------------------------
/tests/extracted-nodes-db/insert-nodes.sql:
--------------------------------------------------------------------------------

```sql
 1 | -- Auto-generated SQL for n8n nodes
 2 | 
 3 | -- Node: n8n-nodes-base.Function
 4 | INSERT INTO nodes (node_type, name, package_name, code_hash, code_length, source_location, has_credentials)
 5 | VALUES ('n8n-nodes-base.Function', 'Function', 'n8n-nodes-base', 'd68f1ab94b190161e2ec2c56ec6631f6c3992826557c100ec578efff5de96a70', 7449, 'node_modules/n8n-nodes-base/dist/nodes/Function/Function.node.js', false);
 6 | 
 7 | -- Node: n8n-nodes-base.Webhook
 8 | INSERT INTO nodes (node_type, name, package_name, code_hash, code_length, source_location, has_credentials)
 9 | VALUES ('n8n-nodes-base.Webhook', 'Webhook', 'n8n-nodes-base', '143d6bbdce335c5a9204112b2c1e8b92e4061d75ba3cb23301845f6fed9e6c71', 10667, 'node_modules/n8n-nodes-base/dist/nodes/Webhook/Webhook.node.js', false);
10 | 
11 | -- Node: n8n-nodes-base.HttpRequest
12 | INSERT INTO nodes (node_type, name, package_name, code_hash, code_length, source_location, has_credentials)
13 | VALUES ('n8n-nodes-base.HttpRequest', 'HttpRequest', 'n8n-nodes-base', '5b5e2328474b7e85361c940dfe942e167b3f0057f38062f56d6b693f0a7ffe7e', 1343, 'node_modules/n8n-nodes-base/dist/nodes/HttpRequest/HttpRequest.node.js', false);
14 | 
15 | -- Node: n8n-nodes-base.If
16 | INSERT INTO nodes (node_type, name, package_name, code_hash, code_length, source_location, has_credentials)
17 | VALUES ('n8n-nodes-base.If', 'If', 'n8n-nodes-base', '7910ed9177a946b76f04ca847defb81226c37c698e4cdb63913f038c6c257ee1', 20533, 'node_modules/n8n-nodes-base/dist/nodes/If/If.node.js', false);
18 | 
19 | -- Node: n8n-nodes-base.SplitInBatches
20 | INSERT INTO nodes (node_type, name, package_name, code_hash, code_length, source_location, has_credentials)
21 | VALUES ('n8n-nodes-base.SplitInBatches', 'SplitInBatches', 'n8n-nodes-base', 'c751422a11e30bf361a6c4803376289740a40434aeb77f90e18cd4dd7ba5c019', 1135, 'node_modules/n8n-nodes-base/dist/nodes/SplitInBatches/SplitInBatches.node.js', false);
22 | 
23 | -- Node: n8n-nodes-base.Airtable
24 | INSERT INTO nodes (node_type, name, package_name, code_hash, code_length, source_location, has_credentials)
25 | VALUES ('n8n-nodes-base.Airtable', 'Airtable', 'n8n-nodes-base', '2d67e72931697178946f5127b43e954649c4c5e7ad9e29764796404ae96e7db5', 936, 'node_modules/n8n-nodes-base/dist/nodes/Airtable/Airtable.node.js', false);
26 | 
27 | -- Node: n8n-nodes-base.Slack
28 | INSERT INTO nodes (node_type, name, package_name, code_hash, code_length, source_location, has_credentials)
29 | VALUES ('n8n-nodes-base.Slack', 'Slack', 'n8n-nodes-base', '0ed10d0646f3c595406359edfa2c293dac41991cee59ad4fb3ccf2bb70eca6fc', 1007, 'node_modules/n8n-nodes-base/dist/nodes/Slack/Slack.node.js', false);
30 | 
31 | -- Node: n8n-nodes-base.Discord
32 | INSERT INTO nodes (node_type, name, package_name, code_hash, code_length, source_location, has_credentials)
33 | VALUES ('n8n-nodes-base.Discord', 'Discord', 'n8n-nodes-base', '4995f9ca5c5b57d2486c2e320cc7505238e7f2260861f7e321b44b45ccabeb00', 10049, 'node_modules/n8n-nodes-base/dist/nodes/Discord/Discord.node.js', false);
34 | 
35 | 
```

--------------------------------------------------------------------------------
/src/mcp/tool-docs/validation/validate-workflow-connections.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { ToolDocumentation } from '../types';
 2 | 
 3 | export const validateWorkflowConnectionsDoc: ToolDocumentation = {
 4 |   name: 'validate_workflow_connections',
 5 |   category: 'validation',
 6 |   essentials: {
 7 |     description: 'Check workflow connections only: valid nodes, no cycles, proper triggers, AI tool links. Fast structure validation.',
 8 |     keyParameters: ['workflow'],
 9 |     example: 'validate_workflow_connections({workflow: {nodes: [...], connections: {...}}})',
10 |     performance: 'Fast (<100ms)',
11 |     tips: [
12 |       'Use for quick structure checks when editing connections',
13 |       'Detects orphaned nodes and circular dependencies',
14 |       'Validates AI Agent tool connections to ensure proper node references'
15 |     ]
16 |   },
17 |   full: {
18 |     description: 'Validates only the connection structure of a workflow without checking node configurations or expressions. This focused validation checks that all referenced nodes exist, detects circular dependencies, ensures proper trigger node placement, validates AI tool connections, and identifies orphaned or unreachable nodes.',
19 |     parameters: {
20 |       workflow: { 
21 |         type: 'object', 
22 |         required: true, 
23 |         description: 'The workflow JSON with nodes array and connections object.' 
24 |       }
25 |     },
26 |     returns: 'Object with valid (boolean), errors (array), warnings (array), and statistics about connections',
27 |     examples: [
28 |       'validate_workflow_connections({workflow: myWorkflow}) - Check all connections',
29 |       'validate_workflow_connections({workflow: {nodes: [...], connections: {...}}}) - Validate structure only'
30 |     ],
31 |     useCases: [
32 |       'Quick validation when modifying workflow connections',
33 |       'Ensure all node references in connections are valid',
34 |       'Detect circular dependencies that would cause infinite loops',
35 |       'Validate AI Agent nodes have proper tool connections',
36 |       'Check workflow has at least one trigger node',
37 |       'Find orphaned nodes not connected to any flow'
38 |     ],
39 |     performance: 'Fast (<100ms). Only validates structure, not node content. Scales linearly with connection count.',
40 |     bestPractices: [
41 |       'Run after adding or removing connections',
42 |       'Use before validate_workflow for quick structural checks',
43 |       'Check for warnings about orphaned nodes',
44 |       'Ensure trigger nodes are properly positioned',
45 |       'Validate after using n8n_update_partial_workflow with connection operations'
46 |     ],
47 |     pitfalls: [
48 |       'Does not validate node configurations - use validate_workflow for full validation',
49 |       'Cannot detect logical errors in connection flow',
50 |       'Some valid workflows may have intentionally disconnected nodes',
51 |       'Circular dependency detection only catches direct loops',
52 |       'Does not validate connection types match node capabilities'
53 |     ],
54 |     relatedTools: ['validate_workflow', 'validate_workflow_expressions', 'n8n_update_partial_workflow']
55 |   }
56 | };
```

--------------------------------------------------------------------------------
/src/mcp/tool-docs/workflow_management/n8n-list-workflows.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { ToolDocumentation } from '../types';
 2 | 
 3 | export const n8nListWorkflowsDoc: ToolDocumentation = {
 4 |   name: 'n8n_list_workflows',
 5 |   category: 'workflow_management',
 6 |   essentials: {
 7 |     description: 'List workflows (minimal metadata only - no nodes/connections). Supports pagination via cursor.',
 8 |     keyParameters: ['limit', 'active', 'tags'],
 9 |     example: 'n8n_list_workflows({limit: 20, active: true})',
10 |     performance: 'Fast (100-300ms)',
11 |     tips: [
12 |       'Use cursor for pagination',
13 |       'Filter by active status',
14 |       'Tag filtering for organization'
15 |     ]
16 |   },
17 |   full: {
18 |     description: 'Lists workflows from n8n with powerful filtering options. Returns ONLY minimal metadata (id, name, active, dates, tags, nodeCount) - no workflow structure, nodes, or connections. Use n8n_get_workflow to fetch full workflow details.',
19 |     parameters: {
20 |       limit: { type: 'number', description: 'Number of workflows to return (1-100, default: 100)' },
21 |       cursor: { type: 'string', description: 'Pagination cursor from previous response for next page' },
22 |       active: { type: 'boolean', description: 'Filter by active/inactive status' },
23 |       tags: { type: 'array', description: 'Filter by exact tag matches (AND logic)' },
24 |       projectId: { type: 'string', description: 'Filter by project ID (enterprise feature)' },
25 |       excludePinnedData: { type: 'boolean', description: 'Exclude pinned data from response (default: true)' }
26 |     },
27 |     returns: 'Object with: workflows array (minimal fields: id, name, active, createdAt, updatedAt, tags, nodeCount), returned (count in this response), hasMore (boolean), nextCursor (for pagination), and _note (guidance when more data exists)',
28 |     examples: [
29 |       'n8n_list_workflows({limit: 20}) - First 20 workflows',
30 |       'n8n_list_workflows({active: true, tags: ["production"]}) - Active production workflows',
31 |       'n8n_list_workflows({cursor: "abc123", limit: 50}) - Next page of results'
32 |     ],
33 |     useCases: [
34 |       'Build workflow dashboards',
35 |       'Find workflows by status',
36 |       'Organize by tags',
37 |       'Bulk workflow operations',
38 |       'Generate workflow reports'
39 |     ],
40 |     performance: 'Very fast - typically 50-200ms. Returns only minimal metadata without workflow structure.',
41 |     bestPractices: [
42 |       'Always check hasMore flag to determine if pagination is needed',
43 |       'Use cursor from previous response to get next page',
44 |       'The returned count is NOT the total in the system',
45 |       'Iterate with cursor until hasMore is false for complete list'
46 |     ],
47 |     pitfalls: [
48 |       'Requires N8N_API_URL and N8N_API_KEY configured',
49 |       'Maximum 100 workflows per request',
50 |       'Server may return fewer than requested limit',
51 |       'returned field is count of current page only, not system total'
52 |     ],
53 |     relatedTools: ['n8n_get_workflow_minimal', 'n8n_get_workflow', 'n8n_update_partial_workflow', 'n8n_list_executions']
54 |   }
55 | };
```

--------------------------------------------------------------------------------
/test-reinit-fix.sh:
--------------------------------------------------------------------------------

```bash
  1 | #!/bin/bash
  2 | 
  3 | # Test script to verify re-initialization fix works
  4 | 
  5 | echo "Starting n8n MCP server..."
  6 | AUTH_TOKEN=test123456789012345678901234567890 npm run start:http &
  7 | SERVER_PID=$!
  8 | 
  9 | # Wait for server to start
 10 | sleep 3
 11 | 
 12 | echo "Testing multiple initialize requests..."
 13 | 
 14 | # First initialize request
 15 | echo "1. First initialize request:"
 16 | RESPONSE1=$(curl -s -X POST http://localhost:3000/mcp \
 17 |   -H "Content-Type: application/json" \
 18 |   -H "Accept: application/json, text/event-stream" \
 19 |   -H "Authorization: Bearer test123456789012345678901234567890" \
 20 |   -d '{
 21 |     "jsonrpc": "2.0",
 22 |     "id": 1,
 23 |     "method": "initialize",
 24 |     "params": {
 25 |       "protocolVersion": "2024-11-05",
 26 |       "capabilities": {
 27 |         "roots": {
 28 |           "listChanged": false
 29 |         }
 30 |       },
 31 |       "clientInfo": {
 32 |         "name": "test-client-1",
 33 |         "version": "1.0.0"
 34 |       }
 35 |     }
 36 |   }')
 37 | 
 38 | if echo "$RESPONSE1" | grep -q '"result"'; then
 39 |     echo "✅ First initialize request succeeded"
 40 | else
 41 |     echo "❌ First initialize request failed: $RESPONSE1"
 42 | fi
 43 | 
 44 | # Second initialize request (this was failing before)
 45 | echo "2. Second initialize request (this was failing before the fix):"
 46 | RESPONSE2=$(curl -s -X POST http://localhost:3000/mcp \
 47 |   -H "Content-Type: application/json" \
 48 |   -H "Accept: application/json, text/event-stream" \
 49 |   -H "Authorization: Bearer test123456789012345678901234567890" \
 50 |   -d '{
 51 |     "jsonrpc": "2.0",
 52 |     "id": 2,
 53 |     "method": "initialize",
 54 |     "params": {
 55 |       "protocolVersion": "2024-11-05",
 56 |       "capabilities": {
 57 |         "roots": {
 58 |           "listChanged": false
 59 |         }
 60 |       },
 61 |       "clientInfo": {
 62 |         "name": "test-client-2",
 63 |         "version": "1.0.0"
 64 |       }
 65 |     }
 66 |   }')
 67 | 
 68 | if echo "$RESPONSE2" | grep -q '"result"'; then
 69 |     echo "✅ Second initialize request succeeded - FIX WORKING!"
 70 | else
 71 |     echo "❌ Second initialize request failed: $RESPONSE2"
 72 | fi
 73 | 
 74 | # Third initialize request to be sure
 75 | echo "3. Third initialize request:"
 76 | RESPONSE3=$(curl -s -X POST http://localhost:3000/mcp \
 77 |   -H "Content-Type: application/json" \
 78 |   -H "Accept: application/json, text/event-stream" \
 79 |   -H "Authorization: Bearer test123456789012345678901234567890" \
 80 |   -d '{
 81 |     "jsonrpc": "2.0",
 82 |     "id": 3,
 83 |     "method": "initialize",
 84 |     "params": {
 85 |       "protocolVersion": "2024-11-05",
 86 |       "capabilities": {
 87 |         "roots": {
 88 |           "listChanged": false
 89 |         }
 90 |       },
 91 |       "clientInfo": {
 92 |         "name": "test-client-3",
 93 |         "version": "1.0.0"
 94 |       }
 95 |     }
 96 |   }')
 97 | 
 98 | if echo "$RESPONSE3" | grep -q '"result"'; then
 99 |     echo "✅ Third initialize request succeeded"
100 | else
101 |     echo "❌ Third initialize request failed: $RESPONSE3"
102 | fi
103 | 
104 | # Check health to see active transports
105 | echo "4. Checking server health for active transports:"
106 | HEALTH=$(curl -s -X GET http://localhost:3000/health)
107 | echo "$HEALTH" | python3 -m json.tool
108 | 
109 | # Cleanup
110 | echo "Stopping server..."
111 | kill $SERVER_PID
112 | wait $SERVER_PID 2>/dev/null
113 | 
114 | echo "Test completed!"
```

--------------------------------------------------------------------------------
/src/mcp/tool-docs/validation/validate-workflow-expressions.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { ToolDocumentation } from '../types';
 2 | 
 3 | export const validateWorkflowExpressionsDoc: ToolDocumentation = {
 4 |   name: 'validate_workflow_expressions',
 5 |   category: 'validation',
 6 |   essentials: {
 7 |     description: 'Validate n8n expressions: syntax {{}}, variables ($json/$node), references. Returns errors with locations.',
 8 |     keyParameters: ['workflow'],
 9 |     example: 'validate_workflow_expressions({workflow: {nodes: [...], connections: {...}}})',
10 |     performance: 'Fast (<100ms)',
11 |     tips: [
12 |       'Catches syntax errors in {{}} expressions before runtime',
13 |       'Validates $json, $node, and other n8n variables',
14 |       'Shows exact location of expression errors in node parameters'
15 |     ]
16 |   },
17 |   full: {
18 |     description: 'Validates all n8n expressions within a workflow for syntax correctness and reference validity. This tool scans all node parameters for n8n expressions (enclosed in {{}}), checks expression syntax, validates variable references like $json and $node("NodeName"), ensures referenced nodes exist in the workflow, and provides detailed error locations for debugging.',
19 |     parameters: {
20 |       workflow: { 
21 |         type: 'object', 
22 |         required: true, 
23 |         description: 'The workflow JSON to check for expression errors.' 
24 |       }
25 |     },
26 |     returns: 'Object with valid (boolean), errors (array with node ID, parameter path, and error details), and expression count',
27 |     examples: [
28 |       'validate_workflow_expressions({workflow: myWorkflow}) - Check all expressions',
29 |       'validate_workflow_expressions({workflow: {nodes: [...], connections: {...}}}) - Validate expression syntax'
30 |     ],
31 |     useCases: [
32 |       'Catch expression syntax errors before workflow execution',
33 |       'Validate node references in $node() expressions exist',
34 |       'Find typos in variable names like $json or $input',
35 |       'Ensure complex expressions are properly formatted',
36 |       'Debug expression errors with exact parameter locations',
37 |       'Validate expressions after workflow modifications'
38 |     ],
39 |     performance: 'Fast (<100ms). Scans all string parameters in all nodes. Performance scales with workflow size and expression count.',
40 |     bestPractices: [
41 |       'Run after modifying any expressions in node parameters',
42 |       'Check all $node() references when renaming nodes',
43 |       'Validate expressions before workflow deployment',
44 |       'Pay attention to nested object paths in expressions',
45 |       'Use with validate_workflow for comprehensive validation'
46 |     ],
47 |     pitfalls: [
48 |       'Cannot validate expression logic, only syntax',
49 |       'Runtime data availability not checked (e.g., if $json.field exists)',
50 |       'Complex JavaScript in expressions may need runtime testing',
51 |       'Does not validate expression return types',
52 |       'Some valid expressions may use advanced features not fully parsed'
53 |     ],
54 |     relatedTools: ['validate_workflow', 'validate_workflow_connections', 'validate_node_operation']
55 |   }
56 | };
```

--------------------------------------------------------------------------------
/scripts/test-optimized-docker.sh:
--------------------------------------------------------------------------------

```bash
 1 | #!/bin/bash
 2 | # Test script for optimized Docker build
 3 | 
 4 | set -e
 5 | 
 6 | echo "🧪 Testing Optimized Docker Build"
 7 | echo "================================="
 8 | 
 9 | # Colors for output
10 | GREEN='\033[0;32m'
11 | RED='\033[0;31m'
12 | NC='\033[0m' # No Color
13 | 
14 | # Build the optimized image
15 | echo -e "\n📦 Building optimized Docker image..."
16 | docker build -f Dockerfile.optimized -t n8n-mcp:optimized .
17 | 
18 | # Check image size
19 | echo -e "\n📊 Checking image size..."
20 | SIZE=$(docker images n8n-mcp:optimized --format "{{.Size}}")
21 | echo "Image size: $SIZE"
22 | 
23 | # Extract size in MB for comparison
24 | SIZE_MB=$(docker images n8n-mcp:optimized --format "{{.Size}}" | sed 's/MB//' | sed 's/GB/*1024/' | bc 2>/dev/null || echo "0")
25 | if [ "$SIZE_MB" != "0" ] && [ "$SIZE_MB" -lt "300" ]; then
26 |     echo -e "${GREEN}✅ Image size is optimized (<300MB)${NC}"
27 | else
28 |     echo -e "${RED}⚠️  Image might be larger than expected${NC}"
29 | fi
30 | 
31 | # Test stdio mode
32 | echo -e "\n🔍 Testing stdio mode..."
33 | TEST_RESULT=$(echo '{"jsonrpc":"2.0","method":"tools/list","id":1}' | \
34 |     docker run --rm -i -e MCP_MODE=stdio n8n-mcp:optimized 2>/dev/null | \
35 |     grep -o '"name":"[^"]*"' | head -1)
36 | 
37 | if [ -n "$TEST_RESULT" ]; then
38 |     echo -e "${GREEN}✅ Stdio mode working${NC}"
39 | else
40 |     echo -e "${RED}❌ Stdio mode failed${NC}"
41 | fi
42 | 
43 | # Test HTTP mode
44 | echo -e "\n🌐 Testing HTTP mode..."
45 | docker run -d --name test-optimized \
46 |     -e MCP_MODE=http \
47 |     -e AUTH_TOKEN=test-token \
48 |     -p 3002:3000 \
49 |     n8n-mcp:optimized
50 | 
51 | # Wait for startup
52 | sleep 5
53 | 
54 | # Test health endpoint
55 | HEALTH=$(curl -s http://localhost:3002/health | grep -o '"status":"healthy"' || echo "")
56 | if [ -n "$HEALTH" ]; then
57 |     echo -e "${GREEN}✅ Health endpoint working${NC}"
58 | else
59 |     echo -e "${RED}❌ Health endpoint failed${NC}"
60 | fi
61 | 
62 | # Test MCP endpoint
63 | MCP_TEST=$(curl -s -H "Authorization: Bearer test-token" \
64 |     -H "Content-Type: application/json" \
65 |     -d '{"jsonrpc":"2.0","method":"tools/list","id":1}' \
66 |     http://localhost:3002/mcp | grep -o '"tools":\[' || echo "")
67 | 
68 | if [ -n "$MCP_TEST" ]; then
69 |     echo -e "${GREEN}✅ MCP endpoint working${NC}"
70 | else
71 |     echo -e "${RED}❌ MCP endpoint failed${NC}"
72 | fi
73 | 
74 | # Test database statistics tool
75 | STATS_TEST=$(curl -s -H "Authorization: Bearer test-token" \
76 |     -H "Content-Type: application/json" \
77 |     -d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"get_database_statistics","arguments":{}},"id":2}' \
78 |     http://localhost:3002/mcp | grep -o '"totalNodes":[0-9]*' || echo "")
79 | 
80 | if [ -n "$STATS_TEST" ]; then
81 |     echo -e "${GREEN}✅ Database statistics tool working${NC}"
82 |     echo "Database stats: $STATS_TEST"
83 | else
84 |     echo -e "${RED}❌ Database statistics tool failed${NC}"
85 | fi
86 | 
87 | # Cleanup
88 | docker stop test-optimized >/dev/null 2>&1
89 | docker rm test-optimized >/dev/null 2>&1
90 | 
91 | # Compare with original image
92 | echo -e "\n📊 Size Comparison:"
93 | echo "Original image: $(docker images n8n-mcp:latest --format "{{.Size}}" 2>/dev/null || echo "Not built")"
94 | echo "Optimized image: $SIZE"
95 | 
96 | echo -e "\n✨ Testing complete!"
```

--------------------------------------------------------------------------------
/tests/test-mcp-server-extraction.js:
--------------------------------------------------------------------------------

```javascript
  1 | #!/usr/bin/env node
  2 | 
  3 | /**
  4 |  * Test MCP Server extraction functionality
  5 |  * Simulates an MCP client calling the get_node_source_code tool
  6 |  */
  7 | 
  8 | const { spawn } = require('child_process');
  9 | const path = require('path');
 10 | 
 11 | // MCP request to get AI Agent node source code
 12 | const mcpRequest = {
 13 |   jsonrpc: '2.0',
 14 |   id: 1,
 15 |   method: 'tools/call',
 16 |   params: {
 17 |     name: 'get_node_source_code',
 18 |     arguments: {
 19 |       nodeType: '@n8n/n8n-nodes-langchain.Agent',
 20 |       includeCredentials: true
 21 |     }
 22 |   }
 23 | };
 24 | 
 25 | async function testMCPExtraction() {
 26 |   console.log('=== MCP Server Node Extraction Test ===\n');
 27 |   console.log('Starting MCP server...');
 28 |   
 29 |   // Start the MCP server
 30 |   const serverPath = path.join(__dirname, '../dist/index.js');
 31 |   const server = spawn('node', [serverPath], {
 32 |     env: {
 33 |       ...process.env,
 34 |       MCP_SERVER_PORT: '3000',
 35 |       MCP_SERVER_HOST: '0.0.0.0',
 36 |       N8N_API_URL: 'http://n8n:5678',
 37 |       N8N_API_KEY: 'test-api-key',
 38 |       MCP_AUTH_TOKEN: 'test-token',
 39 |       LOG_LEVEL: 'info'
 40 |     },
 41 |     stdio: ['pipe', 'pipe', 'pipe']
 42 |   });
 43 | 
 44 |   let responseBuffer = '';
 45 |   let errorBuffer = '';
 46 | 
 47 |   server.stdout.on('data', (data) => {
 48 |     responseBuffer += data.toString();
 49 |   });
 50 | 
 51 |   server.stderr.on('data', (data) => {
 52 |     errorBuffer += data.toString();
 53 |   });
 54 | 
 55 |   // Give server time to start
 56 |   await new Promise(resolve => setTimeout(resolve, 2000));
 57 | 
 58 |   console.log('Sending MCP request...');
 59 |   console.log(JSON.stringify(mcpRequest, null, 2));
 60 |   
 61 |   // Send the request via stdin (MCP uses stdio transport)
 62 |   server.stdin.write(JSON.stringify(mcpRequest) + '\n');
 63 |   
 64 |   // Wait for response
 65 |   await new Promise(resolve => setTimeout(resolve, 3000));
 66 |   
 67 |   // Kill the server
 68 |   server.kill();
 69 |   
 70 |   console.log('\n=== Server Output ===');
 71 |   console.log(responseBuffer);
 72 |   
 73 |   if (errorBuffer) {
 74 |     console.log('\n=== Server Errors ===');
 75 |     console.log(errorBuffer);
 76 |   }
 77 |   
 78 |   // Try to parse any JSON responses
 79 |   const lines = responseBuffer.split('\n').filter(line => line.trim());
 80 |   for (const line of lines) {
 81 |     try {
 82 |       const data = JSON.parse(line);
 83 |       if (data.id === 1 && data.result) {
 84 |         console.log('\n✅ MCP Response received!');
 85 |         console.log(`Node type: ${data.result.nodeType}`);
 86 |         console.log(`Source code length: ${data.result.sourceCode ? data.result.sourceCode.length : 0} characters`);
 87 |         console.log(`Location: ${data.result.location}`);
 88 |         console.log(`Has credentials: ${data.result.credentialCode ? 'Yes' : 'No'}`);
 89 |         console.log(`Has package info: ${data.result.packageInfo ? 'Yes' : 'No'}`);
 90 |         
 91 |         if (data.result.sourceCode) {
 92 |           console.log('\nFirst 300 characters of extracted code:');
 93 |           console.log('='.repeat(60));
 94 |           console.log(data.result.sourceCode.substring(0, 300) + '...');
 95 |           console.log('='.repeat(60));
 96 |         }
 97 |       }
 98 |     } catch (e) {
 99 |       // Not JSON, skip
100 |     }
101 |   }
102 | }
103 | 
104 | testMCPExtraction().catch(console.error);
```

--------------------------------------------------------------------------------
/scripts/audit-schema-coverage.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Database Schema Coverage Audit Script
 3 |  *
 4 |  * Audits the database to determine how many nodes have complete schema information
 5 |  * for resourceLocator mode validation. This helps assess the coverage of our
 6 |  * schema-driven validation approach.
 7 |  */
 8 | 
 9 | import Database from 'better-sqlite3';
10 | import path from 'path';
11 | 
12 | const dbPath = path.join(__dirname, '../data/nodes.db');
13 | const db = new Database(dbPath, { readonly: true });
14 | 
15 | console.log('=== Schema Coverage Audit ===\n');
16 | 
17 | // Query 1: How many nodes have resourceLocator properties?
18 | const totalResourceLocator = db.prepare(`
19 |   SELECT COUNT(*) as count FROM nodes
20 |   WHERE properties_schema LIKE '%resourceLocator%'
21 | `).get() as { count: number };
22 | 
23 | console.log(`Nodes with resourceLocator properties: ${totalResourceLocator.count}`);
24 | 
25 | // Query 2: Of those, how many have modes defined?
26 | const withModes = db.prepare(`
27 |   SELECT COUNT(*) as count FROM nodes
28 |   WHERE properties_schema LIKE '%resourceLocator%'
29 |     AND properties_schema LIKE '%modes%'
30 | `).get() as { count: number };
31 | 
32 | console.log(`Nodes with modes defined: ${withModes.count}`);
33 | 
34 | // Query 3: Which nodes have resourceLocator but NO modes?
35 | const withoutModes = db.prepare(`
36 |   SELECT node_type, display_name
37 |   FROM nodes
38 |   WHERE properties_schema LIKE '%resourceLocator%'
39 |     AND properties_schema NOT LIKE '%modes%'
40 |   LIMIT 10
41 | `).all() as Array<{ node_type: string; display_name: string }>;
42 | 
43 | console.log(`\nSample nodes WITHOUT modes (showing 10):`);
44 | withoutModes.forEach(node => {
45 |   console.log(`  - ${node.display_name} (${node.node_type})`);
46 | });
47 | 
48 | // Calculate coverage percentage
49 | const coverage = totalResourceLocator.count > 0
50 |   ? (withModes.count / totalResourceLocator.count) * 100
51 |   : 0;
52 | 
53 | console.log(`\nSchema coverage: ${coverage.toFixed(1)}% of resourceLocator nodes have modes defined`);
54 | 
55 | // Query 4: Get some examples of nodes WITH modes for verification
56 | console.log('\nSample nodes WITH modes (showing 5):');
57 | const withModesExamples = db.prepare(`
58 |   SELECT node_type, display_name
59 |   FROM nodes
60 |   WHERE properties_schema LIKE '%resourceLocator%'
61 |     AND properties_schema LIKE '%modes%'
62 |   LIMIT 5
63 | `).all() as Array<{ node_type: string; display_name: string }>;
64 | 
65 | withModesExamples.forEach(node => {
66 |   console.log(`  - ${node.display_name} (${node.node_type})`);
67 | });
68 | 
69 | // Summary
70 | console.log('\n=== Summary ===');
71 | console.log(`Total nodes in database: ${db.prepare('SELECT COUNT(*) as count FROM nodes').get() as any as { count: number }.count}`);
72 | console.log(`Nodes with resourceLocator: ${totalResourceLocator.count}`);
73 | console.log(`Nodes with complete mode schemas: ${withModes.count}`);
74 | console.log(`Nodes without mode schemas: ${totalResourceLocator.count - withModes.count}`);
75 | console.log(`\nImplication: Schema-driven validation will apply to ${withModes.count} nodes.`);
76 | console.log(`For the remaining ${totalResourceLocator.count - withModes.count} nodes, validation will be skipped (graceful degradation).`);
77 | 
78 | db.close();
79 | 
```

--------------------------------------------------------------------------------
/src/scripts/debug-http-search.ts:
--------------------------------------------------------------------------------

```typescript
 1 | #!/usr/bin/env npx tsx
 2 | 
 3 | import { createDatabaseAdapter } from '../database/database-adapter';
 4 | import { NodeRepository } from '../database/node-repository';
 5 | import { NodeSimilarityService } from '../services/node-similarity-service';
 6 | import path from 'path';
 7 | 
 8 | async function debugHttpSearch() {
 9 |   const dbPath = path.join(process.cwd(), 'data/nodes.db');
10 |   const db = await createDatabaseAdapter(dbPath);
11 |   const repository = new NodeRepository(db);
12 |   const service = new NodeSimilarityService(repository);
13 | 
14 |   console.log('Testing "http" search...\n');
15 | 
16 |   // Check if httpRequest exists
17 |   const httpNode = repository.getNode('nodes-base.httpRequest');
18 |   console.log('HTTP Request node exists:', httpNode ? 'Yes' : 'No');
19 |   if (httpNode) {
20 |     console.log('  Display name:', httpNode.displayName);
21 |   }
22 | 
23 |   // Test the search with internal details
24 |   const suggestions = await service.findSimilarNodes('http', 5);
25 |   console.log('\nSuggestions for "http":', suggestions.length);
26 |   suggestions.forEach(s => {
27 |     console.log(`  - ${s.nodeType} (${Math.round(s.confidence * 100)}%)`);
28 |   });
29 | 
30 |   // Manually calculate score for httpRequest
31 |   console.log('\nManual score calculation for httpRequest:');
32 |   const testNode = {
33 |     nodeType: 'nodes-base.httpRequest',
34 |     displayName: 'HTTP Request',
35 |     category: 'Core Nodes'
36 |   };
37 | 
38 |   const cleanInvalid = 'http';
39 |   const cleanValid = 'nodesbasehttprequest';
40 |   const displayNameClean = 'httprequest';
41 | 
42 |   // Check substring
43 |   const hasSubstring = cleanValid.includes(cleanInvalid) || displayNameClean.includes(cleanInvalid);
44 |   console.log(`  Substring match: ${hasSubstring}`);
45 | 
46 |   // This should give us pattern match score
47 |   const patternScore = hasSubstring ? 35 : 0; // Using 35 for short searches
48 |   console.log(`  Pattern score: ${patternScore}`);
49 | 
50 |   // Name similarity would be low
51 |   console.log(`  Total score would need to be >= 50 to appear`);
52 | 
53 |   // Get all nodes and check which ones contain 'http'
54 |   const allNodes = repository.getAllNodes();
55 |   const httpNodes = allNodes.filter(n =>
56 |     n.nodeType.toLowerCase().includes('http') ||
57 |     (n.displayName && n.displayName.toLowerCase().includes('http'))
58 |   );
59 | 
60 |   console.log('\n\nNodes containing "http" in name:');
61 |   httpNodes.slice(0, 5).forEach(n => {
62 |     console.log(`  - ${n.nodeType} (${n.displayName})`);
63 | 
64 |     // Calculate score for this node
65 |     const normalizedSearch = 'http';
66 |     const normalizedType = n.nodeType.toLowerCase().replace(/[^a-z0-9]/g, '');
67 |     const normalizedDisplay = (n.displayName || '').toLowerCase().replace(/[^a-z0-9]/g, '');
68 | 
69 |     const containsInType = normalizedType.includes(normalizedSearch);
70 |     const containsInDisplay = normalizedDisplay.includes(normalizedSearch);
71 | 
72 |     console.log(`    Type check: "${normalizedType}" includes "${normalizedSearch}" = ${containsInType}`);
73 |     console.log(`    Display check: "${normalizedDisplay}" includes "${normalizedSearch}" = ${containsInDisplay}`);
74 |   });
75 | }
76 | 
77 | debugHttpSearch().catch(console.error);
```

--------------------------------------------------------------------------------
/src/mcp/tool-docs/workflow_management/n8n-delete-execution.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { ToolDocumentation } from '../types';
 2 | 
 3 | export const n8nDeleteExecutionDoc: ToolDocumentation = {
 4 |   name: 'n8n_delete_execution',
 5 |   category: 'workflow_management',
 6 |   essentials: {
 7 |     description: 'Delete an execution record. This only removes the execution history, not any data processed.',
 8 |     keyParameters: ['id'],
 9 |     example: 'n8n_delete_execution({id: "12345"})',
10 |     performance: 'Immediate deletion, no undo available',
11 |     tips: [
12 |       'Deletion is permanent - execution cannot be recovered',
13 |       'Only removes execution history, not external data changes',
14 |       'Use for cleanup of test executions or sensitive data'
15 |     ]
16 |   },
17 |   full: {
18 |     description: `Permanently deletes a workflow execution record from n8n's history. This removes the execution metadata, logs, and any stored input/output data. However, it does NOT undo any actions the workflow performed (API calls, database changes, file operations, etc.). Use this for cleaning up test executions, removing sensitive data, or managing storage.`,
19 |     parameters: {
20 |       id: {
21 |         type: 'string',
22 |         required: true,
23 |         description: 'The execution ID to delete. This action cannot be undone'
24 |       }
25 |     },
26 |     returns: `Confirmation of deletion or error if execution not found. No data is returned about the deleted execution.`,
27 |     examples: [
28 |       'n8n_delete_execution({id: "12345"}) - Delete a specific execution',
29 |       'n8n_delete_execution({id: "test-run-567"}) - Clean up test execution',
30 |       'n8n_delete_execution({id: "sensitive-data-890"}) - Remove execution with sensitive data',
31 |       'n8n_delete_execution({id: "failed-execution-123"}) - Delete failed execution after debugging'
32 |     ],
33 |     useCases: [
34 |       'Clean up test or development execution history',
35 |       'Remove executions containing sensitive or personal data',
36 |       'Manage storage by deleting old execution records',
37 |       'Clean up after debugging failed workflows',
38 |       'Comply with data retention policies'
39 |     ],
40 |     performance: `Deletion is immediate and permanent. The operation is fast (< 100ms) as it only removes database records. No external systems or data are affected.`,
41 |     bestPractices: [
42 |       'Verify execution ID before deletion - action cannot be undone',
43 |       'Consider exporting execution data before deletion if needed',
44 |       'Use list_executions to find executions to delete',
45 |       'Document why executions were deleted for audit trails',
46 |       'Remember deletion only affects n8n records, not external changes'
47 |     ],
48 |     pitfalls: [
49 |       'Deletion is PERMANENT - no undo or recovery possible',
50 |       'Does NOT reverse workflow actions (API calls, DB changes, etc.)',
51 |       'Deleting executions breaks audit trails and debugging history',
52 |       'Cannot delete currently running executions (waiting status)',
53 |       'Bulk deletion not supported - must delete one at a time'
54 |     ],
55 |     relatedTools: ['n8n_list_executions', 'n8n_get_execution', 'n8n_trigger_webhook_workflow']
56 |   }
57 | };
```

--------------------------------------------------------------------------------
/src/mcp/tool-docs/discovery/list-ai-tools.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { ToolDocumentation } from '../types';
 2 | 
 3 | export const listAiToolsDoc: ToolDocumentation = {
 4 |   name: 'list_ai_tools',
 5 |   category: 'discovery',
 6 |   essentials: {
 7 |     description: 'DEPRECATED: Basic list of 263 AI nodes. For comprehensive AI Agent guidance, use tools_documentation({topic: "ai_agents_guide"}). That guide covers architecture, connections, tools, validation, and best practices. Use search_nodes({query: "AI", includeExamples: true}) for AI nodes with working examples.',
 8 |     keyParameters: [],
 9 |     example: 'tools_documentation({topic: "ai_agents_guide"}) // Recommended alternative',
10 |     performance: 'Instant (cached)',
11 |     tips: [
12 |       'NEW: Use ai_agents_guide for comprehensive AI workflow documentation',
13 |       'Use search_nodes({includeExamples: true}) for AI nodes with real-world examples',
14 |       'ANY node can be an AI tool - not limited to AI-specific nodes',
15 |       'Use get_node_as_tool_info for guidance on any node'
16 |     ]
17 |   },
18 |   full: {
19 |     description: '**DEPRECATED in favor of ai_agents_guide**. Lists 263 nodes with built-in AI capabilities. For comprehensive documentation on building AI Agent workflows, use tools_documentation({topic: "ai_agents_guide"}) which covers architecture, the 8 AI connection types, validation, and best practices with real examples. IMPORTANT: This basic list is NOT a complete guide - use the full AI Agents guide instead.',
20 |     parameters: {},
21 |     returns: 'Array of 263 AI-optimized nodes. RECOMMENDED: Use ai_agents_guide for comprehensive guidance, or search_nodes({query: "AI", includeExamples: true}) for AI nodes with working configuration examples.',
22 |     examples: [
23 |       '// RECOMMENDED: Use the comprehensive AI Agents guide',
24 |       'tools_documentation({topic: "ai_agents_guide"})',
25 |       '',
26 |       '// Or search for AI nodes with real-world examples',
27 |       'search_nodes({query: "AI Agent", includeExamples: true})',
28 |       '',
29 |       '// Basic list (deprecated)',
30 |       'list_ai_tools() - Returns 263 AI-optimized nodes'
31 |     ],
32 |     useCases: [
33 |       'Discover AI model integrations (OpenAI, Anthropic, Google AI)',
34 |       'Find vector databases for RAG applications',
35 |       'Locate embedding generators and processors',
36 |       'Build AI agent tool chains with ANY n8n node'
37 |     ],
38 |     performance: 'Instant - results are pre-cached in memory',
39 |     bestPractices: [
40 |       'Remember: ANY node works as an AI tool when connected to AI Agent',
41 |       'Common non-AI nodes used as tools: Slack (messaging), Google Sheets (data), HTTP Request (APIs), Code (custom logic)',
42 |       'For community nodes: set N8N_COMMUNITY_PACKAGES_ALLOW_TOOL_USAGE=true'
43 |     ],
44 |     pitfalls: [
45 |       'This list is NOT exhaustive - it only shows nodes with AI-specific features',
46 |       'Don\'t limit yourself to this list when building AI workflows',
47 |       'Community nodes require environment variable to work as tools'
48 |     ],
49 |     relatedTools: ['get_node_as_tool_info for any node usage', 'search_nodes to find specific nodes', 'get_node_essentials to configure nodes']
50 |   }
51 | };
```

--------------------------------------------------------------------------------
/scripts/test-sqljs-triggers.ts:
--------------------------------------------------------------------------------

```typescript
 1 | #!/usr/bin/env node
 2 | /**
 3 |  * Test script to verify trigger detection works with sql.js adapter
 4 |  */
 5 | 
 6 | import { createDatabaseAdapter } from '../src/database/database-adapter';
 7 | import { NodeRepository } from '../src/database/node-repository';
 8 | import { logger } from '../src/utils/logger';
 9 | import path from 'path';
10 | 
11 | async function testSqlJsTriggers() {
12 |   logger.info('🧪 Testing trigger detection with sql.js adapter...\n');
13 |   
14 |   try {
15 |     // Force sql.js by temporarily renaming better-sqlite3
16 |     const originalRequire = require.cache[require.resolve('better-sqlite3')];
17 |     if (originalRequire) {
18 |       delete require.cache[require.resolve('better-sqlite3')];
19 |     }
20 |     
21 |     // Mock better-sqlite3 to force sql.js usage
22 |     const Module = require('module');
23 |     const originalResolveFilename = Module._resolveFilename;
24 |     Module._resolveFilename = function(request: string, parent: any, isMain: boolean) {
25 |       if (request === 'better-sqlite3') {
26 |         throw new Error('Forcing sql.js adapter for testing');
27 |       }
28 |       return originalResolveFilename.apply(this, arguments);
29 |     };
30 |     
31 |     // Now create adapter - should use sql.js
32 |     const dbPath = path.join(process.cwd(), 'data', 'nodes.db');
33 |     logger.info(`📁 Database path: ${dbPath}`);
34 |     
35 |     const adapter = await createDatabaseAdapter(dbPath);
36 |     logger.info('✅ Adapter created (should be sql.js)\n');
37 |     
38 |     // Test direct query
39 |     logger.info('📊 Testing direct database query:');
40 |     const triggerNodes = ['nodes-base.webhook', 'nodes-base.cron', 'nodes-base.interval', 'nodes-base.emailReadImap'];
41 |     
42 |     for (const nodeType of triggerNodes) {
43 |       const row = adapter.prepare('SELECT * FROM nodes WHERE node_type = ?').get(nodeType);
44 |       if (row) {
45 |         logger.info(`${nodeType}:`);
46 |         logger.info(`  is_trigger raw value: ${row.is_trigger} (type: ${typeof row.is_trigger})`);
47 |         logger.info(`  !!is_trigger: ${!!row.is_trigger}`);
48 |         logger.info(`  Number(is_trigger) === 1: ${Number(row.is_trigger) === 1}`);
49 |       }
50 |     }
51 |     
52 |     // Test through repository
53 |     logger.info('\n📦 Testing through NodeRepository:');
54 |     const repository = new NodeRepository(adapter);
55 |     
56 |     for (const nodeType of triggerNodes) {
57 |       const node = repository.getNode(nodeType);
58 |       if (node) {
59 |         logger.info(`${nodeType}: isTrigger = ${node.isTrigger}`);
60 |       }
61 |     }
62 |     
63 |     // Test list query
64 |     logger.info('\n📋 Testing list query:');
65 |     const allTriggers = adapter.prepare(
66 |       'SELECT node_type, is_trigger FROM nodes WHERE node_type IN (?, ?, ?, ?)'
67 |     ).all(...triggerNodes);
68 |     
69 |     for (const node of allTriggers) {
70 |       logger.info(`${node.node_type}: is_trigger = ${node.is_trigger} (type: ${typeof node.is_trigger})`);
71 |     }
72 |     
73 |     adapter.close();
74 |     logger.info('\n✅ Test complete!');
75 |     
76 |     // Restore original require
77 |     Module._resolveFilename = originalResolveFilename;
78 |     
79 |   } catch (error) {
80 |     logger.error('Test failed:', error);
81 |     process.exit(1);
82 |   }
83 | }
84 | 
85 | // Run test
86 | testSqlJsTriggers().catch(console.error);
```

--------------------------------------------------------------------------------
/scripts/extract-nodes-simple.sh:
--------------------------------------------------------------------------------

```bash
  1 | #!/bin/bash
  2 | set -e
  3 | 
  4 | echo "🐳 Simple n8n Node Extraction via Docker"
  5 | echo "======================================="
  6 | 
  7 | # Colors for output
  8 | GREEN='\033[0;32m'
  9 | YELLOW='\033[1;33m'
 10 | RED='\033[0;31m'
 11 | NC='\033[0m' # No Color
 12 | 
 13 | # Function to print colored output
 14 | print_status() {
 15 |     echo -e "${GREEN}[$(date +'%H:%M:%S')]${NC} $1"
 16 | }
 17 | 
 18 | print_warning() {
 19 |     echo -e "${YELLOW}[$(date +'%H:%M:%S')]${NC} ⚠️  $1"
 20 | }
 21 | 
 22 | print_error() {
 23 |     echo -e "${RED}[$(date +'%H:%M:%S')]${NC} ❌ $1"
 24 | }
 25 | 
 26 | # Check if Docker is running
 27 | if ! docker info > /dev/null 2>&1; then
 28 |     print_error "Docker is not running. Please start Docker and try again."
 29 |     exit 1
 30 | fi
 31 | 
 32 | print_status "Docker is running ✅"
 33 | 
 34 | # Build the project first
 35 | print_status "Building the project..."
 36 | npm run build
 37 | 
 38 | # Create a temporary directory for extraction
 39 | TEMP_DIR=$(mktemp -d)
 40 | print_status "Created temporary directory: $TEMP_DIR"
 41 | 
 42 | # Run Docker container to copy node files
 43 | print_status "Running n8n container to extract nodes..."
 44 | docker run --rm -d --name n8n-temp n8nio/n8n:latest sleep 300
 45 | 
 46 | # Wait a bit for container to start
 47 | sleep 5
 48 | 
 49 | # Copy n8n modules from container
 50 | print_status "Copying n8n modules from container..."
 51 | docker cp n8n-temp:/usr/local/lib/node_modules/n8n/node_modules "$TEMP_DIR/node_modules" || {
 52 |     print_error "Failed to copy node_modules"
 53 |     docker stop n8n-temp
 54 |     rm -rf "$TEMP_DIR"
 55 |     exit 1
 56 | }
 57 | 
 58 | # Stop the container
 59 | docker stop n8n-temp
 60 | 
 61 | # Run our extraction script locally
 62 | print_status "Running extraction script..."
 63 | NODE_ENV=development \
 64 | NODE_DB_PATH=./data/nodes-fresh.db \
 65 | N8N_MODULES_PATH="$TEMP_DIR/node_modules" \
 66 | node scripts/extract-from-docker.js
 67 | 
 68 | # Clean up
 69 | print_status "Cleaning up temporary files..."
 70 | rm -rf "$TEMP_DIR"
 71 | 
 72 | # Check the results
 73 | print_status "Checking extraction results..."
 74 | if [ -f "./data/nodes-fresh.db" ]; then
 75 |     NODE_COUNT=$(sqlite3 ./data/nodes-fresh.db "SELECT COUNT(*) FROM nodes;" 2>/dev/null || echo "0")
 76 |     print_status "Extracted $NODE_COUNT nodes"
 77 |     
 78 |     # Check if we got the If node source code and look for version
 79 |     IF_SOURCE=$(sqlite3 ./data/nodes-fresh.db "SELECT source_code FROM nodes WHERE node_type='n8n-nodes-base.If' LIMIT 1;" 2>/dev/null || echo "")
 80 |     if [[ $IF_SOURCE =~ version:[[:space:]]*([0-9]+) ]]; then
 81 |         IF_CODE_VERSION="${BASH_REMATCH[1]}"
 82 |         print_status "If node version from source code: v$IF_CODE_VERSION"
 83 |         
 84 |         if [ "$IF_CODE_VERSION" -ge "2" ]; then
 85 |             print_status "✅ Successfully extracted latest If node (v$IF_CODE_VERSION)!"
 86 |         else
 87 |             print_warning "If node is still v$IF_CODE_VERSION, expected v2 or higher"
 88 |         fi
 89 |     fi
 90 | else
 91 |     print_error "Database file not found after extraction"
 92 | fi
 93 | 
 94 | print_status "✨ Extraction complete!"
 95 | 
 96 | # Offer to restart the MCP server
 97 | echo ""
 98 | read -p "Would you like to restart the MCP server with the new nodes? (y/n) " -n 1 -r
 99 | echo ""
100 | if [[ $REPLY =~ ^[Yy]$ ]]; then
101 |     print_status "Restarting MCP server..."
102 |     # Kill any existing server process
103 |     pkill -f "node.*dist/index.js" || true
104 |     
105 |     # Start the server
106 |     npm start &
107 |     print_status "MCP server restarted with fresh node database"
108 | fi
```

--------------------------------------------------------------------------------
/tests/unit/http-server-n8n-reinit.test.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { describe, it, expect, beforeEach, afterEach } from 'vitest';
  2 | import { SingleSessionHTTPServer } from '../../src/http-server-single-session';
  3 | import express from 'express';
  4 | 
  5 | describe('HTTP Server n8n Re-initialization', () => {
  6 |   let server: SingleSessionHTTPServer;
  7 |   let app: express.Application;
  8 | 
  9 |   beforeEach(() => {
 10 |     // Set required environment variables for testing
 11 |     process.env.AUTH_TOKEN = 'test-token-32-chars-minimum-length-for-security';
 12 |     process.env.NODE_DB_PATH = ':memory:';
 13 |   });
 14 | 
 15 |   afterEach(async () => {
 16 |     if (server) {
 17 |       await server.shutdown();
 18 |     }
 19 |     // Clean up environment
 20 |     delete process.env.AUTH_TOKEN;
 21 |     delete process.env.NODE_DB_PATH;
 22 |   });
 23 | 
 24 |   it('should handle re-initialization requests gracefully', async () => {
 25 |     // Create mock request and response
 26 |     const mockReq = {
 27 |       method: 'POST',
 28 |       url: '/mcp',
 29 |       headers: {},
 30 |       body: {
 31 |         jsonrpc: '2.0',
 32 |         id: 1,
 33 |         method: 'initialize',
 34 |         params: {
 35 |           protocolVersion: '2024-11-05',
 36 |           capabilities: { tools: {} },
 37 |           clientInfo: { name: 'n8n', version: '1.0.0' }
 38 |         }
 39 |       },
 40 |       get: (header: string) => {
 41 |         if (header === 'user-agent') return 'test-agent';
 42 |         if (header === 'content-length') return '100';
 43 |         if (header === 'content-type') return 'application/json';
 44 |         return undefined;
 45 |       },
 46 |       ip: '127.0.0.1'
 47 |     } as any;
 48 | 
 49 |     const mockRes = {
 50 |       headersSent: false,
 51 |       statusCode: 200,
 52 |       finished: false,
 53 |       status: (code: number) => mockRes,
 54 |       json: (data: any) => mockRes,
 55 |       setHeader: (name: string, value: string) => mockRes,
 56 |       end: () => mockRes
 57 |     } as any;
 58 | 
 59 |     try {
 60 |       server = new SingleSessionHTTPServer();
 61 |       
 62 |       // First request should work
 63 |       await server.handleRequest(mockReq, mockRes);
 64 |       expect(mockRes.statusCode).toBe(200);
 65 |       
 66 |       // Second request (re-initialization) should also work
 67 |       mockReq.body.id = 2;
 68 |       await server.handleRequest(mockReq, mockRes);
 69 |       expect(mockRes.statusCode).toBe(200);
 70 |       
 71 |     } catch (error) {
 72 |       // This test mainly ensures the logic doesn't throw errors
 73 |       // The actual MCP communication would need a more complex setup
 74 |       console.log('Expected error in unit test environment:', error);
 75 |       expect(error).toBeDefined(); // We expect some error due to simplified mock setup
 76 |     }
 77 |   });
 78 | 
 79 |   it('should identify initialize requests correctly', () => {
 80 |     const initializeRequest = {
 81 |       jsonrpc: '2.0',
 82 |       id: 1,
 83 |       method: 'initialize',
 84 |       params: {}
 85 |     };
 86 | 
 87 |     const nonInitializeRequest = {
 88 |       jsonrpc: '2.0',
 89 |       id: 1,
 90 |       method: 'tools/list'
 91 |     };
 92 | 
 93 |     // Test the logic we added for detecting initialize requests
 94 |     const isInitReq1 = initializeRequest && 
 95 |       initializeRequest.method === 'initialize' && 
 96 |       initializeRequest.jsonrpc === '2.0';
 97 |     
 98 |     const isInitReq2 = nonInitializeRequest && 
 99 |       nonInitializeRequest.method === 'initialize' && 
100 |       nonInitializeRequest.jsonrpc === '2.0';
101 | 
102 |     expect(isInitReq1).toBe(true);
103 |     expect(isInitReq2).toBe(false);
104 |   });
105 | });
```

--------------------------------------------------------------------------------
/scripts/test-nodes-base-prefix.ts:
--------------------------------------------------------------------------------

```typescript
  1 | #!/usr/bin/env tsx
  2 | 
  3 | /**
  4 |  * Specific test for nodes-base. prefix validation
  5 |  */
  6 | 
  7 | import { WorkflowValidator } from '../src/services/workflow-validator';
  8 | import { EnhancedConfigValidator } from '../src/services/enhanced-config-validator';
  9 | import { NodeRepository } from '../src/database/node-repository';
 10 | import { createDatabaseAdapter } from '../src/database/database-adapter';
 11 | import { Logger } from '../src/utils/logger';
 12 | 
 13 | const logger = new Logger({ prefix: '[TestNodesBasePrefix]' });
 14 | 
 15 | async function testValidation() {
 16 |   const adapter = await createDatabaseAdapter('./data/nodes.db');
 17 |   const repository = new NodeRepository(adapter);
 18 |   const validator = new WorkflowValidator(repository, EnhancedConfigValidator);
 19 | 
 20 |   logger.info('Testing nodes-base. prefix validation...\n');
 21 | 
 22 |   // Test various nodes-base. prefixed types
 23 |   const testCases = [
 24 |     { type: 'nodes-base.webhook', expected: 'n8n-nodes-base.webhook' },
 25 |     { type: 'nodes-base.httpRequest', expected: 'n8n-nodes-base.httpRequest' },
 26 |     { type: 'nodes-base.set', expected: 'n8n-nodes-base.set' },
 27 |     { type: 'nodes-base.code', expected: 'n8n-nodes-base.code' },
 28 |     { type: 'nodes-base.slack', expected: 'n8n-nodes-base.slack' },
 29 |   ];
 30 | 
 31 |   for (const testCase of testCases) {
 32 |     const workflow = {
 33 |       name: `Test ${testCase.type}`,
 34 |       nodes: [{
 35 |         id: 'test-node',
 36 |         name: 'Test Node',
 37 |         type: testCase.type,
 38 |         typeVersion: 1,
 39 |         position: [100, 100] as [number, number],
 40 |         parameters: {}
 41 |       }],
 42 |       connections: {}
 43 |     };
 44 | 
 45 |     logger.info(`Testing: "${testCase.type}"`);
 46 |     const result = await validator.validateWorkflow(workflow as any);
 47 |     
 48 |     const nodeTypeError = result.errors.find(err => 
 49 |       err && typeof err === 'object' && 'message' in err && 
 50 |       err.message.includes(testCase.type) && 
 51 |       err.message.includes(testCase.expected)
 52 |     );
 53 | 
 54 |     if (nodeTypeError) {
 55 |       logger.info(`✅ Caught and suggested: "${testCase.expected}"`);
 56 |     } else {
 57 |       logger.error(`❌ Failed to catch invalid type: "${testCase.type}"`);
 58 |       result.errors.forEach(err => {
 59 |         if (err && typeof err === 'object' && 'message' in err) {
 60 |           logger.error(`   Error: ${err.message}`);
 61 |         }
 62 |       });
 63 |     }
 64 |   }
 65 | 
 66 |   // Test that n8n-nodes-base. prefix still works
 67 |   const validWorkflow = {
 68 |     name: 'Valid Workflow',
 69 |     nodes: [{
 70 |       id: 'webhook',
 71 |       name: 'Webhook',
 72 |       type: 'n8n-nodes-base.webhook',
 73 |       typeVersion: 2,
 74 |       position: [100, 100] as [number, number],
 75 |       parameters: {}
 76 |     }],
 77 |     connections: {}
 78 |   };
 79 | 
 80 |   logger.info('\nTesting valid n8n-nodes-base.webhook:');
 81 |   const validResult = await validator.validateWorkflow(validWorkflow as any);
 82 |   
 83 |   const hasNodeTypeError = validResult.errors.some(err => 
 84 |     err && typeof err === 'object' && 'message' in err && 
 85 |     err.message.includes('node type')
 86 |   );
 87 | 
 88 |   if (!hasNodeTypeError) {
 89 |     logger.info('✅ Correctly accepted n8n-nodes-base.webhook');
 90 |   } else {
 91 |     logger.error('❌ Incorrectly rejected valid n8n-nodes-base.webhook');
 92 |   }
 93 | 
 94 |   adapter.close();
 95 | }
 96 | 
 97 | testValidation().catch(err => {
 98 |   logger.error('Test failed:', err);
 99 |   process.exit(1);
100 | });
```

--------------------------------------------------------------------------------
/docs/CODECOV_SETUP.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Codecov Setup Guide
  2 | 
  3 | This guide explains how to set up and configure Codecov for the n8n-MCP project.
  4 | 
  5 | ## Prerequisites
  6 | 
  7 | 1. A Codecov account (sign up at https://codecov.io)
  8 | 2. Repository admin access to add the CODECOV_TOKEN secret
  9 | 
 10 | ## Setup Steps
 11 | 
 12 | ### 1. Get Your Codecov Token
 13 | 
 14 | 1. Sign in to [Codecov](https://codecov.io)
 15 | 2. Add your repository: `czlonkowski/n8n-mcp`
 16 | 3. Copy the upload token from the repository settings
 17 | 
 18 | ### 2. Add Token to GitHub Secrets
 19 | 
 20 | 1. Go to your GitHub repository settings
 21 | 2. Navigate to `Settings` → `Secrets and variables` → `Actions`
 22 | 3. Click "New repository secret"
 23 | 4. Name: `CODECOV_TOKEN`
 24 | 5. Value: Paste your Codecov token
 25 | 6. Click "Add secret"
 26 | 
 27 | ### 3. Update the Badge Token
 28 | 
 29 | Edit the README.md file and replace `YOUR_TOKEN` in the Codecov badge with your actual token:
 30 | 
 31 | ```markdown
 32 | [![codecov](https://codecov.io/gh/czlonkowski/n8n-mcp/graph/badge.svg?token=YOUR_ACTUAL_TOKEN)](https://codecov.io/gh/czlonkowski/n8n-mcp)
 33 | ```
 34 | 
 35 | Note: The token in the badge URL is a read-only token and safe to commit.
 36 | 
 37 | ## Configuration Details
 38 | 
 39 | ### codecov.yml
 40 | 
 41 | The configuration file sets:
 42 | - **Target coverage**: 80% for both project and patch
 43 | - **Coverage precision**: 2 decimal places
 44 | - **Comment behavior**: Comments on all PRs with coverage changes
 45 | - **Ignored files**: Test files, scripts, node_modules, and build outputs
 46 | 
 47 | ### GitHub Actions
 48 | 
 49 | The workflow:
 50 | 1. Runs tests with coverage using `npm run test:coverage`
 51 | 2. Generates LCOV format coverage report
 52 | 3. Uploads to Codecov using the official action
 53 | 4. Fails the build if upload fails
 54 | 
 55 | ### Vitest Configuration
 56 | 
 57 | Coverage settings in `vitest.config.ts`:
 58 | - **Provider**: V8 (fast and accurate)
 59 | - **Reporters**: text, json, html, and lcov
 60 | - **Thresholds**: 80% lines, 80% functions, 75% branches, 80% statements
 61 | 
 62 | ## Viewing Coverage
 63 | 
 64 | ### Local Coverage
 65 | 
 66 | ```bash
 67 | # Generate coverage report
 68 | npm run test:coverage
 69 | 
 70 | # View HTML report
 71 | open coverage/index.html
 72 | ```
 73 | 
 74 | ### Online Coverage
 75 | 
 76 | 1. Visit https://codecov.io/gh/czlonkowski/n8n-mcp
 77 | 2. View detailed reports, graphs, and file-by-file coverage
 78 | 3. Check PR comments for coverage changes
 79 | 
 80 | ## Troubleshooting
 81 | 
 82 | ### Coverage Not Uploading
 83 | 
 84 | 1. Verify CODECOV_TOKEN is set in GitHub secrets
 85 | 2. Check GitHub Actions logs for errors
 86 | 3. Ensure coverage/lcov.info is generated
 87 | 
 88 | ### Badge Not Showing
 89 | 
 90 | 1. Wait a few minutes after first upload
 91 | 2. Verify the token in the badge URL is correct
 92 | 3. Check if the repository is public/private settings match
 93 | 
 94 | ### Low Coverage Areas
 95 | 
 96 | Current areas with lower coverage that could be improved:
 97 | - HTTP server implementations
 98 | - MCP index files
 99 | - Some edge cases in validators
100 | 
101 | ## Best Practices
102 | 
103 | 1. **Write tests first**: Aim for TDD when adding features
104 | 2. **Focus on critical paths**: Prioritize testing core functionality
105 | 3. **Mock external dependencies**: Use MSW for HTTP, mock for databases
106 | 4. **Keep coverage realistic**: 80% is good, 100% isn't always practical
107 | 5. **Monitor trends**: Watch coverage over time, not just absolute numbers
108 | 
109 | ## Resources
110 | 
111 | - [Codecov Documentation](https://docs.codecov.io/)
112 | - [Vitest Coverage](https://vitest.dev/guide/coverage.html)
113 | - [GitHub Actions + Codecov](https://github.com/codecov/codecov-action)
```

--------------------------------------------------------------------------------
/src/mcp/stdio-wrapper.ts:
--------------------------------------------------------------------------------

```typescript
  1 | #!/usr/bin/env node
  2 | 
  3 | /**
  4 |  * Stdio wrapper for MCP server
  5 |  * Ensures clean JSON-RPC communication by suppressing all non-JSON output
  6 |  */
  7 | 
  8 | // CRITICAL: Set environment BEFORE any imports to prevent any initialization logs
  9 | process.env.MCP_MODE = 'stdio';
 10 | process.env.DISABLE_CONSOLE_OUTPUT = 'true';
 11 | process.env.LOG_LEVEL = 'error';
 12 | 
 13 | // Suppress all console output before anything else
 14 | const originalConsoleLog = console.log;
 15 | const originalConsoleError = console.error;
 16 | const originalConsoleWarn = console.warn;
 17 | const originalConsoleInfo = console.info;
 18 | const originalConsoleDebug = console.debug;
 19 | const originalConsoleTrace = console.trace;
 20 | const originalConsoleDir = console.dir;
 21 | const originalConsoleTime = console.time;
 22 | const originalConsoleTimeEnd = console.timeEnd;
 23 | 
 24 | // Override ALL console methods to prevent any output
 25 | console.log = () => {};
 26 | console.error = () => {};
 27 | console.warn = () => {};
 28 | console.info = () => {};
 29 | console.debug = () => {};
 30 | console.trace = () => {};
 31 | console.dir = () => {};
 32 | console.time = () => {};
 33 | console.timeEnd = () => {};
 34 | console.timeLog = () => {};
 35 | console.group = () => {};
 36 | console.groupEnd = () => {};
 37 | console.table = () => {};
 38 | console.clear = () => {};
 39 | console.count = () => {};
 40 | console.countReset = () => {};
 41 | 
 42 | // Import and run the server AFTER suppressing output
 43 | import { N8NDocumentationMCPServer } from './server';
 44 | 
 45 | let server: N8NDocumentationMCPServer | null = null;
 46 | 
 47 | async function main() {
 48 |   try {
 49 |     server = new N8NDocumentationMCPServer();
 50 |     await server.run();
 51 |   } catch (error) {
 52 |     // In case of fatal error, output to stderr only
 53 |     originalConsoleError('Fatal error:', error);
 54 |     process.exit(1);
 55 |   }
 56 | }
 57 | 
 58 | // Handle uncaught errors silently
 59 | process.on('uncaughtException', (error) => {
 60 |   originalConsoleError('Uncaught exception:', error);
 61 |   process.exit(1);
 62 | });
 63 | 
 64 | process.on('unhandledRejection', (reason) => {
 65 |   originalConsoleError('Unhandled rejection:', reason);
 66 |   process.exit(1);
 67 | });
 68 | 
 69 | // Handle termination signals for proper cleanup
 70 | let isShuttingDown = false;
 71 | 
 72 | async function shutdown(signal: string) {
 73 |   if (isShuttingDown) return;
 74 |   isShuttingDown = true;
 75 |   
 76 |   // Log to stderr only (not stdout which would corrupt JSON-RPC)
 77 |   originalConsoleError(`Received ${signal}, shutting down gracefully...`);
 78 |   
 79 |   try {
 80 |     // Shutdown the server if it exists
 81 |     if (server) {
 82 |       await server.shutdown();
 83 |     }
 84 |   } catch (error) {
 85 |     originalConsoleError('Error during shutdown:', error);
 86 |   }
 87 |   
 88 |   // Close stdin to signal we're done reading
 89 |   process.stdin.pause();
 90 |   process.stdin.destroy();
 91 |   
 92 |   // Exit with timeout to ensure we don't hang
 93 |   setTimeout(() => {
 94 |     process.exit(0);
 95 |   }, 500).unref(); // unref() allows process to exit if this is the only thing keeping it alive
 96 |   
 97 |   // But also exit immediately if nothing else is pending
 98 |   process.exit(0);
 99 | }
100 | 
101 | // Register signal handlers
102 | process.on('SIGTERM', () => void shutdown('SIGTERM'));
103 | process.on('SIGINT', () => void shutdown('SIGINT'));
104 | process.on('SIGHUP', () => void shutdown('SIGHUP'));
105 | 
106 | // Also handle stdin close (when Claude Desktop closes the pipe)
107 | process.stdin.on('end', () => {
108 |   originalConsoleError('stdin closed, shutting down...');
109 |   void shutdown('STDIN_CLOSE');
110 | });
111 | 
112 | main();
```

--------------------------------------------------------------------------------
/src/mcp/tool-docs/discovery/list-nodes.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { ToolDocumentation } from '../types';
 2 | 
 3 | export const listNodesDoc: ToolDocumentation = {
 4 |   name: 'list_nodes',
 5 |   category: 'discovery',
 6 |   essentials: {
 7 |     description: 'Lists n8n nodes with filtering options. Returns up to 525 total nodes. Default limit is 50, use limit:200 to get all nodes. Filter by category to find specific node types like triggers (104 nodes) or AI nodes (263 nodes).',
 8 |     keyParameters: ['category', 'package', 'limit', 'isAITool'],
 9 |     example: 'list_nodes({limit:200})',
10 |     performance: '<10ms for any query size',
11 |     tips: [
12 |       'Use limit:200 to get all 525 nodes',
13 |       'Categories: trigger (104), transform (250+), output/input (50+)',
14 |       'Use search_nodes for keyword search'
15 |     ]
16 |   },
17 |   full: {
18 |     description: 'Lists n8n nodes with comprehensive filtering options. Returns an array of node metadata including type, name, description, and category. Database contains 525 total nodes: 456 from n8n-nodes-base package and 69 from @n8n/n8n-nodes-langchain package.',
19 |     parameters: {
20 |       category: { type: 'string', description: 'Filter by category: "trigger" (104 nodes), "transform" (250+ nodes), "output", "input", or "AI"', required: false },
21 |       package: { type: 'string', description: 'Filter by package: "n8n-nodes-base" (456 core nodes) or "@n8n/n8n-nodes-langchain" (69 AI nodes)', required: false },
22 |       limit: { type: 'number', description: 'Maximum results to return. Default: 50. Use 200+ to get all 525 nodes', required: false },
23 |       isAITool: { type: 'boolean', description: 'Filter to show only AI-capable nodes (263 nodes)', required: false },
24 |       developmentStyle: { type: 'string', description: 'Filter by style: "programmatic" or "declarative". Most nodes are programmatic', required: false }
25 |     },
26 |     returns: 'Array of node objects, each containing: nodeType (e.g., "nodes-base.webhook"), displayName (e.g., "Webhook"), description, category, package, isAITool flag',
27 |     examples: [
28 |       'list_nodes({limit:200}) - Returns all 525 nodes',
29 |       'list_nodes({category:"trigger"}) - Returns 104 trigger nodes (Webhook, Schedule, Email Trigger, etc.)',
30 |       'list_nodes({package:"@n8n/n8n-nodes-langchain"}) - Returns 69 AI/LangChain nodes',
31 |       'list_nodes({isAITool:true}) - Returns 263 AI-capable nodes',
32 |       'list_nodes({category:"trigger", isAITool:true}) - Combines filters for AI-capable triggers'
33 |     ],
34 |     useCases: [
35 |       'Browse all available nodes when building workflows',
36 |       'Find all trigger nodes to start workflows',
37 |       'Discover AI/ML nodes for intelligent automation',
38 |       'Check available nodes in specific packages'
39 |     ],
40 |     performance: '<10ms for any query size. Results are cached in memory',
41 |     bestPractices: [
42 |       'Use limit:200 when you need the complete node inventory',
43 |       'Filter by category for focused discovery',
44 |       'Combine with get_node_essentials to configure selected nodes'
45 |     ],
46 |     pitfalls: [
47 |       'No text search capability - use search_nodes for keyword search',
48 |       'developmentStyle filter rarely useful - most nodes are "programmatic"'
49 |     ],
50 |     relatedTools: ['search_nodes for keyword search', 'list_ai_tools for AI-specific discovery', 'get_node_essentials to configure nodes']
51 |   }
52 | };
```

--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------

```dockerfile
 1 | # syntax=docker/dockerfile:1.7
 2 | # Ultra-optimized Dockerfile - minimal runtime dependencies (no n8n packages)
 3 | 
 4 | # Stage 1: Builder (TypeScript compilation only)
 5 | FROM node:22-alpine AS builder
 6 | WORKDIR /app
 7 | 
 8 | # Copy tsconfig files for TypeScript compilation
 9 | COPY tsconfig*.json ./
10 | 
11 | # Create minimal package.json and install ONLY build dependencies
12 | # Note: openai and zod are needed for TypeScript compilation of template metadata modules
13 | RUN --mount=type=cache,target=/root/.npm \
14 |     echo '{}' > package.json && \
15 |     npm install --no-save typescript@^5.8.3 @types/node@^22.15.30 @types/express@^5.0.3 \
16 |         @modelcontextprotocol/sdk@^1.12.1 dotenv@^16.5.0 express@^5.1.0 axios@^1.10.0 \
17 |         n8n-workflow@^1.96.0 uuid@^11.0.5 @types/uuid@^10.0.0 \
18 |         openai@^4.77.0 zod@^3.24.1 lru-cache@^11.2.1 @supabase/supabase-js@^2.57.4
19 | 
20 | # Copy source and build
21 | COPY src ./src
22 | # Note: src/n8n contains TypeScript types needed for compilation
23 | # These will be compiled but not included in runtime
24 | RUN npx tsc -p tsconfig.build.json
25 | 
26 | # Stage 2: Runtime (minimal dependencies)
27 | FROM node:22-alpine AS runtime
28 | WORKDIR /app
29 | 
30 | # Install only essential runtime tools
31 | RUN apk add --no-cache curl su-exec && \
32 |     rm -rf /var/cache/apk/*
33 | 
34 | # Copy runtime-only package.json
35 | COPY package.runtime.json package.json
36 | 
37 | # Install runtime dependencies with cache mount
38 | RUN --mount=type=cache,target=/root/.npm \
39 |     npm install --production --no-audit --no-fund
40 | 
41 | # Copy built application
42 | COPY --from=builder /app/dist ./dist
43 | 
44 | # Copy pre-built database and required files
45 | # Cache bust: 2025-07-06-trigger-fix-v3 - includes is_trigger=true for webhook,cron,interval,emailReadImap
46 | COPY data/nodes.db ./data/
47 | COPY src/database/schema-optimized.sql ./src/database/
48 | COPY .env.example ./
49 | 
50 | # Copy entrypoint script, config parser, and n8n-mcp command
51 | COPY docker/docker-entrypoint.sh /usr/local/bin/
52 | COPY docker/parse-config.js /app/docker/
53 | COPY docker/n8n-mcp /usr/local/bin/
54 | RUN chmod +x /usr/local/bin/docker-entrypoint.sh /usr/local/bin/n8n-mcp
55 | 
56 | # Add container labels
57 | LABEL org.opencontainers.image.source="https://github.com/czlonkowski/n8n-mcp"
58 | LABEL org.opencontainers.image.description="n8n MCP Server - Runtime Only"
59 | LABEL org.opencontainers.image.licenses="MIT"
60 | LABEL org.opencontainers.image.title="n8n-mcp"
61 | 
62 | # Create non-root user with unpredictable UID/GID
63 | # Using a hash of the build time to generate unpredictable IDs
64 | RUN BUILD_HASH=$(date +%s | sha256sum | head -c 8) && \
65 |     UID=$((10000 + 0x${BUILD_HASH} % 50000)) && \
66 |     GID=$((10000 + 0x${BUILD_HASH} % 50000)) && \
67 |     addgroup -g ${GID} -S nodejs && \
68 |     adduser -S nodejs -u ${UID} -G nodejs && \
69 |     chown -R nodejs:nodejs /app
70 | 
71 | # Switch to non-root user
72 | USER nodejs
73 | 
74 | # Set Docker environment flag
75 | ENV IS_DOCKER=true
76 | 
77 | # Telemetry: Anonymous usage statistics are ENABLED by default
78 | # To opt-out, uncomment the following line:
79 | # ENV N8N_MCP_TELEMETRY_DISABLED=true
80 | 
81 | # Expose HTTP port
82 | EXPOSE 3000
83 | 
84 | # Set stop signal to SIGTERM (default, but explicit is better)
85 | STOPSIGNAL SIGTERM
86 | 
87 | # Health check
88 | HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
89 |   CMD curl -f http://127.0.0.1:3000/health || exit 1
90 | 
91 | # Optimized entrypoint
92 | ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
93 | CMD ["node", "dist/mcp/index.js"]
94 | 
```

--------------------------------------------------------------------------------
/src/mcp/tool-docs/configuration/get-node-as-tool-info.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { ToolDocumentation } from '../types';
 2 | 
 3 | export const getNodeAsToolInfoDoc: ToolDocumentation = {
 4 |   name: 'get_node_as_tool_info',
 5 |   category: 'configuration',
 6 |   essentials: {
 7 |     description: 'Explains how to use ANY node as an AI tool with requirements and examples.',
 8 |     keyParameters: ['nodeType'],
 9 |     example: 'get_node_as_tool_info({nodeType: "nodes-base.slack"})',
10 |     performance: 'Fast - returns guidance and examples',
11 |     tips: [
12 |       'ANY node can be used as AI tool, not just AI-marked ones',
13 |       'Community nodes need N8N_COMMUNITY_PACKAGES_ALLOW_TOOL_USAGE=true',
14 |       'Provides specific use cases and connection requirements'
15 |     ]
16 |   },
17 |   full: {
18 |     description: `Shows how to use any n8n node as an AI tool in AI Agent workflows. In n8n, ANY node can be connected to an AI Agent's tool port, allowing the AI to use that node's functionality. This tool provides specific guidance, requirements, and examples for using a node as an AI tool.`,
19 |     parameters: {
20 |       nodeType: {
21 |         type: 'string',
22 |         required: true,
23 |         description: 'Full node type WITH prefix: "nodes-base.slack", "nodes-base.googleSheets", etc.',
24 |         examples: [
25 |           'nodes-base.slack',
26 |           'nodes-base.httpRequest',
27 |           'nodes-base.googleSheets',
28 |           'nodes-langchain.documentLoader'
29 |         ]
30 |       }
31 |     },
32 |     returns: `Object containing:
33 | - nodeType: The node's full type identifier
34 | - displayName: Human-readable name
35 | - isMarkedAsAITool: Whether node has usableAsTool property
36 | - aiToolCapabilities: Detailed AI tool usage information including:
37 |   - canBeUsedAsTool: Always true in n8n
38 |   - requiresEnvironmentVariable: For community nodes
39 |   - commonUseCases: Specific AI tool use cases
40 |   - requirements: Connection and environment setup
41 |   - examples: Code examples for common scenarios
42 |   - tips: Best practices for AI tool usage`,
43 |     examples: [
44 |       'get_node_as_tool_info({nodeType: "nodes-base.slack"}) - Get AI tool guidance for Slack',
45 |       'get_node_as_tool_info({nodeType: "nodes-base.httpRequest"}) - Learn to use HTTP Request as AI tool',
46 |       'get_node_as_tool_info({nodeType: "nodes-base.postgres"}) - Database queries as AI tools'
47 |     ],
48 |     useCases: [
49 |       'Understanding how to connect any node to AI Agent',
50 |       'Learning environment requirements for community nodes',
51 |       'Getting specific use case examples for AI tool usage',
52 |       'Checking if a node is optimized for AI usage',
53 |       'Understanding credential requirements for AI tools'
54 |     ],
55 |     performance: 'Very fast - returns pre-computed guidance and examples',
56 |     bestPractices: [
57 |       'Use this before configuring nodes as AI tools',
58 |       'Check environment requirements for community nodes',
59 |       'Review common use cases to understand best applications',
60 |       'Test nodes independently before connecting to AI Agent',
61 |       'Give tools descriptive names in AI Agent configuration'
62 |     ],
63 |     pitfalls: [
64 |       'Community nodes require environment variable to be used as tools',
65 |       'Not all nodes make sense as AI tools (e.g., triggers)',
66 |       'Some nodes require specific credentials configuration',
67 |       'Tool descriptions in AI Agent must be clear and detailed'
68 |     ],
69 |     relatedTools: ['list_ai_tools', 'get_node_essentials', 'validate_node_operation']
70 |   }
71 | };
```

--------------------------------------------------------------------------------
/src/mcp/tool-docs/system/n8n-list-available-tools.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { ToolDocumentation } from '../types';
 2 | 
 3 | export const n8nListAvailableToolsDoc: ToolDocumentation = {
 4 |   name: 'n8n_list_available_tools',
 5 |   category: 'system',
 6 |   essentials: {
 7 |     description: 'List all available n8n management tools and their capabilities',
 8 |     keyParameters: [],
 9 |     example: 'n8n_list_available_tools({})',
10 |     performance: 'Instant - returns static tool list',
11 |     tips: [
12 |       'Shows only tools available with current API configuration',
13 |       'If no n8n tools appear, run n8n_diagnostic to troubleshoot',
14 |       'Tool availability depends on N8N_API_URL and N8N_API_KEY being set'
15 |     ]
16 |   },
17 |   full: {
18 |     description: `Lists all available n8n management tools based on current configuration.
19 | 
20 | This tool provides:
21 | - Complete list of n8n management tools (when API is configured)
22 | - Tool descriptions and capabilities
23 | - Categorized tool listing (workflow, execution, system)
24 | - Dynamic availability based on API configuration
25 | 
26 | The tool list is dynamic:
27 | - Shows 14+ management tools when N8N_API_URL and N8N_API_KEY are configured
28 | - Shows only documentation tools when API is not configured
29 | - Helps discover available functionality
30 | - Provides quick reference for tool names and purposes`,
31 |     parameters: {},
32 |     returns: `Object containing:
33 | - tools: Array of available tool objects, each with:
34 |   - name: Tool identifier (e.g., 'n8n_create_workflow')
35 |   - description: Brief description of tool functionality
36 |   - category: Tool category ('workflow', 'execution', 'system')
37 |   - requiresApi: Whether tool needs API configuration
38 | - categories: Summary count by category
39 | - totalTools: Total number of available tools
40 | - apiConfigured: Whether n8n API is configured`,
41 |     examples: [
42 |       'n8n_list_available_tools({}) - List all available tools',
43 |       '// Check for specific tool availability\nconst tools = await n8n_list_available_tools({});\nconst hasWorkflowTools = tools.tools.some(t => t.category === "workflow");',
44 |       '// Discover management capabilities\nconst result = await n8n_list_available_tools({});\nconsole.log(`${result.totalTools} tools available`);'
45 |     ],
46 |     useCases: [
47 |       'Discovering available n8n management capabilities',
48 |       'Checking if API configuration is working correctly',
49 |       'Finding the right tool for a specific task',
50 |       'Generating help documentation or command lists',
51 |       'Verifying tool availability before automation scripts'
52 |     ],
53 |     performance: `Instant response:
54 | - No API calls required
55 | - Returns pre-defined tool list
56 | - Filtered based on configuration
57 | - Zero network overhead`,
58 |     bestPractices: [
59 |       'Check tool availability before building automation workflows',
60 |       'Use with n8n_diagnostic if expected tools are missing',
61 |       'Reference tool names exactly as returned by this tool',
62 |       'Group operations by category for better organization',
63 |       'Cache results as tool list only changes with configuration'
64 |     ],
65 |     pitfalls: [
66 |       'Tool list is empty if N8N_API_URL and N8N_API_KEY are not set',
67 |       'Does not validate if tools will actually work - just shows availability',
68 |       'Tool names must be used exactly as returned',
69 |       'Does not show tool parameters - use tools_documentation for details'
70 |     ],
71 |     relatedTools: ['n8n_diagnostic', 'n8n_health_check', 'tools_documentation']
72 |   }
73 | };
```

--------------------------------------------------------------------------------
/scripts/test-webhook-validation.ts:
--------------------------------------------------------------------------------

```typescript
  1 | #!/usr/bin/env npx tsx
  2 | 
  3 | import { EnhancedConfigValidator } from '../src/services/enhanced-config-validator.js';
  4 | 
  5 | console.log('🧪 Testing Webhook Data Access Validation\n');
  6 | 
  7 | const testCases = [
  8 |   {
  9 |     name: 'Direct webhook data access (incorrect)',
 10 |     config: {
 11 |       language: 'javaScript',
 12 |       jsCode: `// Processing data from Webhook node
 13 | const prevWebhook = $('Webhook').first();
 14 | const command = items[0].json.testCommand;
 15 | const data = items[0].json.payload;
 16 | return [{json: {command, data}}];`
 17 |     },
 18 |     expectWarning: true
 19 |   },
 20 |   {
 21 |     name: 'Correct webhook data access through body',
 22 |     config: {
 23 |       language: 'javaScript',
 24 |       jsCode: `// Processing data from Webhook node
 25 | const webhookData = items[0].json.body;
 26 | const command = webhookData.testCommand;
 27 | const data = webhookData.payload;
 28 | return [{json: {command, data}}];`
 29 |     },
 30 |     expectWarning: false
 31 |   },
 32 |   {
 33 |     name: 'Common webhook field names without body',
 34 |     config: {
 35 |       language: 'javaScript',
 36 |       jsCode: `// Processing webhook
 37 | const command = items[0].json.command;
 38 | const action = items[0].json.action;
 39 | const payload = items[0].json.payload;
 40 | return [{json: {command, action, payload}}];`
 41 |     },
 42 |     expectWarning: true
 43 |   },
 44 |   {
 45 |     name: 'Non-webhook data access (should not warn)',
 46 |     config: {
 47 |       language: 'javaScript',
 48 |       jsCode: `// Processing data from HTTP Request node
 49 | const data = items[0].json.results;
 50 | const status = items[0].json.status;
 51 | return [{json: {data, status}}];`
 52 |     },
 53 |     expectWarning: false
 54 |   },
 55 |   {
 56 |     name: 'Mixed correct and incorrect access',
 57 |     config: {
 58 |       language: 'javaScript',
 59 |       jsCode: `// Mixed access patterns
 60 | const webhookBody = items[0].json.body;  // Correct
 61 | const directAccess = items[0].json.command;  // Incorrect if webhook
 62 | return [{json: {webhookBody, directAccess}}];`
 63 |     },
 64 |     expectWarning: false  // If user already uses .body, we assume they know the pattern
 65 |   }
 66 | ];
 67 | 
 68 | let passCount = 0;
 69 | let failCount = 0;
 70 | 
 71 | for (const test of testCases) {
 72 |   console.log(`Test: ${test.name}`);
 73 |   const result = EnhancedConfigValidator.validateWithMode(
 74 |     'nodes-base.code',
 75 |     test.config,
 76 |     [
 77 |       { name: 'language', type: 'options', options: ['javaScript', 'python'] },
 78 |       { name: 'jsCode', type: 'string' }
 79 |     ],
 80 |     'operation',
 81 |     'ai-friendly'
 82 |   );
 83 |   
 84 |   const hasWebhookWarning = result.warnings.some(w => 
 85 |     w.message.includes('Webhook data is nested under .body') ||
 86 |     w.message.includes('webhook data, remember it\'s nested under .body')
 87 |   );
 88 |   
 89 |   const passed = hasWebhookWarning === test.expectWarning;
 90 |   
 91 |   console.log(`  Expected warning: ${test.expectWarning}`);
 92 |   console.log(`  Has webhook warning: ${hasWebhookWarning}`);
 93 |   console.log(`  Result: ${passed ? '✅ PASS' : '❌ FAIL'}`);
 94 |   
 95 |   if (result.warnings.length > 0) {
 96 |     const relevantWarnings = result.warnings
 97 |       .filter(w => w.message.includes('webhook') || w.message.includes('Webhook'))
 98 |       .map(w => w.message);
 99 |     if (relevantWarnings.length > 0) {
100 |       console.log(`  Webhook warnings: ${relevantWarnings.join(', ')}`);
101 |     }
102 |   }
103 |   
104 |   if (passed) passCount++;
105 |   else failCount++;
106 |   
107 |   console.log();
108 | }
109 | 
110 | console.log(`\n📊 Results: ${passCount} passed, ${failCount} failed`);
111 | console.log(failCount === 0 ? '✅ All webhook validation tests passed!' : '❌ Some tests failed');
```
Page 3/63FirstPrevNextLast