#
tokens: 49233/50000 68/628 files (page 2/63)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 2 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

--------------------------------------------------------------------------------
/tests/test-package-info.js:
--------------------------------------------------------------------------------

```javascript
 1 | #!/usr/bin/env node
 2 | 
 3 | const { NodeSourceExtractor } = require('../dist/utils/node-source-extractor');
 4 | 
 5 | async function testPackageInfo() {
 6 |   console.log('🧪 Testing Package Info Extraction\n');
 7 |   
 8 |   const extractor = new NodeSourceExtractor();
 9 |   
10 |   const testNodes = [
11 |     'n8n-nodes-base.Slack',
12 |     'n8n-nodes-base.HttpRequest',
13 |     'n8n-nodes-base.Function'
14 |   ];
15 |   
16 |   for (const nodeType of testNodes) {
17 |     console.log(`\n📦 Testing ${nodeType}:`);
18 |     try {
19 |       const result = await extractor.extractNodeSource(nodeType);
20 |       console.log(`  - Source Code: ${result.sourceCode ? '✅' : '❌'} (${result.sourceCode?.length || 0} bytes)`);
21 |       console.log(`  - Credential Code: ${result.credentialCode ? '✅' : '❌'} (${result.credentialCode?.length || 0} bytes)`);
22 |       console.log(`  - Package Name: ${result.packageInfo?.name || '❌ undefined'}`);
23 |       console.log(`  - Package Version: ${result.packageInfo?.version || '❌ undefined'}`);
24 |     } catch (error) {
25 |       console.log(`  ❌ Error: ${error.message}`);
26 |     }
27 |   }
28 | }
29 | 
30 | testPackageInfo().catch(console.error);
```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "compilerOptions": {
 3 |     "target": "ES2020",
 4 |     "module": "commonjs",
 5 |     "lib": ["ES2020"],
 6 |     "types": ["node", "vitest/globals", "./types/test-env"],
 7 |     "outDir": "./dist",
 8 |     "rootDir": "./",
 9 |     "strict": true,
10 |     "esModuleInterop": true,
11 |     "skipLibCheck": true,
12 |     "forceConsistentCasingInFileNames": true,
13 |     "resolveJsonModule": true,
14 |     "declaration": true,
15 |     "declarationMap": true,
16 |     "sourceMap": true,
17 |     "removeComments": true,
18 |     "noImplicitAny": true,
19 |     "strictNullChecks": true,
20 |     "strictFunctionTypes": true,
21 |     "strictBindCallApply": true,
22 |     "strictPropertyInitialization": true,
23 |     "noImplicitThis": true,
24 |     "alwaysStrict": true,
25 |     "noUnusedLocals": false,
26 |     "noUnusedParameters": false,
27 |     "noImplicitReturns": true,
28 |     "noFallthroughCasesInSwitch": true,
29 |     "moduleResolution": "node",
30 |     "allowJs": true,
31 |     "baseUrl": ".",
32 |     "paths": {
33 |       "@/*": ["src/*"],
34 |       "@tests/*": ["tests/*"]
35 |     }
36 |   },
37 |   "include": ["src/**/*", "tests/**/*", "vitest.config.ts", "types/**/*"],
38 |   "exclude": ["node_modules", "dist"]
39 | }
```

--------------------------------------------------------------------------------
/scripts/generate-benchmark-stub.js:
--------------------------------------------------------------------------------

```javascript
 1 | #!/usr/bin/env node
 2 | 
 3 | /**
 4 |  * Generates a stub benchmark-results.json file when benchmarks fail to produce output.
 5 |  * This ensures the CI pipeline doesn't fail due to missing files.
 6 |  */
 7 | 
 8 | const fs = require('fs');
 9 | const path = require('path');
10 | 
11 | const stubResults = {
12 |   timestamp: new Date().toISOString(),
13 |   files: [
14 |     {
15 |       filepath: 'tests/benchmarks/stub.bench.ts',
16 |       groups: [
17 |         {
18 |           name: 'Stub Benchmarks',
19 |           benchmarks: [
20 |             {
21 |               name: 'stub-benchmark',
22 |               result: {
23 |                 mean: 0.001,
24 |                 min: 0.001,
25 |                 max: 0.001,
26 |                 hz: 1000,
27 |                 p75: 0.001,
28 |                 p99: 0.001,
29 |                 p995: 0.001,
30 |                 p999: 0.001,
31 |                 rme: 0,
32 |                 samples: 1
33 |               }
34 |             }
35 |           ]
36 |         }
37 |       ]
38 |     }
39 |   ]
40 | };
41 | 
42 | const outputPath = path.join(process.cwd(), 'benchmark-results.json');
43 | fs.writeFileSync(outputPath, JSON.stringify(stubResults, null, 2));
44 | console.log(`Generated stub benchmark results at ${outputPath}`);
```

--------------------------------------------------------------------------------
/tests/mocks/n8n-api/data/credentials.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Mock credential data for MSW handlers
 3 |  */
 4 | 
 5 | export interface MockCredential {
 6 |   id: string;
 7 |   name: string;
 8 |   type: string;
 9 |   data?: Record<string, any>; // Usually encrypted in real n8n
10 |   createdAt: string;
11 |   updatedAt: string;
12 | }
13 | 
14 | export const mockCredentials: MockCredential[] = [
15 |   {
16 |     id: 'cred_1',
17 |     name: 'Slack Account',
18 |     type: 'slackApi',
19 |     createdAt: '2024-01-01T00:00:00.000Z',
20 |     updatedAt: '2024-01-01T00:00:00.000Z'
21 |   },
22 |   {
23 |     id: 'cred_2',
24 |     name: 'HTTP Header Auth',
25 |     type: 'httpHeaderAuth',
26 |     createdAt: '2024-01-01T00:00:00.000Z',
27 |     updatedAt: '2024-01-01T00:00:00.000Z'
28 |   },
29 |   {
30 |     id: 'cred_3',
31 |     name: 'OpenAI API',
32 |     type: 'openAiApi',
33 |     createdAt: '2024-01-01T00:00:00.000Z',
34 |     updatedAt: '2024-01-01T00:00:00.000Z'
35 |   }
36 | ];
37 | 
38 | /**
39 |  * Factory for creating mock credentials
40 |  */
41 | export const credentialFactory = {
42 |   create: (type: string, name?: string): MockCredential => ({
43 |     id: `cred_${Date.now()}`,
44 |     name: name || `${type} Credential`,
45 |     type,
46 |     createdAt: new Date().toISOString(),
47 |     updatedAt: new Date().toISOString()
48 |   })
49 | };
```

--------------------------------------------------------------------------------
/docker-compose.buildkit.yml:
--------------------------------------------------------------------------------

```yaml
 1 | # Docker Compose file with BuildKit optimizations for faster builds
 2 | # This now builds without n8n dependencies for ultra-fast builds
 3 | version: '3.8'
 4 | 
 5 | services:
 6 |   n8n-mcp:
 7 |     build:
 8 |       context: .
 9 |       dockerfile: Dockerfile
10 |       # Enable BuildKit
11 |       x-bake:
12 |         cache-from:
13 |           - type=gha
14 |           - type=local,src=/tmp/.buildx-cache
15 |         cache-to:
16 |           - type=gha,mode=max
17 |           - type=local,dest=/tmp/.buildx-cache-new,mode=max
18 |       args:
19 |         BUILDKIT_INLINE_CACHE: 1
20 |     image: n8n-mcp:latest
21 |     container_name: n8n-mcp
22 |     ports:
23 |       - "3000:3000"
24 |     environment:
25 |       - MCP_MODE=${MCP_MODE:-http}
26 |       - AUTH_TOKEN=${AUTH_TOKEN}
27 |       - NODE_ENV=${NODE_ENV:-production}
28 |       - LOG_LEVEL=${LOG_LEVEL:-info}
29 |       - PORT=3000
30 |     volumes:
31 |       # Mount data directory for persistence
32 |       - ./data:/app/data
33 |     restart: unless-stopped
34 |     healthcheck:
35 |       test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
36 |       interval: 30s
37 |       timeout: 10s
38 |       retries: 3
39 |       start_period: 5s
40 | 
41 | # Use external network if needed
42 | networks:
43 |   default:
44 |     name: n8n-mcp-network
```

--------------------------------------------------------------------------------
/scripts/test-fuzzy-simple.ts:
--------------------------------------------------------------------------------

```typescript
 1 | #!/usr/bin/env node
 2 | 
 3 | import { N8NDocumentationMCPServer } from '../src/mcp/server';
 4 | 
 5 | async function testSimple() {
 6 |   const server = new N8NDocumentationMCPServer();
 7 |   await new Promise(resolve => setTimeout(resolve, 1000));
 8 |   
 9 |   // Just test one query
10 |   const result = await server.executeTool('search_nodes', {
11 |     query: 'slak',
12 |     mode: 'FUZZY',
13 |     limit: 5
14 |   });
15 |   
16 |   console.log('Query: "slak" (FUZZY mode)');
17 |   console.log(`Results: ${result.results.length}`);
18 |   
19 |   if (result.results.length === 0) {
20 |     // Let's check with a lower threshold
21 |     const serverAny = server as any;
22 |     const slackNode = { 
23 |       node_type: 'nodes-base.slack', 
24 |       display_name: 'Slack', 
25 |       description: 'Consume Slack API' 
26 |     };
27 |     const score = serverAny.calculateFuzzyScore(slackNode, 'slak');
28 |     console.log(`\nSlack node score for "slak": ${score}`);
29 |     console.log('Current threshold: 400');
30 |     console.log('Should it match?', score >= 400 ? 'YES' : 'NO');
31 |   } else {
32 |     result.results.forEach((r: any, i: number) => {
33 |       console.log(`${i + 1}. ${r.displayName}`);
34 |     });
35 |   }
36 | }
37 | 
38 | testSimple().catch(console.error);
```

--------------------------------------------------------------------------------
/src/n8n/MCPApi.credentials.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import {
 2 |   ICredentialType,
 3 |   INodeProperties,
 4 | } from 'n8n-workflow';
 5 | 
 6 | export class MCPApi implements ICredentialType {
 7 |   name = 'mcpApi';
 8 |   displayName = 'MCP API';
 9 |   documentationUrl = 'mcp';
10 |   properties: INodeProperties[] = [
11 |     {
12 |       displayName: 'Server URL',
13 |       name: 'serverUrl',
14 |       type: 'string',
15 |       default: 'http://localhost:3000',
16 |       placeholder: 'http://localhost:3000',
17 |       description: 'The URL of the MCP server',
18 |     },
19 |     {
20 |       displayName: 'Authentication Token',
21 |       name: 'authToken',
22 |       type: 'string',
23 |       typeOptions: {
24 |         password: true,
25 |       },
26 |       default: '',
27 |       description: 'Authentication token for the MCP server (if required)',
28 |     },
29 |     {
30 |       displayName: 'Connection Type',
31 |       name: 'connectionType',
32 |       type: 'options',
33 |       options: [
34 |         {
35 |           name: 'HTTP',
36 |           value: 'http',
37 |         },
38 |         {
39 |           name: 'WebSocket',
40 |           value: 'websocket',
41 |         },
42 |         {
43 |           name: 'STDIO',
44 |           value: 'stdio',
45 |         },
46 |       ],
47 |       default: 'http',
48 |       description: 'How to connect to the MCP server',
49 |     },
50 |   ];
51 | }
```

--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * n8n-MCP - Model Context Protocol Server for n8n
 3 |  * Copyright (c) 2024 AiAdvisors Romuald Czlonkowski
 4 |  * Licensed under the Sustainable Use License v1.0
 5 |  */
 6 | 
 7 | // Engine exports for service integration
 8 | export { N8NMCPEngine, EngineHealth, EngineOptions } from './mcp-engine';
 9 | export { SingleSessionHTTPServer } from './http-server-single-session';
10 | export { ConsoleManager } from './utils/console-manager';
11 | export { N8NDocumentationMCPServer } from './mcp/server';
12 | 
13 | // Type exports for multi-tenant and library usage
14 | export type {
15 |   InstanceContext
16 | } from './types/instance-context';
17 | export {
18 |   validateInstanceContext,
19 |   isInstanceContext
20 | } from './types/instance-context';
21 | 
22 | // Session restoration types (v2.19.0)
23 | export type {
24 |   SessionRestoreHook,
25 |   SessionRestorationOptions,
26 |   SessionState
27 | } from './types/session-restoration';
28 | 
29 | // Re-export MCP SDK types for convenience
30 | export type {
31 |   Tool,
32 |   CallToolResult,
33 |   ListToolsResult
34 | } from '@modelcontextprotocol/sdk/types.js';
35 | 
36 | // Default export for convenience
37 | import N8NMCPEngine from './mcp-engine';
38 | export default N8NMCPEngine;
39 | 
40 | // Legacy CLI functionality - moved to ./mcp/index.ts
41 | // This file now serves as the main entry point for library usage
```

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

```bash
 1 | #!/bin/bash
 2 | 
 3 | # Script to run Docker config tests
 4 | # Usage: ./scripts/test-docker-config.sh [unit|integration|all]
 5 | 
 6 | set -e
 7 | 
 8 | MODE=${1:-all}
 9 | 
10 | echo "Running Docker config tests in mode: $MODE"
11 | 
12 | case $MODE in
13 |   unit)
14 |     echo "Running unit tests..."
15 |     npm test -- tests/unit/docker/
16 |     ;;
17 |   integration)
18 |     echo "Running integration tests (requires Docker)..."
19 |     RUN_DOCKER_TESTS=true npm run test:integration -- tests/integration/docker/
20 |     ;;
21 |   all)
22 |     echo "Running all Docker config tests..."
23 |     npm test -- tests/unit/docker/
24 |     if command -v docker &> /dev/null; then
25 |       echo "Docker found, running integration tests..."
26 |       RUN_DOCKER_TESTS=true npm run test:integration -- tests/integration/docker/
27 |     else
28 |       echo "Docker not found, skipping integration tests"
29 |     fi
30 |     ;;
31 |   coverage)
32 |     echo "Running Docker config tests with coverage..."
33 |     npm run test:coverage -- tests/unit/docker/
34 |     ;;
35 |   security)
36 |     echo "Running security-focused tests..."
37 |     npm test -- tests/unit/docker/config-security.test.ts tests/unit/docker/parse-config.test.ts
38 |     ;;
39 |   *)
40 |     echo "Usage: $0 [unit|integration|all|coverage|security]"
41 |     exit 1
42 |     ;;
43 | esac
44 | 
45 | echo "Docker config tests completed!"
```

--------------------------------------------------------------------------------
/src/utils/simple-cache.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Simple in-memory cache with TTL support
 3 |  * No external dependencies needed
 4 |  */
 5 | export class SimpleCache {
 6 |   private cache = new Map<string, { data: any; expires: number }>();
 7 |   private cleanupTimer: NodeJS.Timeout | null = null;
 8 |   
 9 |   constructor() {
10 |     // Clean up expired entries every minute
11 |     this.cleanupTimer = setInterval(() => {
12 |       const now = Date.now();
13 |       for (const [key, item] of this.cache.entries()) {
14 |         if (item.expires < now) this.cache.delete(key);
15 |       }
16 |     }, 60000);
17 |   }
18 |   
19 |   get(key: string): any {
20 |     const item = this.cache.get(key);
21 |     if (!item || item.expires < Date.now()) {
22 |       this.cache.delete(key);
23 |       return null;
24 |     }
25 |     return item.data;
26 |   }
27 |   
28 |   set(key: string, data: any, ttlSeconds: number = 300): void {
29 |     this.cache.set(key, {
30 |       data,
31 |       expires: Date.now() + (ttlSeconds * 1000)
32 |     });
33 |   }
34 |   
35 |   clear(): void {
36 |     this.cache.clear();
37 |   }
38 |   
39 |   /**
40 |    * Clean up the cache and stop the cleanup timer
41 |    * Essential for preventing memory leaks in long-running servers
42 |    */
43 |   destroy(): void {
44 |     if (this.cleanupTimer) {
45 |       clearInterval(this.cleanupTimer);
46 |       this.cleanupTimer = null;
47 |     }
48 |     this.cache.clear();
49 |   }
50 | }
```

--------------------------------------------------------------------------------
/docs/CODEX_SETUP.md:
--------------------------------------------------------------------------------

```markdown
 1 | # Codex Setup
 2 | 
 3 | Connect n8n-MCP to Codex for enhanced n8n workflow development.
 4 | 
 5 | ## Update your Codex configuration
 6 | 
 7 | Go to your Codex settings at `~/.codex/config.toml` and add the following configuration:
 8 | 
 9 | ### Basic configuration (documentation tools only):
10 | ```toml
11 | [mcp_servers.n8n]
12 | command = "npx"
13 | args = ["n8n-mcp"]
14 | env = { "MCP_MODE" = "stdio", "LOG_LEVEL" = "error", "DISABLE_CONSOLE_OUTPUT" = "true" }
15 | ```
16 | 
17 | ### Full configuration (with n8n management tools):
18 | ```toml
19 | [mcp_servers.n8n]
20 | command = "npx"
21 | args = ["n8n-mcp"]
22 | env = { "MCP_MODE" = "stdio", "LOG_LEVEL" = "error", "DISABLE_CONSOLE_OUTPUT" = "true", "N8N_API_URL" = "https://your-n8n-instance.com", "N8N_API_KEY" = "your-api-key" }
23 | ```
24 | 
25 | Make sure to replace `https://your-n8n-instance.com` with your actual n8n URL and `your-api-key` with your n8n API key.
26 | 
27 | ## Managing Your MCP Server
28 | Enter the Codex CLI and use the `/mcp` command to see server status and available tools.
29 | 
30 | ![n8n-MCP connected and showing 39 tools available](./img/codex_connected.png)
31 | 
32 | ## Project Instructions
33 | 
34 | For optimal results, create a `AGENTS.md` file in your project root with the instructions same with [main README's Claude Project Setup section](../README.md#-claude-project-setup).
35 | 
```

--------------------------------------------------------------------------------
/tests/integration/n8n-api/scripts/cleanup-orphans.ts:
--------------------------------------------------------------------------------

```typescript
 1 | #!/usr/bin/env tsx
 2 | /**
 3 |  * Cleanup Orphaned Test Resources
 4 |  *
 5 |  * Standalone script to clean up orphaned workflows and executions
 6 |  * from failed test runs. Run this periodically in CI or manually
 7 |  * to maintain a clean test environment.
 8 |  *
 9 |  * Usage:
10 |  *   npm run test:cleanup:orphans
11 |  *   tsx tests/integration/n8n-api/scripts/cleanup-orphans.ts
12 |  */
13 | 
14 | import { cleanupAllTestResources } from '../utils/cleanup-helpers';
15 | import { getN8nCredentials, validateCredentials } from '../utils/credentials';
16 | 
17 | async function main() {
18 |   console.log('Starting cleanup of orphaned test resources...\n');
19 | 
20 |   try {
21 |     // Validate credentials
22 |     const creds = getN8nCredentials();
23 |     validateCredentials(creds);
24 | 
25 |     console.log(`n8n Instance: ${creds.url}`);
26 |     console.log(`Cleanup Tag: ${creds.cleanup.tag}`);
27 |     console.log(`Cleanup Prefix: ${creds.cleanup.namePrefix}\n`);
28 | 
29 |     // Run cleanup
30 |     const result = await cleanupAllTestResources();
31 | 
32 |     console.log('\n✅ Cleanup complete!');
33 |     console.log(`   Workflows deleted: ${result.workflows}`);
34 |     console.log(`   Executions deleted: ${result.executions}`);
35 | 
36 |     process.exit(0);
37 |   } catch (error) {
38 |     console.error('\n❌ Cleanup failed:', error);
39 |     process.exit(1);
40 |   }
41 | }
42 | 
43 | main();
44 | 
```

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

```typescript
 1 | #!/usr/bin/env npx tsx
 2 | /**
 3 |  * Direct telemetry test with hardcoded credentials
 4 |  */
 5 | 
 6 | import { createClient } from '@supabase/supabase-js';
 7 | 
 8 | const TELEMETRY_BACKEND = {
 9 |   URL: 'https://ydyufsohxdfpopqbubwk.supabase.co',
10 |   ANON_KEY: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InlkeXVmc29oeGRmcG9wcWJ1YndrIiwicm9sZSI6ImFub24iLCJpYXQiOjE3Mzc2MzAxMDgsImV4cCI6MjA1MzIwNjEwOH0.LsUTx9OsNtnqg-jxXaJPc84aBHVDehHiMaFoF2Ir8s0'
11 | };
12 | 
13 | async function testDirect() {
14 |   console.log('🧪 Direct Telemetry Test\n');
15 | 
16 |   const supabase = createClient(TELEMETRY_BACKEND.URL, TELEMETRY_BACKEND.ANON_KEY, {
17 |     auth: {
18 |       persistSession: false,
19 |       autoRefreshToken: false,
20 |     }
21 |   });
22 | 
23 |   const testEvent = {
24 |     user_id: 'direct-test-' + Date.now(),
25 |     event: 'direct_test',
26 |     properties: {
27 |       source: 'test-telemetry-direct.ts',
28 |       timestamp: new Date().toISOString()
29 |     }
30 |   };
31 | 
32 |   console.log('Sending event:', testEvent);
33 | 
34 |   const { data, error } = await supabase
35 |     .from('telemetry_events')
36 |     .insert([testEvent]);
37 | 
38 |   if (error) {
39 |     console.error('❌ Failed:', error);
40 |   } else {
41 |     console.log('✅ Success! Event sent directly to Supabase');
42 |     console.log('Response:', data);
43 |   }
44 | }
45 | 
46 | testDirect().catch(console.error);
47 | 
```

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

```bash
 1 | #!/bin/bash
 2 | # scripts/test-docker.sh
 3 | 
 4 | echo "🧪 Testing n8n-MCP Docker Deployment"
 5 | 
 6 | # Test 1: Build simple image
 7 | echo "1. Building simple Docker image..."
 8 | docker build -t n8n-mcp:test .
 9 | 
10 | # Test 2: Test stdio mode
11 | echo "2. Testing stdio mode..."
12 | echo '{"jsonrpc":"2.0","method":"tools/list","id":1}' | \
13 |   docker run --rm -i -e MCP_MODE=stdio n8n-mcp:test
14 | 
15 | # Test 3: Test HTTP mode
16 | echo "3. Testing HTTP mode..."
17 | docker run -d --name test-http \
18 |   -e MCP_MODE=http \
19 |   -e AUTH_TOKEN=test-token \
20 |   -p 3001:3000 \
21 |   n8n-mcp:test
22 | 
23 | sleep 5
24 | 
25 | # Check health
26 | curl -f http://localhost:3001/health || echo "Health check failed"
27 | 
28 | # Test auth
29 | curl -H "Authorization: Bearer test-token" \
30 |      -H "Content-Type: application/json" \
31 |      -d '{"jsonrpc":"2.0","method":"tools/list","id":1}' \
32 |      http://localhost:3001/mcp
33 | 
34 | docker stop test-http && docker rm test-http
35 | 
36 | # Test 4: Volume persistence
37 | echo "4. Testing volume persistence..."
38 | docker volume create test-data
39 | docker run -d --name test-persist \
40 |   -v test-data:/app/data \
41 |   -e MCP_MODE=http \
42 |   -e AUTH_TOKEN=test \
43 |   -p 3002:3000 \
44 |   n8n-mcp:test
45 | 
46 | sleep 10
47 | docker exec test-persist ls -la /app/data/nodes.db
48 | docker stop test-persist && docker rm test-persist
49 | docker volume rm test-data
50 | 
51 | echo "✅ Docker tests completed!"
```

--------------------------------------------------------------------------------
/src/errors/validation-service-error.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Custom error class for validation service failures
 3 |  */
 4 | export class ValidationServiceError extends Error {
 5 |   constructor(
 6 |     message: string,
 7 |     public readonly nodeType?: string,
 8 |     public readonly property?: string,
 9 |     public readonly cause?: Error
10 |   ) {
11 |     super(message);
12 |     this.name = 'ValidationServiceError';
13 | 
14 |     // Maintains proper stack trace for where our error was thrown (only available on V8)
15 |     if (Error.captureStackTrace) {
16 |       Error.captureStackTrace(this, ValidationServiceError);
17 |     }
18 |   }
19 | 
20 |   /**
21 |    * Create error for JSON parsing failure
22 |    */
23 |   static jsonParseError(nodeType: string, cause: Error): ValidationServiceError {
24 |     return new ValidationServiceError(
25 |       `Failed to parse JSON data for node ${nodeType}`,
26 |       nodeType,
27 |       undefined,
28 |       cause
29 |     );
30 |   }
31 | 
32 |   /**
33 |    * Create error for node not found
34 |    */
35 |   static nodeNotFound(nodeType: string): ValidationServiceError {
36 |     return new ValidationServiceError(
37 |       `Node type ${nodeType} not found in repository`,
38 |       nodeType
39 |     );
40 |   }
41 | 
42 |   /**
43 |    * Create error for critical data extraction failure
44 |    */
45 |   static dataExtractionError(nodeType: string, dataType: string, cause?: Error): ValidationServiceError {
46 |     return new ValidationServiceError(
47 |       `Failed to extract ${dataType} for node ${nodeType}`,
48 |       nodeType,
49 |       dataType,
50 |       cause
51 |     );
52 |   }
53 | }
```

--------------------------------------------------------------------------------
/scripts/build-optimized.sh:
--------------------------------------------------------------------------------

```bash
 1 | #!/bin/bash
 2 | # Optimized Docker build script - no n8n dependencies!
 3 | 
 4 | set -e
 5 | 
 6 | # Enable BuildKit
 7 | export DOCKER_BUILDKIT=1
 8 | export COMPOSE_DOCKER_CLI_BUILD=1
 9 | 
10 | echo "🚀 Building n8n-mcp (runtime-only, no n8n deps)..."
11 | echo "💡 This build assumes database is pre-built"
12 | 
13 | # Check if nodes.db exists
14 | if [ ! -f "data/nodes.db" ]; then
15 |     echo "⚠️  Warning: data/nodes.db not found!"
16 |     echo "   Run 'npm run rebuild' first to create the database"
17 |     exit 1
18 | fi
19 | 
20 | # Build with BuildKit
21 | echo "📦 Building Docker image..."
22 | 
23 | docker build \
24 |     --progress=plain \
25 |     --cache-from type=gha \
26 |     --cache-from type=registry,ref=ghcr.io/czlonkowski/n8n-mcp:buildcache \
27 |     --build-arg BUILDKIT_INLINE_CACHE=1 \
28 |     -t "n8n-mcp:latest" \
29 |     .
30 | 
31 | # Show image size
32 | echo ""
33 | echo "📊 Image size:"
34 | docker images n8n-mcp:latest --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}"
35 | 
36 | # Test the build
37 | echo ""
38 | echo "🧪 Testing build..."
39 | docker run --rm n8n-mcp:latest node -e "console.log('Build OK - Runtime dependencies only!')"
40 | 
41 | # Estimate size savings
42 | echo ""
43 | echo "💰 Size comparison:"
44 | echo "   Old approach (with n8n deps): ~1.5GB"
45 | echo "   New approach (runtime only):  ~280MB"
46 | echo "   Savings: ~82% smaller!"
47 | 
48 | echo ""
49 | echo "✅ Build complete!"
50 | echo ""
51 | echo "🎯 Next steps:"
52 | echo "  - Use 'docker run -p 3000:3000 -e AUTH_TOKEN=your-token n8n-mcp:latest' to run"
53 | echo "  - Use 'docker-compose up' for production deployment"
54 | echo "  - Remember to rebuild database locally before pushing!"
```

--------------------------------------------------------------------------------
/scripts/test-http-search.ts:
--------------------------------------------------------------------------------

```typescript
 1 | #\!/usr/bin/env node
 2 | 
 3 | import { N8NDocumentationMCPServer } from '../src/mcp/server';
 4 | 
 5 | async function testHttpSearch() {
 6 |   const server = new N8NDocumentationMCPServer();
 7 |   await new Promise(resolve => setTimeout(resolve, 1000));
 8 |   
 9 |   console.log('Testing search for "http"...\n');
10 |   
11 |   const result = await server.executeTool('search_nodes', {
12 |     query: 'http',
13 |     limit: 50 // Get more results to see where HTTP Request is
14 |   });
15 |   
16 |   console.log(`Total results: ${result.results.length}\n`);
17 |   
18 |   // Find HTTP Request node in results
19 |   const httpRequestIndex = result.results.findIndex((r: any) => 
20 |     r.nodeType === 'nodes-base.httpRequest'
21 |   );
22 |   
23 |   if (httpRequestIndex === -1) {
24 |     console.log('❌ HTTP Request node NOT FOUND in results\!');
25 |   } else {
26 |     console.log(`✅ HTTP Request found at position ${httpRequestIndex + 1}`);
27 |   }
28 |   
29 |   console.log('\nTop 10 results:');
30 |   result.results.slice(0, 10).forEach((r: any, i: number) => {
31 |     console.log(`${i + 1}. ${r.nodeType} - ${r.displayName}`);
32 |   });
33 |   
34 |   // Also check LIKE search directly
35 |   console.log('\n\nTesting LIKE search fallback:');
36 |   const serverAny = server as any;
37 |   const likeResult = await serverAny.searchNodesLIKE('http', 20);
38 |   
39 |   console.log(`LIKE search found ${likeResult.results.length} results`);
40 |   console.log('Top 5 LIKE results:');
41 |   likeResult.results.slice(0, 5).forEach((r: any, i: number) => {
42 |     console.log(`${i + 1}. ${r.nodeType} - ${r.displayName}`);
43 |   });
44 | }
45 | 
46 | testHttpSearch().catch(console.error);
47 | 
```

--------------------------------------------------------------------------------
/docker-compose.extract.yml:
--------------------------------------------------------------------------------

```yaml
 1 | version: '3.8'
 2 | 
 3 | services:
 4 |   # Latest n8n container for node extraction
 5 |   n8n-latest:
 6 |     image: n8nio/n8n:latest
 7 |     container_name: n8n-latest-extractor
 8 |     environment:
 9 |       - N8N_BASIC_AUTH_ACTIVE=false
10 |       - N8N_PORT=5678
11 |       - N8N_ENCRYPTION_KEY=dummy-key-for-extraction
12 |     volumes:
13 |       # Mount n8n's node_modules for extraction
14 |       - n8n_modules:/usr/local/lib/node_modules/n8n/node_modules
15 |       # Provide writable directory for n8n config
16 |       - n8n_config:/home/node/.n8n
17 |     # We don't need n8n to stay running, just to install modules
18 |     entrypoint: ["sh", "-c", "sleep 300"]
19 |     healthcheck:
20 |       test: ["CMD-SHELL", "ls /usr/local/lib/node_modules/n8n/node_modules/n8n-nodes-base > /dev/null 2>&1"]
21 |       interval: 5s
22 |       timeout: 30s
23 |       retries: 20
24 | 
25 |   # Extractor service that will read from the mounted volumes
26 |   node-extractor:
27 |     image: node:22-alpine
28 |     container_name: n8n-node-extractor
29 |     working_dir: /app
30 |     depends_on:
31 |       n8n-latest:
32 |         condition: service_healthy
33 |     volumes:
34 |       # Mount the n8n modules from the n8n container
35 |       - n8n_modules:/n8n-modules:ro
36 |       - n8n_custom:/n8n-custom:ro
37 |       # Mount our project files
38 |       - ./:/app
39 |     environment:
40 |       - NODE_ENV=development
41 |       - NODE_DB_PATH=/app/data/nodes-fresh.db
42 |       - N8N_MODULES_PATH=/n8n-modules
43 |       - N8N_CUSTOM_PATH=/n8n-custom
44 |     command: /bin/sh -c "apk add --no-cache sqlite && node /app/scripts/extract-from-docker.js"
45 | 
46 | volumes:
47 |   n8n_modules:
48 |   n8n_custom:
49 |   n8n_config:
```

--------------------------------------------------------------------------------
/scripts/test-workflow-insert.ts:
--------------------------------------------------------------------------------

```typescript
 1 | #!/usr/bin/env npx tsx
 2 | /**
 3 |  * Test direct workflow insert to Supabase
 4 |  */
 5 | 
 6 | import { createClient } from '@supabase/supabase-js';
 7 | 
 8 | const TELEMETRY_BACKEND = {
 9 |   URL: 'https://ydyufsohxdfpopqbubwk.supabase.co',
10 |   ANON_KEY: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InlkeXVmc29oeGRmcG9wcWJ1YndrIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTg3OTYyMDAsImV4cCI6MjA3NDM3MjIwMH0.xESphg6h5ozaDsm4Vla3QnDJGc6Nc_cpfoqTHRynkCk'
11 | };
12 | 
13 | async function testWorkflowInsert() {
14 |   const supabase = createClient(TELEMETRY_BACKEND.URL, TELEMETRY_BACKEND.ANON_KEY, {
15 |     auth: {
16 |       persistSession: false,
17 |       autoRefreshToken: false,
18 |     }
19 |   });
20 | 
21 |   const testWorkflow = {
22 |     user_id: 'direct-test-' + Date.now(),
23 |     workflow_hash: 'hash-direct-' + Date.now(),
24 |     node_count: 2,
25 |     node_types: ['webhook', 'http'],
26 |     has_trigger: true,
27 |     has_webhook: true,
28 |     complexity: 'simple' as const,
29 |     sanitized_workflow: {
30 |       nodes: [
31 |         { id: '1', type: 'webhook', parameters: {} },
32 |         { id: '2', type: 'http', parameters: {} }
33 |       ],
34 |       connections: {}
35 |     }
36 |   };
37 | 
38 |   console.log('Attempting direct insert to telemetry_workflows...');
39 |   console.log('Data:', JSON.stringify(testWorkflow, null, 2));
40 | 
41 |   const { data, error } = await supabase
42 |     .from('telemetry_workflows')
43 |     .insert([testWorkflow]);
44 | 
45 |   if (error) {
46 |     console.error('\n❌ Error:', error);
47 |   } else {
48 |     console.log('\n✅ Success! Workflow inserted');
49 |     if (data) {
50 |       console.log('Response:', data);
51 |     }
52 |   }
53 | }
54 | 
55 | testWorkflowInsert().catch(console.error);
```

--------------------------------------------------------------------------------
/.github/ABOUT.md:
--------------------------------------------------------------------------------

```markdown
 1 | # About n8n-MCP
 2 | 
 3 | **n8n-MCP** is a Model Context Protocol (MCP) server that gives AI assistants like Claude deep understanding of n8n's 525+ workflow automation nodes.
 4 | 
 5 | ## 🎯 What it does
 6 | 
 7 | - Provides AI assistants with instant access to n8n node documentation, properties, and examples
 8 | - Reduces workflow creation time from 45 minutes to 3 minutes (as tested by Claude)
 9 | - Eliminates guesswork with accurate node configurations and validation
10 | - Enables AI to build production-ready n8n workflows on the first try
11 | 
12 | ## 🚀 Key Features
13 | 
14 | - **Smart Search** - Find the right nodes instantly
15 | - **Essential Properties** - Get only what matters (10-20 properties instead of 200+)
16 | - **Task Templates** - Pre-configured settings for common automations
17 | - **Real-time Validation** - Catch errors before deployment
18 | - **Universal Compatibility** - Works with any Node.js version
19 | 
20 | ## 📊 Impact
21 | 
22 | > "Before MCP, I was translating. Now I'm composing." - Claude
23 | 
24 | - **6 errors → 0 errors** in workflow creation
25 | - **45 minutes → 3 minutes** development time
26 | - **100% node coverage** with 90% documentation
27 | - **263 AI-capable nodes** fully documented
28 | 
29 | ## 🔧 Use Cases
30 | 
31 | Perfect for:
32 | - AI assistants building n8n workflows
33 | - Developers learning n8n
34 | - Teams using AI for automation
35 | - Anyone tired of trial-and-error workflow building
36 | 
37 | ## 🏃 Get Started
38 | 
39 | ```bash
40 | # Quick start with Docker
41 | docker run -it ghcr.io/czlonkowski/n8n-mcp:latest
42 | ```
43 | 
44 | See the [README](../README.md) for full setup instructions.
45 | 
46 | ---
47 | 
48 | **Built with ❤️ for the n8n community** | Making AI + n8n workflow creation delightful
```

--------------------------------------------------------------------------------
/tests/integration/docker/test-helpers.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { promisify } from 'util';
 2 | import { exec as execCallback } from 'child_process';
 3 | 
 4 | export const exec = promisify(execCallback);
 5 | 
 6 | /**
 7 |  * Wait for a container to be healthy by checking the health endpoint
 8 |  */
 9 | export async function waitForHealthy(containerName: string, timeout = 10000): Promise<boolean> {
10 |   const startTime = Date.now();
11 |   
12 |   while (Date.now() - startTime < timeout) {
13 |     try {
14 |       const { stdout } = await exec(
15 |         `docker exec ${containerName} curl -s http://localhost:3000/health`
16 |       );
17 |       
18 |       if (stdout.includes('ok')) {
19 |         return true;
20 |       }
21 |     } catch (error) {
22 |       // Container might not be ready yet
23 |     }
24 |     
25 |     await new Promise(resolve => setTimeout(resolve, 500));
26 |   }
27 |   
28 |   return false;
29 | }
30 | 
31 | /**
32 |  * Check if a container is running in HTTP mode by verifying the server is listening
33 |  */
34 | export async function isRunningInHttpMode(containerName: string): Promise<boolean> {
35 |   try {
36 |     const { stdout } = await exec(
37 |       `docker exec ${containerName} sh -c "netstat -tln 2>/dev/null | grep :3000 || echo 'Not listening'"`
38 |     );
39 |     
40 |     return stdout.includes(':3000');
41 |   } catch {
42 |     return false;
43 |   }
44 | }
45 | 
46 | /**
47 |  * Get process environment variables from inside a running container
48 |  */
49 | export async function getProcessEnv(containerName: string, varName: string): Promise<string | null> {
50 |   try {
51 |     const { stdout } = await exec(
52 |       `docker exec ${containerName} sh -c "cat /proc/1/environ | tr '\\0' '\\n' | grep '^${varName}=' | cut -d= -f2-"`
53 |     );
54 |     
55 |     return stdout.trim() || null;
56 |   } catch {
57 |     return null;
58 |   }
59 | }
```

--------------------------------------------------------------------------------
/tests/debug-slack-doc.js:
--------------------------------------------------------------------------------

```javascript
 1 | #!/usr/bin/env node
 2 | 
 3 | const { execSync } = require('child_process');
 4 | const path = require('path');
 5 | 
 6 | const tempDir = path.join(process.cwd(), 'temp', 'n8n-docs');
 7 | 
 8 | console.log('🔍 Debugging Slack documentation search...\n');
 9 | 
10 | // Search for all Slack related files
11 | console.log('All Slack-related markdown files:');
12 | try {
13 |   const allSlackFiles = execSync(
14 |     `find ${tempDir}/docs/integrations/builtin -name "*slack*.md" -type f`,
15 |     { encoding: 'utf-8' }
16 |   ).trim().split('\n');
17 |   
18 |   allSlackFiles.forEach(file => {
19 |     console.log(`  - ${file}`);
20 |   });
21 | } catch (error) {
22 |   console.log('  No files found');
23 | }
24 | 
25 | console.log('\n📄 Checking file paths:');
26 | const possiblePaths = [
27 |   'docs/integrations/builtin/app-nodes/n8n-nodes-base.Slack.md',
28 |   'docs/integrations/builtin/app-nodes/n8n-nodes-base.slack.md',
29 |   'docs/integrations/builtin/core-nodes/n8n-nodes-base.Slack.md',
30 |   'docs/integrations/builtin/core-nodes/n8n-nodes-base.slack.md',
31 |   'docs/integrations/builtin/trigger-nodes/n8n-nodes-base.Slack.md',
32 |   'docs/integrations/builtin/trigger-nodes/n8n-nodes-base.slack.md',
33 |   'docs/integrations/builtin/credentials/slack.md',
34 | ];
35 | 
36 | const fs = require('fs');
37 | possiblePaths.forEach(p => {
38 |   const fullPath = path.join(tempDir, p);
39 |   const exists = fs.existsSync(fullPath);
40 |   console.log(`  ${exists ? '✓' : '✗'} ${p}`);
41 |   
42 |   if (exists) {
43 |     // Read first few lines
44 |     const content = fs.readFileSync(fullPath, 'utf-8');
45 |     const lines = content.split('\n').slice(0, 10);
46 |     const title = lines.find(l => l.includes('title:'));
47 |     if (title) {
48 |       console.log(`    Title: ${title.trim()}`);
49 |     }
50 |   }
51 | });
```

--------------------------------------------------------------------------------
/scripts/analyze-optimization.sh:
--------------------------------------------------------------------------------

```bash
 1 | #!/bin/bash
 2 | # Analyze potential optimization savings
 3 | 
 4 | echo "🔍 Analyzing Docker Optimization Potential"
 5 | echo "=========================================="
 6 | 
 7 | # Check current database size
 8 | if [ -f data/nodes.db ]; then
 9 |     DB_SIZE=$(du -h data/nodes.db | cut -f1)
10 |     echo "Current database size: $DB_SIZE"
11 | fi
12 | 
13 | # Check node_modules size
14 | if [ -d node_modules ]; then
15 |     echo -e "\n📦 Package sizes:"
16 |     echo "Total node_modules: $(du -sh node_modules | cut -f1)"
17 |     echo "n8n packages:"
18 |     for pkg in n8n n8n-core n8n-workflow @n8n/n8n-nodes-langchain; do
19 |         if [ -d "node_modules/$pkg" ]; then
20 |             SIZE=$(du -sh "node_modules/$pkg" 2>/dev/null | cut -f1 || echo "N/A")
21 |             echo "  - $pkg: $SIZE"
22 |         fi
23 |     done
24 | fi
25 | 
26 | # Check runtime dependencies
27 | echo -e "\n🎯 Runtime-only dependencies:"
28 | RUNTIME_DEPS="@modelcontextprotocol/sdk better-sqlite3 sql.js express dotenv"
29 | RUNTIME_SIZE=0
30 | for dep in $RUNTIME_DEPS; do
31 |     if [ -d "node_modules/$dep" ]; then
32 |         SIZE=$(du -sh "node_modules/$dep" 2>/dev/null | cut -f1 || echo "0")
33 |         echo "  - $dep: $SIZE"
34 |     fi
35 | done
36 | 
37 | # Estimate savings
38 | echo -e "\n💡 Optimization potential:"
39 | echo "- Current image: 2.61GB"
40 | echo "- Estimated optimized: ~200MB"
41 | echo "- Savings: ~92%"
42 | 
43 | # Show what would be removed
44 | echo -e "\n🗑️  Would remove in optimization:"
45 | echo "- n8n packages (>2GB)"
46 | echo "- Build dependencies"
47 | echo "- Documentation files"
48 | echo "- Test files"
49 | echo "- Source maps"
50 | 
51 | # Check if optimized database exists
52 | if [ -f data/nodes-optimized.db ]; then
53 |     OPT_SIZE=$(du -h data/nodes-optimized.db | cut -f1)
54 |     echo -e "\n✅ Optimized database exists: $OPT_SIZE"
55 | fi
```

--------------------------------------------------------------------------------
/scripts/export-webhook-workflows.ts:
--------------------------------------------------------------------------------

```typescript
 1 | #!/usr/bin/env tsx
 2 | 
 3 | /**
 4 |  * Export Webhook Workflow JSONs
 5 |  *
 6 |  * Generates the 4 webhook workflow JSON files needed for integration testing.
 7 |  * These workflows must be imported into n8n and activated manually.
 8 |  */
 9 | 
10 | import { writeFileSync, mkdirSync } from 'fs';
11 | import { join } from 'path';
12 | import { exportAllWebhookWorkflows } from '../tests/integration/n8n-api/utils/webhook-workflows';
13 | 
14 | const OUTPUT_DIR = join(process.cwd(), 'workflows-for-import');
15 | 
16 | // Create output directory
17 | mkdirSync(OUTPUT_DIR, { recursive: true });
18 | 
19 | // Generate all workflow JSONs
20 | const workflows = exportAllWebhookWorkflows();
21 | 
22 | // Write each workflow to a separate file
23 | Object.entries(workflows).forEach(([method, workflow]) => {
24 |   const filename = `webhook-${method.toLowerCase()}.json`;
25 |   const filepath = join(OUTPUT_DIR, filename);
26 | 
27 |   writeFileSync(filepath, JSON.stringify(workflow, null, 2), 'utf-8');
28 | 
29 |   console.log(`✓ Generated: ${filename}`);
30 | });
31 | 
32 | console.log(`\n✓ All workflow JSONs written to: ${OUTPUT_DIR}`);
33 | console.log('\nNext steps:');
34 | console.log('1. Import each JSON file into your n8n instance');
35 | console.log('2. Activate each workflow in the n8n UI');
36 | console.log('3. Copy the webhook URLs from each workflow (open workflow → Webhook node → copy URL)');
37 | console.log('4. Add them to your .env file:');
38 | console.log('   N8N_TEST_WEBHOOK_GET_URL=https://your-n8n.com/webhook/mcp-test-get');
39 | console.log('   N8N_TEST_WEBHOOK_POST_URL=https://your-n8n.com/webhook/mcp-test-post');
40 | console.log('   N8N_TEST_WEBHOOK_PUT_URL=https://your-n8n.com/webhook/mcp-test-put');
41 | console.log('   N8N_TEST_WEBHOOK_DELETE_URL=https://your-n8n.com/webhook/mcp-test-delete');
42 | 
```

--------------------------------------------------------------------------------
/.github/workflows/docker-build-fast.yml:
--------------------------------------------------------------------------------

```yaml
 1 | # .github/workflows/docker-build-fast.yml
 2 | name: Build Docker (AMD64 only - Fast)
 3 | 
 4 | on:
 5 |   workflow_dispatch:
 6 |   pull_request:
 7 |     branches:
 8 |       - main
 9 |     paths:
10 |       - 'Dockerfile'
11 |       - 'package.runtime.json'
12 |       - '.github/workflows/docker-build-fast.yml'
13 | 
14 | env:
15 |   REGISTRY: ghcr.io
16 |   IMAGE_NAME: ${{ github.repository }}
17 | 
18 | jobs:
19 |   build-amd64:
20 |     name: Build Docker Image (AMD64 only)
21 |     runs-on: ubuntu-latest
22 |     permissions:
23 |       contents: read
24 |       packages: write
25 |       
26 |     steps:
27 |       - name: Checkout repository
28 |         uses: actions/checkout@v4
29 |         with:
30 |           lfs: true
31 |         
32 |       - name: Set up Docker Buildx
33 |         uses: docker/setup-buildx-action@v3
34 |         
35 |       - name: Log in to GitHub Container Registry
36 |         if: github.event_name != 'pull_request'
37 |         uses: docker/login-action@v3
38 |         with:
39 |           registry: ${{ env.REGISTRY }}
40 |           username: ${{ github.actor }}
41 |           password: ${{ secrets.GITHUB_TOKEN }}
42 |           
43 |       - name: Extract metadata
44 |         id: meta
45 |         uses: docker/metadata-action@v5
46 |         with:
47 |           images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
48 |           tags: |
49 |             type=ref,event=pr,suffix=-amd64
50 |             type=raw,value=test-amd64
51 |             
52 |       - name: Build and push Docker image (AMD64 only)
53 |         uses: docker/build-push-action@v5
54 |         with:
55 |           context: .
56 |           platforms: linux/amd64
57 |           push: ${{ github.event_name != 'pull_request' }}
58 |           tags: ${{ steps.meta.outputs.tags }}
59 |           labels: ${{ steps.meta.outputs.labels }}
60 |           cache-from: type=gha
61 |           cache-to: type=gha,mode=max
62 |           provenance: false
```

--------------------------------------------------------------------------------
/scripts/test-telemetry-no-select.ts:
--------------------------------------------------------------------------------

```typescript
 1 | #!/usr/bin/env npx tsx
 2 | /**
 3 |  * Test telemetry without requesting data back
 4 |  */
 5 | 
 6 | import { createClient } from '@supabase/supabase-js';
 7 | import dotenv from 'dotenv';
 8 | 
 9 | dotenv.config();
10 | 
11 | async function testNoSelect() {
12 |   const supabaseUrl = process.env.SUPABASE_URL!;
13 |   const supabaseAnonKey = process.env.SUPABASE_ANON_KEY!;
14 | 
15 |   console.log('🧪 Telemetry Test (No Select)\n');
16 | 
17 |   const supabase = createClient(supabaseUrl, supabaseAnonKey, {
18 |     auth: {
19 |       persistSession: false,
20 |       autoRefreshToken: false,
21 |     }
22 |   });
23 | 
24 |   // Insert WITHOUT .select() - just fire and forget
25 |   const testData = {
26 |     user_id: 'test-' + Date.now(),
27 |     event: 'test_event',
28 |     properties: { test: true }
29 |   };
30 | 
31 |   console.log('Inserting:', testData);
32 | 
33 |   const { error } = await supabase
34 |     .from('telemetry_events')
35 |     .insert([testData]);  // No .select() here!
36 | 
37 |   if (error) {
38 |     console.error('❌ Failed:', error);
39 |   } else {
40 |     console.log('✅ Success! Data inserted (no response data)');
41 |   }
42 | 
43 |   // Test workflow insert too
44 |   const testWorkflow = {
45 |     user_id: 'test-' + Date.now(),
46 |     workflow_hash: 'hash-' + Date.now(),
47 |     node_count: 3,
48 |     node_types: ['webhook', 'http', 'slack'],
49 |     has_trigger: true,
50 |     has_webhook: true,
51 |     complexity: 'simple',
52 |     sanitized_workflow: { nodes: [], connections: {} }
53 |   };
54 | 
55 |   console.log('\nInserting workflow:', testWorkflow);
56 | 
57 |   const { error: workflowError } = await supabase
58 |     .from('telemetry_workflows')
59 |     .insert([testWorkflow]);  // No .select() here!
60 | 
61 |   if (workflowError) {
62 |     console.error('❌ Workflow failed:', workflowError);
63 |   } else {
64 |     console.log('✅ Workflow inserted successfully!');
65 |   }
66 | }
67 | 
68 | testNoSelect().catch(console.error);
```

--------------------------------------------------------------------------------
/scripts/test-single-session.sh:
--------------------------------------------------------------------------------

```bash
 1 | #!/bin/bash
 2 | # Test script for single-session HTTP server
 3 | 
 4 | set -e
 5 | 
 6 | echo "🧪 Testing Single-Session HTTP Server..."
 7 | echo
 8 | 
 9 | # Generate test auth token if not set
10 | if [ -z "$AUTH_TOKEN" ]; then
11 |   export AUTH_TOKEN="test-token-$(date +%s)"
12 |   echo "Generated test AUTH_TOKEN: $AUTH_TOKEN"
13 | fi
14 | 
15 | # Start server in background
16 | echo "Starting server..."
17 | MCP_MODE=http npm start > server.log 2>&1 &
18 | SERVER_PID=$!
19 | 
20 | # Wait for server to start
21 | echo "Waiting for server to start..."
22 | sleep 3
23 | 
24 | # Check health endpoint
25 | echo
26 | echo "Testing health endpoint..."
27 | curl -s http://localhost:3000/health | jq .
28 | 
29 | # Test authentication failure
30 | echo
31 | echo "Testing authentication failure..."
32 | curl -s -X POST http://localhost:3000/mcp \
33 |   -H "Content-Type: application/json" \
34 |   -H "Authorization: Bearer wrong-token" \
35 |   -d '{"jsonrpc":"2.0","method":"tools/list","id":1}' | jq .
36 | 
37 | # Test successful request
38 | echo
39 | echo "Testing successful request..."
40 | curl -s -X POST http://localhost:3000/mcp \
41 |   -H "Content-Type: application/json" \
42 |   -H "Authorization: Bearer $AUTH_TOKEN" \
43 |   -d '{"jsonrpc":"2.0","method":"tools/list","id":1}' | jq .
44 | 
45 | # Test session reuse
46 | echo
47 | echo "Testing session reuse (second request)..."
48 | curl -s -X POST http://localhost:3000/mcp \
49 |   -H "Content-Type: application/json" \
50 |   -H "Authorization: Bearer $AUTH_TOKEN" \
51 |   -d '{"jsonrpc":"2.0","method":"get_database_statistics","id":2}' | jq .
52 | 
53 | # Check health again to see session info
54 | echo
55 | echo "Checking health to see session info..."
56 | curl -s http://localhost:3000/health | jq .
57 | 
58 | # Clean up
59 | echo
60 | echo "Stopping server..."
61 | kill $SERVER_PID 2>/dev/null || true
62 | wait $SERVER_PID 2>/dev/null || true
63 | 
64 | echo
65 | echo "✅ Test complete! Check server.log for details."
```

--------------------------------------------------------------------------------
/tests/integration/n8n-api/utils/n8n-client.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Pre-configured n8n API Client for Integration Tests
 3 |  *
 4 |  * Provides a singleton API client instance configured with test credentials.
 5 |  * Automatically loads credentials from .env (local) or GitHub secrets (CI).
 6 |  */
 7 | 
 8 | import { N8nApiClient } from '../../../../src/services/n8n-api-client';
 9 | import { getN8nCredentials, validateCredentials } from './credentials';
10 | 
11 | let client: N8nApiClient | null = null;
12 | 
13 | /**
14 |  * Get or create the test n8n API client
15 |  *
16 |  * Creates a singleton instance configured with credentials from
17 |  * the environment. Validates that required credentials are present.
18 |  *
19 |  * @returns Configured N8nApiClient instance
20 |  * @throws Error if credentials are missing or invalid
21 |  *
22 |  * @example
23 |  * const client = getTestN8nClient();
24 |  * const workflow = await client.createWorkflow({ ... });
25 |  */
26 | export function getTestN8nClient(): N8nApiClient {
27 |   if (!client) {
28 |     const creds = getN8nCredentials();
29 |     validateCredentials(creds);
30 |     client = new N8nApiClient({
31 |       baseUrl: creds.url,
32 |       apiKey: creds.apiKey,
33 |       timeout: 30000,
34 |       maxRetries: 3
35 |     });
36 |   }
37 |   return client;
38 | }
39 | 
40 | /**
41 |  * Reset the test client instance
42 |  *
43 |  * Forces recreation of the client on next call to getTestN8nClient().
44 |  * Useful for testing or when credentials change.
45 |  */
46 | export function resetTestN8nClient(): void {
47 |   client = null;
48 | }
49 | 
50 | /**
51 |  * Check if the n8n API is accessible
52 |  *
53 |  * Performs a health check to verify API connectivity.
54 |  *
55 |  * @returns true if API is accessible, false otherwise
56 |  */
57 | export async function isN8nApiAccessible(): Promise<boolean> {
58 |   try {
59 |     const client = getTestN8nClient();
60 |     await client.healthCheck();
61 |     return true;
62 |   } catch {
63 |     return false;
64 |   }
65 | }
66 | 
```

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

```typescript
 1 | import { Factory } from 'fishery';
 2 | import { faker } from '@faker-js/faker';
 3 | import { ParsedNode } from '../../src/parsers/node-parser';
 4 | 
 5 | /**
 6 |  * Factory for generating ParsedNode test data using Fishery.
 7 |  * Creates realistic node configurations with random but valid data.
 8 |  * 
 9 |  * @example
10 |  * ```typescript
11 |  * // Create a single node with defaults
12 |  * const node = NodeFactory.build();
13 |  * 
14 |  * // Create a node with specific properties
15 |  * const slackNode = NodeFactory.build({
16 |  *   nodeType: 'nodes-base.slack',
17 |  *   displayName: 'Slack',
18 |  *   isAITool: true
19 |  * });
20 |  * 
21 |  * // Create multiple nodes
22 |  * const nodes = NodeFactory.buildList(5);
23 |  * 
24 |  * // Create with custom sequence
25 |  * const sequencedNodes = NodeFactory.buildList(3, {
26 |  *   displayName: (i) => `Node ${i}`
27 |  * });
28 |  * ```
29 |  */
30 | export const NodeFactory = Factory.define<ParsedNode>(() => ({
31 |   nodeType: faker.helpers.arrayElement(['nodes-base.', 'nodes-langchain.']) + faker.word.noun(),
32 |   displayName: faker.helpers.arrayElement(['HTTP', 'Slack', 'Google', 'AWS']) + ' ' + faker.word.noun(),
33 |   description: faker.lorem.sentence(),
34 |   packageName: faker.helpers.arrayElement(['n8n-nodes-base', '@n8n/n8n-nodes-langchain']),
35 |   category: faker.helpers.arrayElement(['transform', 'trigger', 'output', 'input']),
36 |   style: faker.helpers.arrayElement(['declarative', 'programmatic']),
37 |   isAITool: faker.datatype.boolean(),
38 |   isTrigger: faker.datatype.boolean(),
39 |   isWebhook: faker.datatype.boolean(),
40 |   isVersioned: faker.datatype.boolean(),
41 |   version: faker.helpers.arrayElement(['1.0', '2.0', '3.0', '4.2']),
42 |   documentation: faker.datatype.boolean() ? faker.lorem.paragraphs(3) : undefined,
43 |   properties: [],
44 |   operations: [],
45 |   credentials: []
46 | }));
```

--------------------------------------------------------------------------------
/docs/WINDSURF_SETUP.md:
--------------------------------------------------------------------------------

```markdown
 1 | # Windsurf Setup
 2 | 
 3 | Connect n8n-MCP to Windsurf IDE for enhanced n8n workflow development with AI assistance.
 4 | 
 5 | [![n8n-mcp Windsurf Setup Tutorial](./img/windsurf_tut.png)](https://www.youtube.com/watch?v=klxxT1__izg)
 6 | 
 7 | ## Video Tutorial
 8 | 
 9 | Watch the complete setup process: [n8n-MCP Windsurf Setup Tutorial](https://www.youtube.com/watch?v=klxxT1__izg)
10 | 
11 | ## Setup Process
12 | 
13 | ### 1. Access MCP Configuration
14 | 
15 | 1. Go to Settings in Windsurf
16 | 2. Navigate to Windsurf Settings
17 | 3. Go to MCP Servers > Manage Plugins
18 | 4. Click "View Raw Config"
19 | 
20 | ### 2. Add n8n-MCP Configuration
21 | 
22 | Copy the configuration from this repository and add it to your MCP config:
23 | 
24 | **Basic configuration (documentation tools only):**
25 | ```json
26 | {
27 |   "mcpServers": {
28 |     "n8n-mcp": {
29 |       "command": "npx",
30 |       "args": ["n8n-mcp"],
31 |       "env": {
32 |         "MCP_MODE": "stdio",
33 |         "LOG_LEVEL": "error",
34 |         "DISABLE_CONSOLE_OUTPUT": "true"
35 |       }
36 |     }
37 |   }
38 | }
39 | ```
40 | 
41 | **Full configuration (with n8n management tools):**
42 | ```json
43 | {
44 |   "mcpServers": {
45 |     "n8n-mcp": {
46 |       "command": "npx",
47 |       "args": ["n8n-mcp"],
48 |       "env": {
49 |         "MCP_MODE": "stdio",
50 |         "LOG_LEVEL": "error",
51 |         "DISABLE_CONSOLE_OUTPUT": "true",
52 |         "N8N_API_URL": "https://your-n8n-instance.com",
53 |         "N8N_API_KEY": "your-api-key"
54 |       }
55 |     }
56 |   }
57 | }
58 | ```
59 | 
60 | ### 3. Configure n8n Connection
61 | 
62 | 1. Replace `https://your-n8n-instance.com` with your actual n8n URL
63 | 2. Replace `your-api-key` with your n8n API key
64 | 3. Click refresh to apply the changes
65 | 
66 | ### 4. Set Up Project Instructions
67 | 
68 | 1. Create a `.windsurfrules` file in your project root
69 | 2. Copy the Claude Project instructions from the [main README's Claude Project Setup section](../README.md#-claude-project-setup)
70 | 
```

--------------------------------------------------------------------------------
/scripts/test-workflow-tracking-debug.ts:
--------------------------------------------------------------------------------

```typescript
 1 | #!/usr/bin/env npx tsx
 2 | /**
 3 |  * Debug workflow tracking in telemetry manager
 4 |  */
 5 | 
 6 | import { TelemetryManager } from '../src/telemetry/telemetry-manager';
 7 | 
 8 | // Get the singleton instance
 9 | const telemetry = TelemetryManager.getInstance();
10 | 
11 | const testWorkflow = {
12 |   nodes: [
13 |     {
14 |       id: 'webhook1',
15 |       type: 'n8n-nodes-base.webhook',
16 |       name: 'Webhook',
17 |       position: [0, 0],
18 |       parameters: { 
19 |         path: '/test-' + Date.now(),
20 |         httpMethod: 'POST'
21 |       }
22 |     },
23 |     {
24 |       id: 'http1',
25 |       type: 'n8n-nodes-base.httpRequest',
26 |       name: 'HTTP Request',
27 |       position: [250, 0],
28 |       parameters: { 
29 |         url: 'https://api.example.com/data',
30 |         method: 'GET'
31 |       }
32 |     },
33 |     {
34 |       id: 'slack1',
35 |       type: 'n8n-nodes-base.slack',
36 |       name: 'Slack',
37 |       position: [500, 0],
38 |       parameters: {
39 |         channel: '#general',
40 |         text: 'Workflow complete!'
41 |       }
42 |     }
43 |   ],
44 |   connections: {
45 |     'webhook1': {
46 |       main: [[{ node: 'http1', type: 'main', index: 0 }]]
47 |     },
48 |     'http1': {
49 |       main: [[{ node: 'slack1', type: 'main', index: 0 }]]
50 |     }
51 |   }
52 | };
53 | 
54 | console.log('🧪 Testing Workflow Tracking\n');
55 | console.log('Workflow has', testWorkflow.nodes.length, 'nodes');
56 | 
57 | // Track the workflow
58 | console.log('Calling trackWorkflowCreation...');
59 | telemetry.trackWorkflowCreation(testWorkflow, true);
60 | 
61 | console.log('Waiting for async processing...');
62 | 
63 | // Wait for setImmediate to process
64 | setTimeout(async () => {
65 |   console.log('\nForcing flush...');
66 |   await telemetry.flush();
67 |   console.log('✅ Flush complete!');
68 |   
69 |   console.log('\nWorkflow should now be in the telemetry_workflows table.');
70 |   console.log('Check with: SELECT * FROM telemetry_workflows ORDER BY created_at DESC LIMIT 1;');
71 | }, 2000);
72 | 
```

--------------------------------------------------------------------------------
/scripts/test-workflow-sanitizer.ts:
--------------------------------------------------------------------------------

```typescript
 1 | #!/usr/bin/env npx tsx
 2 | /**
 3 |  * Test workflow sanitizer
 4 |  */
 5 | 
 6 | import { WorkflowSanitizer } from '../src/telemetry/workflow-sanitizer';
 7 | 
 8 | const testWorkflow = {
 9 |   nodes: [
10 |     {
11 |       id: 'webhook1',
12 |       type: 'n8n-nodes-base.webhook',
13 |       name: 'Webhook',
14 |       position: [0, 0],
15 |       parameters: { 
16 |         path: '/test-webhook',
17 |         httpMethod: 'POST'
18 |       }
19 |     },
20 |     {
21 |       id: 'http1',
22 |       type: 'n8n-nodes-base.httpRequest',
23 |       name: 'HTTP Request',
24 |       position: [250, 0],
25 |       parameters: { 
26 |         url: 'https://api.example.com/endpoint',
27 |         method: 'GET',
28 |         authentication: 'genericCredentialType',
29 |         sendHeaders: true,
30 |         headerParameters: {
31 |           parameters: [
32 |             {
33 |               name: 'Authorization',
34 |               value: 'Bearer sk-1234567890abcdef'
35 |             }
36 |           ]
37 |         }
38 |       }
39 |     }
40 |   ],
41 |   connections: {
42 |     'webhook1': {
43 |       main: [[{ node: 'http1', type: 'main', index: 0 }]]
44 |     }
45 |   }
46 | };
47 | 
48 | console.log('🧪 Testing Workflow Sanitizer\n');
49 | console.log('Original workflow has', testWorkflow.nodes.length, 'nodes');
50 | 
51 | try {
52 |   const sanitized = WorkflowSanitizer.sanitizeWorkflow(testWorkflow);
53 |   
54 |   console.log('\n✅ Sanitization successful!');
55 |   console.log('\nSanitized output:');
56 |   console.log(JSON.stringify(sanitized, null, 2));
57 |   
58 |   console.log('\n📊 Metrics:');
59 |   console.log('- Workflow Hash:', sanitized.workflowHash);
60 |   console.log('- Node Count:', sanitized.nodeCount);
61 |   console.log('- Node Types:', sanitized.nodeTypes);
62 |   console.log('- Has Trigger:', sanitized.hasTrigger);
63 |   console.log('- Has Webhook:', sanitized.hasWebhook);
64 |   console.log('- Complexity:', sanitized.complexity);
65 | } catch (error) {
66 |   console.error('❌ Sanitization failed:', error);
67 | }
68 | 
```

--------------------------------------------------------------------------------
/src/mcp/tool-docs/templates/list-tasks.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { ToolDocumentation } from '../types';
 2 | 
 3 | export const listTasksDoc: ToolDocumentation = {
 4 |   name: 'list_tasks',
 5 |   category: 'templates',
 6 |   essentials: {
 7 |     description: 'List task templates by category: HTTP/API, Webhooks, Database, AI, Data Processing, Communication.',
 8 |     keyParameters: ['category'],
 9 |     example: 'list_tasks({category: "HTTP/API"})',
10 |     performance: 'Instant',
11 |     tips: [
12 |       'Categories: HTTP/API, Webhooks, Database, AI',
13 |       'Shows pre-configured node settings',
14 |       'Use get_node_for_task for details'
15 |     ]
16 |   },
17 |   full: {
18 |     description: 'Lists available task templates organized by category. Each task represents a common automation pattern with pre-configured node settings. Categories include HTTP/API, Webhooks, Database, AI, Data Processing, and Communication.',
19 |     parameters: {
20 |       category: { type: 'string', description: 'Filter by category (optional)' }
21 |     },
22 |     returns: 'Array of tasks with name, category, description, nodeType',
23 |     examples: [
24 |       'list_tasks() - Get all task templates',
25 |       'list_tasks({category: "Database"}) - Database-related tasks',
26 |       'list_tasks({category: "AI"}) - AI automation tasks'
27 |     ],
28 |     useCases: [
29 |       'Discover common automation patterns',
30 |       'Find pre-configured solutions',
31 |       'Learn node usage patterns',
32 |       'Quick workflow setup'
33 |     ],
34 |     performance: 'Instant - Static task list',
35 |     bestPractices: [
36 |       'Browse all categories first',
37 |       'Use get_node_for_task for config',
38 |       'Combine multiple tasks in workflows'
39 |     ],
40 |     pitfalls: [
41 |       'Tasks are templates, customize as needed',
42 |       'Not all nodes have task templates'
43 |     ],
44 |     relatedTools: ['get_node_for_task', 'search_templates', 'get_templates_for_task']
45 |   }
46 | };
```

--------------------------------------------------------------------------------
/scripts/test-node-info.js:
--------------------------------------------------------------------------------

```javascript
 1 | #!/usr/bin/env node
 2 | /**
 3 |  * Test get_node_info to diagnose timeout issues
 4 |  */
 5 | 
 6 | const { N8NDocumentationMCPServer } = require('../dist/mcp/server');
 7 | 
 8 | async function testNodeInfo() {
 9 |   console.log('🔍 Testing get_node_info...\n');
10 |   
11 |   try {
12 |     const server = new N8NDocumentationMCPServer();
13 |     await new Promise(resolve => setTimeout(resolve, 500));
14 |     
15 |     const nodes = [
16 |       'nodes-base.httpRequest',
17 |       'nodes-base.webhook',
18 |       'nodes-langchain.agent'
19 |     ];
20 |     
21 |     for (const nodeType of nodes) {
22 |       console.log(`Testing ${nodeType}...`);
23 |       const start = Date.now();
24 |       
25 |       try {
26 |         const result = await server.executeTool('get_node_info', { nodeType });
27 |         const elapsed = Date.now() - start;
28 |         const size = JSON.stringify(result).length;
29 |         
30 |         console.log(`✅ Success in ${elapsed}ms`);
31 |         console.log(`   Size: ${(size / 1024).toFixed(1)}KB`);
32 |         console.log(`   Properties: ${result.properties?.length || 0}`);
33 |         console.log(`   Operations: ${result.operations?.length || 0}`);
34 |         
35 |         // Check for issues
36 |         if (size > 50000) {
37 |           console.log(`   ⚠️  WARNING: Response over 50KB!`);
38 |         }
39 |         
40 |         // Check property quality
41 |         const propsWithoutDesc = result.properties?.filter(p => !p.description && !p.displayName).length || 0;
42 |         if (propsWithoutDesc > 0) {
43 |           console.log(`   ⚠️  ${propsWithoutDesc} properties without descriptions`);
44 |         }
45 |         
46 |       } catch (error) {
47 |         const elapsed = Date.now() - start;
48 |         console.log(`❌ Failed after ${elapsed}ms: ${error.message}`);
49 |       }
50 |       
51 |       console.log('');
52 |     }
53 |     
54 |   } catch (error) {
55 |     console.error('Fatal error:', error);
56 |   }
57 | }
58 | 
59 | testNodeInfo().catch(console.error);
```

--------------------------------------------------------------------------------
/docs/CURSOR_SETUP.md:
--------------------------------------------------------------------------------

```markdown
 1 | # Cursor Setup
 2 | 
 3 | Connect n8n-MCP to Cursor IDE for enhanced n8n workflow development with AI assistance.
 4 | 
 5 | [![n8n-mcp Cursor Setup Tutorial](./img/cursor_tut.png)](https://www.youtube.com/watch?v=hRmVxzLGJWI)
 6 | 
 7 | ## Video Tutorial
 8 | 
 9 | Watch the complete setup process: [n8n-MCP Cursor Setup Tutorial](https://www.youtube.com/watch?v=hRmVxzLGJWI)
10 | 
11 | ## Setup Process
12 | 
13 | ### 1. Create MCP Configuration
14 | 
15 | 1. Create a `.cursor` folder in your project root
16 | 2. Create `mcp.json` file inside the `.cursor` folder
17 | 3. Copy the configuration from this repository
18 | 
19 | **Basic configuration (documentation tools only):**
20 | ```json
21 | {
22 |   "mcpServers": {
23 |     "n8n-mcp": {
24 |       "command": "npx",
25 |       "args": ["n8n-mcp"],
26 |       "env": {
27 |         "MCP_MODE": "stdio",
28 |         "LOG_LEVEL": "error",
29 |         "DISABLE_CONSOLE_OUTPUT": "true"
30 |       }
31 |     }
32 |   }
33 | }
34 | ```
35 | 
36 | **Full configuration (with n8n management tools):**
37 | ```json
38 | {
39 |   "mcpServers": {
40 |     "n8n-mcp": {
41 |       "command": "npx",
42 |       "args": ["n8n-mcp"],
43 |       "env": {
44 |         "MCP_MODE": "stdio",
45 |         "LOG_LEVEL": "error",
46 |         "DISABLE_CONSOLE_OUTPUT": "true",
47 |         "N8N_API_URL": "https://your-n8n-instance.com",
48 |         "N8N_API_KEY": "your-api-key"
49 |       }
50 |     }
51 |   }
52 | }
53 | ```
54 | 
55 | ### 2. Configure n8n Connection
56 | 
57 | 1. Replace `https://your-n8n-instance.com` with your actual n8n URL
58 | 2. Replace `your-api-key` with your n8n API key
59 | 
60 | ### 3. Enable MCP Server
61 | 
62 | 1. Click "Enable MCP Server" button in Cursor
63 | 2. Go to Cursor Settings
64 | 3. Search for "mcp"
65 | 4. Confirm MCP is working
66 | 
67 | ### 4. Set Up Project Instructions
68 | 
69 | 1. In your Cursor chat, invoke "create rule" and hit Tab
70 | 2. Name the rule (e.g., "n8n-mcp")
71 | 3. Set rule type to "always"
72 | 4. Copy the Claude Project instructions from the [main README's Claude Project Setup section](../README.md#-claude-project-setup)
73 | 
74 | 
```

--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "$schema": "https://docs.renovatebot.com/renovate-schema.json",
 3 |   "extends": [
 4 |     "config:base"
 5 |   ],
 6 |   "schedule": ["after 9am on monday"],
 7 |   "timezone": "UTC",
 8 |   "packageRules": [
 9 |     {
10 |       "description": "Group all n8n-related updates",
11 |       "groupName": "n8n dependencies",
12 |       "matchPackagePatterns": ["^n8n", "^@n8n/"],
13 |       "matchUpdateTypes": ["minor", "patch"],
14 |       "schedule": ["after 9am on monday"]
15 |     },
16 |     {
17 |       "description": "Require approval for major n8n updates",
18 |       "matchPackagePatterns": ["^n8n", "^@n8n/"],
19 |       "matchUpdateTypes": ["major"],
20 |       "dependencyDashboardApproval": true
21 |     },
22 |     {
23 |       "description": "Disable updates for other dependencies",
24 |       "excludePackagePatterns": ["^n8n", "^@n8n/"],
25 |       "enabled": false
26 |     }
27 |   ],
28 |   "postUpdateOptions": [
29 |     "npmDedupe"
30 |   ],
31 |   "prConcurrentLimit": 1,
32 |   "prCreation": "immediate",
33 |   "labels": ["dependencies", "n8n-update"],
34 |   "assignees": ["@czlonkowski"],
35 |   "reviewers": ["@czlonkowski"],
36 |   "commitMessagePrefix": "chore: ",
37 |   "commitMessageTopic": "{{depName}}",
38 |   "commitMessageExtra": "from {{currentVersion}} to {{newVersion}}",
39 |   "prBodyDefinitions": {
40 |     "Package": "{{depName}}",
41 |     "Type": "{{depType}}",
42 |     "Update": "{{updateType}}",
43 |     "Current": "{{currentVersion}}",
44 |     "New": "{{newVersion}}",
45 |     "Change": "[Compare]({{compareUrl}})"
46 |   },
47 |   "prBodyColumns": ["Package", "Type", "Update", "Current", "New", "Change"],
48 |   "prBodyNotes": [
49 |     "**Important**: Please review the [n8n release notes](https://docs.n8n.io/release-notes/) for breaking changes.",
50 |     "",
51 |     "After merging, please:",
52 |     "1. Run `npm run rebuild` to update the node database",
53 |     "2. Run `npm run validate` to ensure all nodes are properly loaded",
54 |     "3. Test critical functionality"
55 |   ]
56 | }
```

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

```typescript
 1 | import { ToolDocumentation } from '../types';
 2 | 
 3 | export const getNodeDocumentationDoc: ToolDocumentation = {
 4 |   name: 'get_node_documentation',
 5 |   category: 'configuration',
 6 |   essentials: {
 7 |     description: 'Get readable docs with examples/auth/patterns. Better than raw schema! 87% coverage. Format: "nodes-base.slack"',
 8 |     keyParameters: ['nodeType'],
 9 |     example: 'get_node_documentation({nodeType: "nodes-base.slack"})',
10 |     performance: 'Fast - pre-parsed',
11 |     tips: [
12 |       '87% coverage',
13 |       'Includes auth examples',
14 |       'Human-readable format'
15 |     ]
16 |   },
17 |   full: {
18 |     description: 'Returns human-readable documentation parsed from n8n-docs including examples, authentication setup, and common patterns. More useful than raw schema for understanding node usage.',
19 |     parameters: {
20 |       nodeType: { type: 'string', required: true, description: 'Full node type with prefix (e.g., "nodes-base.slack")' }
21 |     },
22 |     returns: 'Parsed markdown documentation with examples, authentication guides, common patterns',
23 |     examples: [
24 |       'get_node_documentation({nodeType: "nodes-base.slack"}) - Slack usage guide',
25 |       'get_node_documentation({nodeType: "nodes-base.googleSheets"}) - Sheets examples'
26 |     ],
27 |     useCases: [
28 |       'Understanding authentication setup',
29 |       'Finding usage examples',
30 |       'Learning common patterns'
31 |     ],
32 |     performance: 'Fast - Pre-parsed documentation stored in database',
33 |     bestPractices: [
34 |       'Use for learning node usage',
35 |       'Check coverage with get_database_statistics',
36 |       'Combine with get_node_essentials'
37 |     ],
38 |     pitfalls: [
39 |       'Not all nodes have docs (87% coverage)',
40 |       'May be outdated for new features',
41 |       'Requires full node type prefix'
42 |     ],
43 |     relatedTools: ['get_node_info', 'get_node_essentials', 'search_nodes']
44 |   }
45 | };
```

--------------------------------------------------------------------------------
/scripts/test-error-message-tracking.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Test script to verify error message tracking is working
 3 |  */
 4 | 
 5 | import { telemetry } from '../src/telemetry';
 6 | 
 7 | async function testErrorTracking() {
 8 |   console.log('=== Testing Error Message Tracking ===\n');
 9 | 
10 |   // Track session first
11 |   console.log('1. Starting session...');
12 |   telemetry.trackSessionStart();
13 | 
14 |   // Track an error WITH a message
15 |   console.log('\n2. Tracking error WITH message:');
16 |   const testErrorMessage = 'This is a test error message with sensitive data: password=secret123 and [email protected]';
17 |   telemetry.trackError(
18 |     'TypeError',
19 |     'tool_execution',
20 |     'test_tool',
21 |     testErrorMessage
22 |   );
23 |   console.log(`   Original message: "${testErrorMessage}"`);
24 | 
25 |   // Track an error WITHOUT a message
26 |   console.log('\n3. Tracking error WITHOUT message:');
27 |   telemetry.trackError(
28 |     'Error',
29 |     'tool_execution',
30 |     'test_tool2'
31 |   );
32 | 
33 |   // Check the event queue
34 |   const metrics = telemetry.getMetrics();
35 |   console.log('\n4. Telemetry metrics:');
36 |   console.log('   Status:', metrics.status);
37 |   console.log('   Events queued:', metrics.tracking.eventsQueued);
38 | 
39 |   // Get raw event queue to inspect
40 |   const eventTracker = (telemetry as any).eventTracker;
41 |   const queue = eventTracker.getEventQueue();
42 | 
43 |   console.log('\n5. Event queue contents:');
44 |   queue.forEach((event, i) => {
45 |     console.log(`\n   Event ${i + 1}:`);
46 |     console.log(`   - Type: ${event.event}`);
47 |     console.log(`   - Properties:`, JSON.stringify(event.properties, null, 6));
48 |   });
49 | 
50 |   // Flush to database
51 |   console.log('\n6. Flushing to database...');
52 |   await telemetry.flush();
53 | 
54 |   console.log('\n7. Done! Check Supabase for error events with "error" field.');
55 |   console.log('   Query: SELECT * FROM telemetry_events WHERE event = \'error_occurred\' ORDER BY created_at DESC LIMIT 5;');
56 | }
57 | 
58 | testErrorTracking().catch(console.error);
59 | 
```

--------------------------------------------------------------------------------
/scripts/sync-runtime-version.js:
--------------------------------------------------------------------------------

```javascript
 1 | #!/usr/bin/env node
 2 | 
 3 | /**
 4 |  * Sync version from package.json to package.runtime.json and README.md
 5 |  * This ensures all files always have the same version
 6 |  */
 7 | 
 8 | const fs = require('fs');
 9 | const path = require('path');
10 | 
11 | const packageJsonPath = path.join(__dirname, '..', 'package.json');
12 | const packageRuntimePath = path.join(__dirname, '..', 'package.runtime.json');
13 | const readmePath = path.join(__dirname, '..', 'README.md');
14 | 
15 | try {
16 |   // Read package.json
17 |   const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
18 |   const version = packageJson.version;
19 |   
20 |   // Read package.runtime.json
21 |   const packageRuntime = JSON.parse(fs.readFileSync(packageRuntimePath, 'utf-8'));
22 |   
23 |   // Update version if different
24 |   if (packageRuntime.version !== version) {
25 |     packageRuntime.version = version;
26 |     
27 |     // Write back with proper formatting
28 |     fs.writeFileSync(
29 |       packageRuntimePath, 
30 |       JSON.stringify(packageRuntime, null, 2) + '\n',
31 |       'utf-8'
32 |     );
33 |     
34 |     console.log(`✅ Updated package.runtime.json version to ${version}`);
35 |   } else {
36 |     console.log(`✓ package.runtime.json already at version ${version}`);
37 |   }
38 |   
39 |   // Update README.md version badge
40 |   let readmeContent = fs.readFileSync(readmePath, 'utf-8');
41 |   const versionBadgeRegex = /(\[!\[Version\]\(https:\/\/img\.shields\.io\/badge\/version-)[^-]+(-.+?\)\])/;
42 |   const newVersionBadge = `$1${version}$2`;
43 |   const updatedReadmeContent = readmeContent.replace(versionBadgeRegex, newVersionBadge);
44 |   
45 |   if (updatedReadmeContent !== readmeContent) {
46 |     fs.writeFileSync(readmePath, updatedReadmeContent);
47 |     console.log(`✅ Updated README.md version badge to ${version}`);
48 |   } else {
49 |     console.log(`✓ README.md already has version badge ${version}`);
50 |   }
51 | } catch (error) {
52 |   console.error('❌ Error syncing version:', error.message);
53 |   process.exit(1);
54 | }
```

--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------

```yaml
 1 | # docker-compose.yml
 2 | # For optimized builds with BuildKit, use: docker-compose -f docker-compose.buildkit.yml up
 3 | version: '3.8'
 4 | 
 5 | services:
 6 |   n8n-mcp:
 7 |     image: ghcr.io/czlonkowski/n8n-mcp:latest
 8 |     container_name: n8n-mcp
 9 |     restart: unless-stopped
10 |     
11 |     # Environment configuration
12 |     environment:
13 |       # Mode configuration
14 |       MCP_MODE: ${MCP_MODE:-http}
15 |       USE_FIXED_HTTP: ${USE_FIXED_HTTP:-true}  # Use fixed implementation for stability
16 |       AUTH_TOKEN: ${AUTH_TOKEN:?AUTH_TOKEN is required for HTTP mode}
17 |       
18 |       # Application settings
19 |       NODE_ENV: ${NODE_ENV:-production}
20 |       LOG_LEVEL: ${LOG_LEVEL:-info}
21 |       PORT: ${PORT:-3000}
22 |       
23 |       # Database
24 |       NODE_DB_PATH: ${NODE_DB_PATH:-/app/data/nodes.db}
25 |       REBUILD_ON_START: ${REBUILD_ON_START:-false}
26 | 
27 |       # Telemetry: Anonymous usage statistics are ENABLED by default
28 |       # To opt-out, uncomment and set to 'true':
29 |       # N8N_MCP_TELEMETRY_DISABLED: ${N8N_MCP_TELEMETRY_DISABLED:-true}
30 | 
31 |       # Optional: n8n API configuration (enables 16 additional management tools)
32 |       # Uncomment and configure to enable n8n workflow management
33 |       # N8N_API_URL: ${N8N_API_URL}
34 |       # N8N_API_KEY: ${N8N_API_KEY}
35 |       # N8N_API_TIMEOUT: ${N8N_API_TIMEOUT:-30000}
36 |       # N8N_API_MAX_RETRIES: ${N8N_API_MAX_RETRIES:-3}
37 |     
38 |     # Volumes for persistence
39 |     volumes:
40 |       - n8n-mcp-data:/app/data
41 |     
42 |     # Port mapping
43 |     ports:
44 |       - "${PORT:-3000}:3000"
45 |     
46 |     # Resource limits
47 |     deploy:
48 |       resources:
49 |         limits:
50 |           memory: 512M
51 |         reservations:
52 |           memory: 256M
53 |     
54 |     # Health check
55 |     healthcheck:
56 |       test: ["CMD", "curl", "-f", "http://127.0.0.1:3000/health"]
57 |       interval: 30s
58 |       timeout: 10s
59 |       retries: 3
60 |       start_period: 40s
61 | 
62 | # Named volume for data persistence
63 | volumes:
64 |   n8n-mcp-data:
65 |     driver: local
```

--------------------------------------------------------------------------------
/src/config/n8n-api.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { z } from 'zod';
 2 | import dotenv from 'dotenv';
 3 | import { logger } from '../utils/logger';
 4 | 
 5 | // n8n API configuration schema
 6 | const n8nApiConfigSchema = z.object({
 7 |   N8N_API_URL: z.string().url().optional(),
 8 |   N8N_API_KEY: z.string().min(1).optional(),
 9 |   N8N_API_TIMEOUT: z.coerce.number().positive().default(30000),
10 |   N8N_API_MAX_RETRIES: z.coerce.number().positive().default(3),
11 | });
12 | 
13 | // Track if we've loaded env vars
14 | let envLoaded = false;
15 | 
16 | // Parse and validate n8n API configuration
17 | export function getN8nApiConfig() {
18 |   // Load environment variables on first access
19 |   if (!envLoaded) {
20 |     dotenv.config();
21 |     envLoaded = true;
22 |   }
23 |   
24 |   const result = n8nApiConfigSchema.safeParse(process.env);
25 |   
26 |   if (!result.success) {
27 |     return null;
28 |   }
29 |   
30 |   const config = result.data;
31 |   
32 |   // Check if both URL and API key are provided
33 |   if (!config.N8N_API_URL || !config.N8N_API_KEY) {
34 |     return null;
35 |   }
36 |   
37 |   return {
38 |     baseUrl: config.N8N_API_URL,
39 |     apiKey: config.N8N_API_KEY,
40 |     timeout: config.N8N_API_TIMEOUT,
41 |     maxRetries: config.N8N_API_MAX_RETRIES,
42 |   };
43 | }
44 | 
45 | // Helper to check if n8n API is configured (lazy check)
46 | export function isN8nApiConfigured(): boolean {
47 |   const config = getN8nApiConfig();
48 |   return config !== null;
49 | }
50 | 
51 | /**
52 |  * Create n8n API configuration from instance context
53 |  * Used for flexible instance configuration support
54 |  */
55 | export function getN8nApiConfigFromContext(context: {
56 |   n8nApiUrl?: string;
57 |   n8nApiKey?: string;
58 |   n8nApiTimeout?: number;
59 |   n8nApiMaxRetries?: number;
60 | }): N8nApiConfig | null {
61 |   if (!context.n8nApiUrl || !context.n8nApiKey) {
62 |     return null;
63 |   }
64 | 
65 |   return {
66 |     baseUrl: context.n8nApiUrl,
67 |     apiKey: context.n8nApiKey,
68 |     timeout: context.n8nApiTimeout ?? 30000,
69 |     maxRetries: context.n8nApiMaxRetries ?? 3,
70 |   };
71 | }
72 | 
73 | // Type export
74 | export type N8nApiConfig = NonNullable<ReturnType<typeof getN8nApiConfig>>;
```

--------------------------------------------------------------------------------
/tests/integration/n8n-api/utils/node-repository.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Node Repository Utility for Integration Tests
 3 |  *
 4 |  * Provides a singleton NodeRepository instance for integration tests
 5 |  * that require validation or autofix functionality.
 6 |  */
 7 | 
 8 | import path from 'path';
 9 | import { createDatabaseAdapter, DatabaseAdapter } from '../../../../src/database/database-adapter';
10 | import { NodeRepository } from '../../../../src/database/node-repository';
11 | 
12 | let repositoryInstance: NodeRepository | null = null;
13 | let dbInstance: DatabaseAdapter | null = null;
14 | 
15 | /**
16 |  * Get or create NodeRepository instance
17 |  *
18 |  * Uses the production nodes.db database (data/nodes.db).
19 |  *
20 |  * @returns Singleton NodeRepository instance
21 |  * @throws {Error} If database file cannot be found or opened
22 |  *
23 |  * @example
24 |  * const repository = await getNodeRepository();
25 |  * const nodeInfo = await repository.getNodeByType('n8n-nodes-base.webhook');
26 |  */
27 | export async function getNodeRepository(): Promise<NodeRepository> {
28 |   if (repositoryInstance) {
29 |     return repositoryInstance;
30 |   }
31 | 
32 |   const dbPath = path.join(process.cwd(), 'data/nodes.db');
33 |   dbInstance = await createDatabaseAdapter(dbPath);
34 |   repositoryInstance = new NodeRepository(dbInstance);
35 | 
36 |   return repositoryInstance;
37 | }
38 | 
39 | /**
40 |  * Close database and reset repository instance
41 |  *
42 |  * Should be called in test cleanup (afterAll) to prevent resource leaks.
43 |  * Properly closes the database connection and resets the singleton.
44 |  *
45 |  * @example
46 |  * afterAll(async () => {
47 |  *   await closeNodeRepository();
48 |  * });
49 |  */
50 | export async function closeNodeRepository(): Promise<void> {
51 |   if (dbInstance && typeof dbInstance.close === 'function') {
52 |     await dbInstance.close();
53 |   }
54 |   dbInstance = null;
55 |   repositoryInstance = null;
56 | }
57 | 
58 | /**
59 |  * Reset repository instance (useful for test cleanup)
60 |  *
61 |  * @deprecated Use closeNodeRepository() instead to properly close database connections
62 |  */
63 | export function resetNodeRepository(): void {
64 |   repositoryInstance = null;
65 | }
66 | 
```

--------------------------------------------------------------------------------
/tests/factories/property-definition-factory.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { Factory } from 'fishery';
 2 | import { faker } from '@faker-js/faker';
 3 | 
 4 | /**
 5 |  * Interface for n8n node property definitions.
 6 |  * Represents the structure of properties that configure node behavior.
 7 |  */
 8 | interface PropertyDefinition {
 9 |   name: string;
10 |   displayName: string;
11 |   type: string;
12 |   default?: any;
13 |   required?: boolean;
14 |   description?: string;
15 |   options?: any[];
16 | }
17 | 
18 | /**
19 |  * Factory for generating PropertyDefinition test data.
20 |  * Creates realistic property configurations for testing node validation and processing.
21 |  * 
22 |  * @example
23 |  * ```typescript
24 |  * // Create a single property
25 |  * const prop = PropertyDefinitionFactory.build();
26 |  * 
27 |  * // Create a required string property
28 |  * const urlProp = PropertyDefinitionFactory.build({
29 |  *   name: 'url',
30 |  *   displayName: 'URL',
31 |  *   type: 'string',
32 |  *   required: true
33 |  * });
34 |  * 
35 |  * // Create an options property with choices
36 |  * const methodProp = PropertyDefinitionFactory.build({
37 |  *   name: 'method',
38 |  *   type: 'options',
39 |  *   options: [
40 |  *     { name: 'GET', value: 'GET' },
41 |  *     { name: 'POST', value: 'POST' }
42 |  *   ]
43 |  * });
44 |  * 
45 |  * // Create multiple properties for a node
46 |  * const nodeProperties = PropertyDefinitionFactory.buildList(5);
47 |  * ```
48 |  */
49 | export const PropertyDefinitionFactory = Factory.define<PropertyDefinition>(() => ({
50 |   name: faker.word.noun() + faker.word.adjective().charAt(0).toUpperCase() + faker.word.adjective().slice(1),
51 |   displayName: faker.helpers.arrayElement(['URL', 'Method', 'Headers', 'Body', 'Authentication']),
52 |   type: faker.helpers.arrayElement(['string', 'number', 'boolean', 'options', 'json']),
53 |   default: faker.datatype.boolean() ? faker.word.sample() : undefined,
54 |   required: faker.datatype.boolean(),
55 |   description: faker.lorem.sentence(),
56 |   options: faker.datatype.boolean() ? [
57 |     {
58 |       name: faker.word.noun(),
59 |       value: faker.word.noun(),
60 |       description: faker.lorem.sentence()
61 |     }
62 |   ] : undefined
63 | }));
```

--------------------------------------------------------------------------------
/docker-compose.n8n.yml:
--------------------------------------------------------------------------------

```yaml
 1 | version: '3.8'
 2 | 
 3 | services:
 4 |   # n8n workflow automation
 5 |   n8n:
 6 |     image: n8nio/n8n:latest
 7 |     container_name: n8n
 8 |     restart: unless-stopped
 9 |     ports:
10 |       - "${N8N_PORT:-5678}:5678"
11 |     environment:
12 |       - N8N_BASIC_AUTH_ACTIVE=${N8N_BASIC_AUTH_ACTIVE:-true}
13 |       - N8N_BASIC_AUTH_USER=${N8N_BASIC_AUTH_USER:-admin}
14 |       - N8N_BASIC_AUTH_PASSWORD=${N8N_BASIC_AUTH_PASSWORD:-password}
15 |       - N8N_HOST=${N8N_HOST:-localhost}
16 |       - N8N_PORT=5678
17 |       - N8N_PROTOCOL=${N8N_PROTOCOL:-http}
18 |       - WEBHOOK_URL=${N8N_WEBHOOK_URL:-http://localhost:5678/}
19 |       - N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
20 |     volumes:
21 |       - n8n_data:/home/node/.n8n
22 |     networks:
23 |       - n8n-network
24 |     healthcheck:
25 |       test: ["CMD", "sh", "-c", "wget --quiet --spider --tries=1 --timeout=10 http://localhost:5678/healthz || exit 1"]
26 |       interval: 30s
27 |       timeout: 10s
28 |       retries: 3
29 |       start_period: 30s
30 | 
31 |   # n8n-mcp server for AI assistance
32 |   n8n-mcp:
33 |     build:
34 |       context: .
35 |       dockerfile: Dockerfile  # Uses standard Dockerfile with N8N_MODE=true env var
36 |     image: ghcr.io/${GITHUB_REPOSITORY:-czlonkowski/n8n-mcp}/n8n-mcp:${VERSION:-latest}
37 |     container_name: n8n-mcp
38 |     restart: unless-stopped
39 |     ports:
40 |       - "${MCP_PORT:-3000}:3000"
41 |     environment:
42 |       - NODE_ENV=production
43 |       - N8N_MODE=true
44 |       - MCP_MODE=http
45 |       - N8N_API_URL=http://n8n:5678
46 |       - N8N_API_KEY=${N8N_API_KEY}
47 |       - MCP_AUTH_TOKEN=${MCP_AUTH_TOKEN}
48 |       - AUTH_TOKEN=${MCP_AUTH_TOKEN}
49 |       - LOG_LEVEL=${LOG_LEVEL:-info}
50 |     volumes:
51 |       - ./data:/app/data:ro
52 |       - mcp_logs:/app/logs
53 |     networks:
54 |       - n8n-network
55 |     depends_on:
56 |       n8n:
57 |         condition: service_healthy
58 |     healthcheck:
59 |       test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
60 |       interval: 30s
61 |       timeout: 10s
62 |       retries: 3
63 |       start_period: 40s
64 | 
65 | volumes:
66 |   n8n_data:
67 |     driver: local
68 |   mcp_logs:
69 |     driver: local
70 | 
71 | networks:
72 |   n8n-network:
73 |     driver: bridge
```

--------------------------------------------------------------------------------
/tests/extracted-nodes-db/extraction-report.json:
--------------------------------------------------------------------------------

```json
 1 | [
 2 |   {
 3 |     "nodeType": "n8n-nodes-base.Slack",
 4 |     "success": true,
 5 |     "hasPackageInfo": true,
 6 |     "hasCredentials": true,
 7 |     "sourceSize": 1007,
 8 |     "credentialSize": 7553,
 9 |     "packageName": "n8n-nodes-base",
10 |     "packageVersion": "1.14.1"
11 |   },
12 |   {
13 |     "nodeType": "n8n-nodes-base.Discord",
14 |     "success": true,
15 |     "hasPackageInfo": true,
16 |     "hasCredentials": false,
17 |     "sourceSize": 10049,
18 |     "credentialSize": 0,
19 |     "packageName": "n8n-nodes-base",
20 |     "packageVersion": "1.14.1"
21 |   },
22 |   {
23 |     "nodeType": "n8n-nodes-base.HttpRequest",
24 |     "success": true,
25 |     "hasPackageInfo": true,
26 |     "hasCredentials": false,
27 |     "sourceSize": 1343,
28 |     "credentialSize": 0,
29 |     "packageName": "n8n-nodes-base",
30 |     "packageVersion": "1.14.1"
31 |   },
32 |   {
33 |     "nodeType": "n8n-nodes-base.Webhook",
34 |     "success": true,
35 |     "hasPackageInfo": true,
36 |     "hasCredentials": false,
37 |     "sourceSize": 10667,
38 |     "credentialSize": 0,
39 |     "packageName": "n8n-nodes-base",
40 |     "packageVersion": "1.14.1"
41 |   },
42 |   {
43 |     "nodeType": "n8n-nodes-base.If",
44 |     "success": true,
45 |     "hasPackageInfo": true,
46 |     "hasCredentials": false,
47 |     "sourceSize": 20533,
48 |     "credentialSize": 0,
49 |     "packageName": "n8n-nodes-base",
50 |     "packageVersion": "1.14.1"
51 |   },
52 |   {
53 |     "nodeType": "n8n-nodes-base.SplitInBatches",
54 |     "success": true,
55 |     "hasPackageInfo": true,
56 |     "hasCredentials": false,
57 |     "sourceSize": 1135,
58 |     "credentialSize": 0,
59 |     "packageName": "n8n-nodes-base",
60 |     "packageVersion": "1.14.1"
61 |   },
62 |   {
63 |     "nodeType": "n8n-nodes-base.Airtable",
64 |     "success": true,
65 |     "hasPackageInfo": true,
66 |     "hasCredentials": true,
67 |     "sourceSize": 936,
68 |     "credentialSize": 5985,
69 |     "packageName": "n8n-nodes-base",
70 |     "packageVersion": "1.14.1"
71 |   },
72 |   {
73 |     "nodeType": "n8n-nodes-base.Function",
74 |     "success": true,
75 |     "hasPackageInfo": true,
76 |     "hasCredentials": false,
77 |     "sourceSize": 7449,
78 |     "credentialSize": 0,
79 |     "packageName": "n8n-nodes-base",
80 |     "packageVersion": "1.14.1"
81 |   }
82 | ]
```

--------------------------------------------------------------------------------
/tests/setup/global-setup.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { beforeEach, afterEach, vi } from 'vitest';
 2 | import { loadTestEnvironment, getTestConfig, getTestTimeout } from './test-env';
 3 | 
 4 | // CI Debug: Log environment loading in CI only
 5 | if (process.env.CI === 'true') {
 6 |   console.log('[CI-DEBUG] Global setup starting, NODE_ENV:', process.env.NODE_ENV);
 7 | }
 8 | 
 9 | // Load test environment configuration
10 | loadTestEnvironment();
11 | 
12 | if (process.env.CI === 'true') {
13 |   console.log('[CI-DEBUG] Global setup complete, N8N_API_URL:', process.env.N8N_API_URL);
14 | }
15 | 
16 | // Get test configuration
17 | const testConfig = getTestConfig();
18 | 
19 | // Reset mocks between tests
20 | beforeEach(() => {
21 |   vi.clearAllMocks();
22 | });
23 | 
24 | // Clean up after each test
25 | afterEach(() => {
26 |   vi.restoreAllMocks();
27 |   
28 |   // Perform cleanup if enabled
29 |   if (testConfig.cleanup.enabled) {
30 |     // Add cleanup logic here if needed
31 |   }
32 | });
33 | 
34 | // Global test timeout from configuration
35 | vi.setConfig({ testTimeout: getTestTimeout('global') });
36 | 
37 | // Configure console output based on test configuration
38 | if (!testConfig.logging.debug) {
39 |   global.console = {
40 |     ...console,
41 |     log: vi.fn(),
42 |     debug: vi.fn(),
43 |     info: vi.fn(),
44 |     warn: testConfig.logging.level === 'error' ? vi.fn() : console.warn,
45 |     error: console.error, // Always show errors
46 |   };
47 | }
48 | 
49 | // Set up performance monitoring if enabled
50 | if (testConfig.performance) {
51 |   // Use a high-resolution timer that maintains timing precision
52 |   let startTime = process.hrtime.bigint();
53 |   
54 |   global.performance = global.performance || {
55 |     now: () => {
56 |       // Convert nanoseconds to milliseconds with high precision
57 |       const currentTime = process.hrtime.bigint();
58 |       return Number(currentTime - startTime) / 1000000; // Convert nanoseconds to milliseconds
59 |     },
60 |     mark: vi.fn(),
61 |     measure: vi.fn(),
62 |     getEntriesByName: vi.fn(() => []),
63 |     getEntriesByType: vi.fn(() => []),
64 |     clearMarks: vi.fn(),
65 |     clearMeasures: vi.fn(),
66 |   } as any;
67 | }
68 | 
69 | // Export test configuration for use in tests
70 | export { testConfig, getTestTimeout, getTestConfig };
```

--------------------------------------------------------------------------------
/src/telemetry/error-sanitizer.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Error Sanitizer for Startup Errors (v2.18.3)
 3 |  * Extracts and sanitizes error messages with security-focused patterns
 4 |  * Now uses shared sanitization utilities to avoid code duplication
 5 |  */
 6 | 
 7 | import { logger } from '../utils/logger';
 8 | import { sanitizeErrorMessageCore } from './error-sanitization-utils';
 9 | 
10 | /**
11 |  * Extract error message from unknown error type
12 |  * Safely handles Error objects, strings, and other types
13 |  */
14 | export function extractErrorMessage(error: unknown): string {
15 |   try {
16 |     if (error instanceof Error) {
17 |       // Include stack trace if available (will be truncated later)
18 |       return error.stack || error.message || 'Unknown error';
19 |     }
20 | 
21 |     if (typeof error === 'string') {
22 |       return error;
23 |     }
24 | 
25 |     if (error && typeof error === 'object') {
26 |       // Try to extract message from object
27 |       const errorObj = error as any;
28 |       if (errorObj.message) {
29 |         return String(errorObj.message);
30 |       }
31 |       if (errorObj.error) {
32 |         return String(errorObj.error);
33 |       }
34 |       // Fall back to JSON stringify with truncation
35 |       try {
36 |         return JSON.stringify(error).substring(0, 500);
37 |       } catch {
38 |         return 'Error object (unstringifiable)';
39 |       }
40 |     }
41 | 
42 |     return String(error);
43 |   } catch (extractError) {
44 |     logger.debug('Error during message extraction:', extractError);
45 |     return 'Error message extraction failed';
46 |   }
47 | }
48 | 
49 | /**
50 |  * Sanitize startup error message to remove sensitive data
51 |  * Now uses shared sanitization core from error-sanitization-utils.ts (v2.18.3)
52 |  * This eliminates code duplication and the ReDoS vulnerability
53 |  */
54 | export function sanitizeStartupError(errorMessage: string): string {
55 |   return sanitizeErrorMessageCore(errorMessage);
56 | }
57 | 
58 | /**
59 |  * Combined operation: Extract and sanitize error message
60 |  * This is the main entry point for startup error processing
61 |  */
62 | export function processStartupError(error: unknown): string {
63 |   const message = extractErrorMessage(error);
64 |   return sanitizeStartupError(message);
65 | }
66 | 
```

--------------------------------------------------------------------------------
/src/database/migrations/add-template-node-configs.sql:
--------------------------------------------------------------------------------

```sql
 1 | -- Migration: Add template_node_configs table
 2 | -- Run during `npm run rebuild` or `npm run fetch:templates`
 3 | -- This migration is idempotent - safe to run multiple times
 4 | 
 5 | -- Create table if it doesn't exist
 6 | CREATE TABLE IF NOT EXISTS template_node_configs (
 7 |   id INTEGER PRIMARY KEY,
 8 |   node_type TEXT NOT NULL,
 9 |   template_id INTEGER NOT NULL,
10 |   template_name TEXT NOT NULL,
11 |   template_views INTEGER DEFAULT 0,
12 | 
13 |   -- Node configuration (extracted from workflow)
14 |   node_name TEXT,                  -- Node name in workflow (e.g., "HTTP Request")
15 |   parameters_json TEXT NOT NULL,   -- JSON: node.parameters
16 |   credentials_json TEXT,            -- JSON: node.credentials (if present)
17 | 
18 |   -- Pre-calculated metadata for filtering
19 |   has_credentials INTEGER DEFAULT 0,
20 |   has_expressions INTEGER DEFAULT 0,  -- Contains {{...}} or $json/$node
21 |   complexity TEXT CHECK(complexity IN ('simple', 'medium', 'complex')),
22 |   use_cases TEXT,                   -- JSON array from template.metadata.use_cases
23 | 
24 |   -- Pre-calculated ranking (1 = best, 2 = second best, etc.)
25 |   rank INTEGER DEFAULT 0,
26 | 
27 |   created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
28 |   FOREIGN KEY (template_id) REFERENCES templates(id) ON DELETE CASCADE
29 | );
30 | 
31 | -- Create indexes if they don't exist
32 | CREATE INDEX IF NOT EXISTS idx_config_node_type_rank
33 |   ON template_node_configs(node_type, rank);
34 | 
35 | CREATE INDEX IF NOT EXISTS idx_config_complexity
36 |   ON template_node_configs(node_type, complexity, rank);
37 | 
38 | CREATE INDEX IF NOT EXISTS idx_config_auth
39 |   ON template_node_configs(node_type, has_credentials, rank);
40 | 
41 | -- Create view if it doesn't exist
42 | CREATE VIEW IF NOT EXISTS ranked_node_configs AS
43 | SELECT
44 |   node_type,
45 |   template_name,
46 |   template_views,
47 |   parameters_json,
48 |   credentials_json,
49 |   has_credentials,
50 |   has_expressions,
51 |   complexity,
52 |   use_cases,
53 |   rank
54 | FROM template_node_configs
55 | WHERE rank <= 5  -- Top 5 per node type
56 | ORDER BY node_type, rank;
57 | 
58 | -- Note: Actual data population is handled by the fetch-templates script
59 | -- This migration only creates the schema
60 | 
```

--------------------------------------------------------------------------------
/src/mcp/tool-docs/validation/validate-node-minimal.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { ToolDocumentation } from '../types';
 2 | 
 3 | export const validateNodeMinimalDoc: ToolDocumentation = {
 4 |   name: 'validate_node_minimal',
 5 |   category: 'validation',
 6 |   essentials: {
 7 |     description: 'Fast check for missing required fields only. No warnings/suggestions. Returns: list of missing fields.',
 8 |     keyParameters: ['nodeType', 'config'],
 9 |     example: 'validate_node_minimal("nodes-base.slack", {resource: "message"})',
10 |     performance: 'Instant',
11 |     tips: [
12 |       'Returns only missing required fields',
13 |       'No warnings or suggestions',
14 |       'Perfect for real-time validation'
15 |     ]
16 |   },
17 |   full: {
18 |     description: 'Minimal validation that only checks for missing required fields. Returns array of missing field names without any warnings or suggestions. Ideal for quick validation during node configuration.',
19 |     parameters: {
20 |       nodeType: { type: 'string', required: true, description: 'Node type with prefix (e.g., "nodes-base.slack")' },
21 |       config: { type: 'object', required: true, description: 'Node configuration to validate' }
22 |     },
23 |     returns: 'Array of missing required field names (empty if valid)',
24 |     examples: [
25 |       'validate_node_minimal("nodes-base.slack", {resource: "message", operation: "post"}) - Check Slack config',
26 |       'validate_node_minimal("nodes-base.httpRequest", {method: "GET"}) - Check HTTP config'
27 |     ],
28 |     useCases: [
29 |       'Real-time form validation',
30 |       'Quick configuration checks',
31 |       'Pre-deployment validation',
32 |       'Interactive configuration builders'
33 |     ],
34 |     performance: 'Instant - Simple field checking without complex validation',
35 |     bestPractices: [
36 |       'Use for quick feedback loops',
37 |       'Follow with validate_node_operation for thorough check',
38 |       'Check return array length for validity'
39 |     ],
40 |     pitfalls: [
41 |       'Only checks required fields',
42 |       'No type validation',
43 |       'No operation-specific validation'
44 |     ],
45 |     relatedTools: ['validate_node_operation', 'get_node_essentials', 'get_property_dependencies']
46 |   }
47 | };
```

--------------------------------------------------------------------------------
/scripts/demo-optimization.sh:
--------------------------------------------------------------------------------

```bash
 1 | #!/bin/bash
 2 | # Demonstrate the optimization concept
 3 | 
 4 | echo "🎯 Demonstrating Docker Optimization"
 5 | echo "===================================="
 6 | 
 7 | # Create a demo directory structure
 8 | DEMO_DIR="optimization-demo"
 9 | rm -rf $DEMO_DIR
10 | mkdir -p $DEMO_DIR
11 | 
12 | # Copy only runtime files
13 | echo -e "\n📦 Creating minimal runtime package..."
14 | cat > $DEMO_DIR/package.json << 'EOF'
15 | {
16 |   "name": "n8n-mcp-optimized",
17 |   "version": "1.0.0",
18 |   "private": true,
19 |   "main": "dist/mcp/index.js",
20 |   "dependencies": {
21 |     "@modelcontextprotocol/sdk": "^1.12.1",
22 |     "better-sqlite3": "^11.10.0",
23 |     "sql.js": "^1.13.0",
24 |     "express": "^5.1.0",
25 |     "dotenv": "^16.5.0"
26 |   }
27 | }
28 | EOF
29 | 
30 | # Copy built files
31 | echo "📁 Copying built application..."
32 | cp -r dist $DEMO_DIR/
33 | cp -r data $DEMO_DIR/
34 | mkdir -p $DEMO_DIR/src/database
35 | cp src/database/schema*.sql $DEMO_DIR/src/database/
36 | 
37 | # Calculate sizes
38 | echo -e "\n📊 Size comparison:"
39 | echo "Original project: $(du -sh . | cut -f1)"
40 | echo "Optimized runtime: $(du -sh $DEMO_DIR | cut -f1)"
41 | 
42 | # Show what's included
43 | echo -e "\n✅ Optimized package includes:"
44 | echo "- Pre-built SQLite database with all node info"
45 | echo "- Compiled JavaScript (dist/)"
46 | echo "- Minimal runtime dependencies"
47 | echo "- No n8n packages needed!"
48 | 
49 | # Create a simple test
50 | echo -e "\n🧪 Testing database content..."
51 | if command -v sqlite3 &> /dev/null; then
52 |     NODE_COUNT=$(sqlite3 data/nodes.db "SELECT COUNT(*) FROM nodes;" 2>/dev/null || echo "0")
53 |     AI_COUNT=$(sqlite3 data/nodes.db "SELECT COUNT(*) FROM nodes WHERE is_ai_tool = 1;" 2>/dev/null || echo "0")
54 |     echo "- Total nodes in database: $NODE_COUNT"
55 |     echo "- AI-capable nodes: $AI_COUNT"
56 | else
57 |     echo "- SQLite CLI not installed, skipping count"
58 | fi
59 | 
60 | echo -e "\n💡 This demonstrates that we can run n8n-MCP with:"
61 | echo "- ~50MB of runtime dependencies (vs 1.6GB)"
62 | echo "- Pre-built database (11MB)"
63 | echo "- No n8n packages at runtime"
64 | echo "- Total optimized size: ~200MB (vs 2.6GB)"
65 | 
66 | # Cleanup
67 | echo -e "\n🧹 Cleaning up demo..."
68 | rm -rf $DEMO_DIR
69 | 
70 | echo -e "\n✨ Optimization concept demonstrated!"
```

--------------------------------------------------------------------------------
/scripts/publish-npm-quick.sh:
--------------------------------------------------------------------------------

```bash
 1 | #!/bin/bash
 2 | # Quick publish script that skips tests
 3 | set -e
 4 | 
 5 | # Color codes
 6 | GREEN='\033[0;32m'
 7 | YELLOW='\033[1;33m'
 8 | NC='\033[0m'
 9 | 
10 | echo "🚀 Preparing n8n-mcp for npm publish (quick mode)..."
11 | 
12 | # Sync version
13 | echo "🔄 Syncing version to package.runtime.json..."
14 | npm run sync:runtime-version
15 | 
16 | VERSION=$(node -e "console.log(require('./package.json').version)")
17 | echo -e "${GREEN}📌 Version: $VERSION${NC}"
18 | 
19 | # Prepare publish directory
20 | PUBLISH_DIR="npm-publish-temp"
21 | rm -rf $PUBLISH_DIR
22 | mkdir -p $PUBLISH_DIR
23 | 
24 | echo "📦 Copying files..."
25 | cp -r dist $PUBLISH_DIR/
26 | cp -r data $PUBLISH_DIR/
27 | cp README.md LICENSE .env.example $PUBLISH_DIR/
28 | cp .npmignore $PUBLISH_DIR/ 2>/dev/null || true
29 | cp package.runtime.json $PUBLISH_DIR/package.json
30 | 
31 | cd $PUBLISH_DIR
32 | 
33 | # Configure package.json
34 | node -e "
35 | const pkg = require('./package.json');
36 | pkg.name = 'n8n-mcp';
37 | pkg.description = 'Integration between n8n workflow automation and Model Context Protocol (MCP)';
38 | pkg.bin = { 'n8n-mcp': './dist/mcp/index.js' };
39 | pkg.repository = { type: 'git', url: 'git+https://github.com/czlonkowski/n8n-mcp.git' };
40 | pkg.keywords = ['n8n', 'mcp', 'model-context-protocol', 'ai', 'workflow', 'automation'];
41 | pkg.author = 'Romuald Czlonkowski @ www.aiadvisors.pl/en';
42 | pkg.license = 'MIT';
43 | pkg.bugs = { url: 'https://github.com/czlonkowski/n8n-mcp/issues' };
44 | pkg.homepage = 'https://github.com/czlonkowski/n8n-mcp#readme';
45 | pkg.files = ['dist/**/*', 'data/nodes.db', '.env.example', 'README.md', 'LICENSE'];
46 | delete pkg.private;
47 | require('fs').writeFileSync('./package.json', JSON.stringify(pkg, null, 2));
48 | "
49 | 
50 | echo ""
51 | echo "📋 Package details:"
52 | echo -e "${GREEN}Name:${NC} $(node -e "console.log(require('./package.json').name)")"
53 | echo -e "${GREEN}Version:${NC} $(node -e "console.log(require('./package.json').version)")"
54 | echo -e "${GREEN}Size:${NC} ~50MB"
55 | echo ""
56 | echo "✅ Ready to publish!"
57 | echo ""
58 | echo -e "${YELLOW}⚠️  Note: Tests were skipped in quick mode${NC}"
59 | echo ""
60 | echo "To publish, run:"
61 | echo -e "  ${GREEN}cd $PUBLISH_DIR${NC}"
62 | echo -e "  ${GREEN}npm publish --otp=YOUR_OTP_CODE${NC}"
```

--------------------------------------------------------------------------------
/src/utils/console-manager.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Console Manager for MCP HTTP Server
 3 |  * 
 4 |  * Prevents console output from interfering with StreamableHTTPServerTransport
 5 |  * by silencing console methods during MCP request handling.
 6 |  */
 7 | export class ConsoleManager {
 8 |   private originalConsole = {
 9 |     log: console.log,
10 |     error: console.error,
11 |     warn: console.warn,
12 |     info: console.info,
13 |     debug: console.debug,
14 |     trace: console.trace
15 |   };
16 |   
17 |   private isSilenced = false;
18 |   
19 |   /**
20 |    * Silence all console output
21 |    */
22 |   public silence(): void {
23 |     if (this.isSilenced || process.env.MCP_MODE !== 'http') {
24 |       return;
25 |     }
26 |     
27 |     this.isSilenced = true;
28 |     process.env.MCP_REQUEST_ACTIVE = 'true';
29 |     console.log = () => {};
30 |     console.error = () => {};
31 |     console.warn = () => {};
32 |     console.info = () => {};
33 |     console.debug = () => {};
34 |     console.trace = () => {};
35 |   }
36 |   
37 |   /**
38 |    * Restore original console methods
39 |    */
40 |   public restore(): void {
41 |     if (!this.isSilenced) {
42 |       return;
43 |     }
44 |     
45 |     this.isSilenced = false;
46 |     process.env.MCP_REQUEST_ACTIVE = 'false';
47 |     console.log = this.originalConsole.log;
48 |     console.error = this.originalConsole.error;
49 |     console.warn = this.originalConsole.warn;
50 |     console.info = this.originalConsole.info;
51 |     console.debug = this.originalConsole.debug;
52 |     console.trace = this.originalConsole.trace;
53 |   }
54 |   
55 |   /**
56 |    * Wrap an operation with console silencing
57 |    * Automatically restores console on completion or error
58 |    */
59 |   public async wrapOperation<T>(operation: () => T | Promise<T>): Promise<T> {
60 |     this.silence();
61 |     try {
62 |       const result = operation();
63 |       if (result instanceof Promise) {
64 |         return await result.finally(() => this.restore());
65 |       }
66 |       this.restore();
67 |       return result;
68 |     } catch (error) {
69 |       this.restore();
70 |       throw error;
71 |     }
72 |   }
73 |   
74 |   /**
75 |    * Check if console is currently silenced
76 |    */
77 |   public get isActive(): boolean {
78 |     return this.isSilenced;
79 |   }
80 | }
81 | 
82 | // Export singleton instance for easy use
83 | export const consoleManager = new ConsoleManager();
```

--------------------------------------------------------------------------------
/tests/integration/n8n-api/types/mcp-responses.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * TypeScript interfaces for MCP handler responses
 3 |  *
 4 |  * These interfaces provide type safety for integration tests,
 5 |  * replacing unsafe `as any` casts with proper type definitions.
 6 |  */
 7 | 
 8 | /**
 9 |  * Workflow validation response from handleValidateWorkflow
10 |  */
11 | export interface ValidationResponse {
12 |   valid: boolean;
13 |   workflowId: string;
14 |   workflowName: string;
15 |   summary: {
16 |     totalNodes: number;
17 |     enabledNodes: number;
18 |     triggerNodes: number;
19 |     validConnections?: number;
20 |     invalidConnections?: number;
21 |     expressionsValidated?: number;
22 |     errorCount: number;
23 |     warningCount: number;
24 |   };
25 |   errors?: Array<{
26 |     node: string;
27 |     nodeName?: string;
28 |     message: string;
29 |     details?: {
30 |       code?: string;
31 |       [key: string]: unknown;
32 |     };
33 |     code?: string;
34 |   }>;
35 |   warnings?: Array<{
36 |     node: string;
37 |     nodeName?: string;
38 |     message: string;
39 |     details?: {
40 |       code?: string;
41 |       [key: string]: unknown;
42 |     };
43 |     code?: string;
44 |   }>;
45 |   info?: Array<{
46 |     node: string;
47 |     nodeName?: string;
48 |     message: string;
49 |     severity?: string;
50 |     details?: unknown;
51 |   }>;
52 |   suggestions?: string[];
53 | }
54 | 
55 | /**
56 |  * Workflow autofix response from handleAutofixWorkflow
57 |  */
58 | export interface AutofixResponse {
59 |   workflowId: string;
60 |   workflowName: string;
61 |   preview?: boolean;
62 |   fixesAvailable?: number;
63 |   fixesApplied?: number;
64 |   fixes?: Array<{
65 |     type: 'expression-format' | 'typeversion-correction' | 'error-output-config' | 'node-type-correction' | 'webhook-missing-path';
66 |     confidence: 'high' | 'medium' | 'low';
67 |     description: string;
68 |     nodeName?: string;
69 |     nodeId?: string;
70 |     before?: unknown;
71 |     after?: unknown;
72 |   }>;
73 |   summary?: {
74 |     totalFixes: number;
75 |     byType: Record<string, number>;
76 |     byConfidence: Record<string, number>;
77 |   };
78 |   stats?: {
79 |     expressionFormat?: number;
80 |     typeVersionCorrection?: number;
81 |     errorOutputConfig?: number;
82 |     nodeTypeCorrection?: number;
83 |     webhookMissingPath?: number;
84 |   };
85 |   message?: string;
86 |   validationSummary?: {
87 |     errors: number;
88 |     warnings: number;
89 |   };
90 | }
91 | 
```

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

```typescript
 1 | import { ToolDocumentation } from '../types';
 2 | 
 3 | export const n8nDeleteWorkflowDoc: ToolDocumentation = {
 4 |   name: 'n8n_delete_workflow',
 5 |   category: 'workflow_management',
 6 |   essentials: {
 7 |     description: 'Permanently delete a workflow. This action cannot be undone.',
 8 |     keyParameters: ['id'],
 9 |     example: 'n8n_delete_workflow({id: "workflow_123"})',
10 |     performance: 'Fast (50-150ms)',
11 |     tips: [
12 |       'Action is irreversible',
13 |       'Deletes all execution history',
14 |       'Check workflow first with get_minimal'
15 |     ]
16 |   },
17 |   full: {
18 |     description: 'Permanently deletes a workflow from n8n including all associated data, execution history, and settings. This is an irreversible operation that should be used with caution. The workflow must exist and the user must have appropriate permissions.',
19 |     parameters: {
20 |       id: { type: 'string', required: true, description: 'Workflow ID to delete permanently' }
21 |     },
22 |     returns: 'Success confirmation or error if workflow not found/cannot be deleted',
23 |     examples: [
24 |       'n8n_delete_workflow({id: "abc123"}) - Delete specific workflow',
25 |       'if (confirm) { n8n_delete_workflow({id: wf.id}); } // With confirmation'
26 |     ],
27 |     useCases: [
28 |       'Remove obsolete workflows',
29 |       'Clean up test workflows',
30 |       'Delete failed experiments',
31 |       'Manage workflow limits',
32 |       'Remove duplicates'
33 |     ],
34 |     performance: 'Fast operation - typically 50-150ms. May take longer if workflow has extensive execution history.',
35 |     bestPractices: [
36 |       'Always confirm before deletion',
37 |       'Check workflow with get_minimal first',
38 |       'Consider deactivating instead of deleting',
39 |       'Export workflow before deletion for backup'
40 |     ],
41 |     pitfalls: [
42 |       'Requires N8N_API_URL and N8N_API_KEY configured',
43 |       'Cannot be undone - permanent deletion',
44 |       'Deletes all execution history',
45 |       'Active workflows can be deleted',
46 |       'No built-in confirmation'
47 |     ],
48 |     relatedTools: ['n8n_get_workflow_minimal', 'n8n_list_workflows', 'n8n_update_partial_workflow', 'n8n_delete_execution']
49 |   }
50 | };
```

--------------------------------------------------------------------------------
/src/database/schema-optimized.sql:
--------------------------------------------------------------------------------

```sql
 1 | -- Optimized schema with source code storage for Docker optimization
 2 | CREATE TABLE IF NOT EXISTS nodes (
 3 |   node_type TEXT PRIMARY KEY,
 4 |   package_name TEXT NOT NULL,
 5 |   display_name TEXT NOT NULL,
 6 |   description TEXT,
 7 |   category TEXT,
 8 |   development_style TEXT CHECK(development_style IN ('declarative', 'programmatic')),
 9 |   is_ai_tool INTEGER DEFAULT 0,
10 |   is_trigger INTEGER DEFAULT 0,
11 |   is_webhook INTEGER DEFAULT 0,
12 |   is_versioned INTEGER DEFAULT 0,
13 |   version TEXT,
14 |   documentation TEXT,
15 |   properties_schema TEXT,
16 |   operations TEXT,
17 |   credentials_required TEXT,
18 |   -- New columns for source code storage
19 |   node_source_code TEXT,
20 |   credential_source_code TEXT,
21 |   source_location TEXT,
22 |   source_extracted_at DATETIME,
23 |   -- Metadata
24 |   updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
25 | );
26 | 
27 | -- Indexes for performance
28 | CREATE INDEX IF NOT EXISTS idx_package ON nodes(package_name);
29 | CREATE INDEX IF NOT EXISTS idx_ai_tool ON nodes(is_ai_tool);
30 | CREATE INDEX IF NOT EXISTS idx_category ON nodes(category);
31 | 
32 | -- FTS5 table for full-text search including source code
33 | CREATE VIRTUAL TABLE IF NOT EXISTS nodes_fts USING fts5(
34 |   node_type,
35 |   display_name,
36 |   description,
37 |   documentation,
38 |   operations,
39 |   node_source_code,
40 |   content=nodes,
41 |   content_rowid=rowid
42 | );
43 | 
44 | -- Trigger to keep FTS in sync
45 | CREATE TRIGGER IF NOT EXISTS nodes_fts_insert AFTER INSERT ON nodes
46 | BEGIN
47 |   INSERT INTO nodes_fts(rowid, node_type, display_name, description, documentation, operations, node_source_code)
48 |   VALUES (new.rowid, new.node_type, new.display_name, new.description, new.documentation, new.operations, new.node_source_code);
49 | END;
50 | 
51 | CREATE TRIGGER IF NOT EXISTS nodes_fts_update AFTER UPDATE ON nodes
52 | BEGIN
53 |   UPDATE nodes_fts 
54 |   SET node_type = new.node_type,
55 |       display_name = new.display_name,
56 |       description = new.description,
57 |       documentation = new.documentation,
58 |       operations = new.operations,
59 |       node_source_code = new.node_source_code
60 |   WHERE rowid = new.rowid;
61 | END;
62 | 
63 | CREATE TRIGGER IF NOT EXISTS nodes_fts_delete AFTER DELETE ON nodes
64 | BEGIN
65 |   DELETE FROM nodes_fts WHERE rowid = old.rowid;
66 | END;
```

--------------------------------------------------------------------------------
/tests/test-small-rebuild.js:
--------------------------------------------------------------------------------

```javascript
 1 | #!/usr/bin/env node
 2 | 
 3 | const { NodeDocumentationService } = require('../dist/services/node-documentation-service');
 4 | 
 5 | async function testSmallRebuild() {
 6 |   console.log('Testing small rebuild...\n');
 7 |   
 8 |   const service = new NodeDocumentationService('./data/nodes-v2-test.db');
 9 |   
10 |   try {
11 |     // First, let's just try the IF node specifically
12 |     const extractor = service.extractor;
13 |     console.log('1️⃣ Testing extraction of IF node...');
14 |     
15 |     try {
16 |       const ifNodeData = await extractor.extractNodeSource('n8n-nodes-base.If');
17 |       console.log('   ✅ Successfully extracted IF node');
18 |       console.log('   Source code length:', ifNodeData.sourceCode.length);
19 |       console.log('   Has credentials:', !!ifNodeData.credentialCode);
20 |     } catch (error) {
21 |       console.log('   ❌ Failed to extract IF node:', error.message);
22 |     }
23 |     
24 |     // Try the Webhook node
25 |     console.log('\n2️⃣ Testing extraction of Webhook node...');
26 |     try {
27 |       const webhookNodeData = await extractor.extractNodeSource('n8n-nodes-base.Webhook');
28 |       console.log('   ✅ Successfully extracted Webhook node');
29 |       console.log('   Source code length:', webhookNodeData.sourceCode.length);
30 |     } catch (error) {
31 |       console.log('   ❌ Failed to extract Webhook node:', error.message);
32 |     }
33 |     
34 |     // Now try storing just these nodes
35 |     console.log('\n3️⃣ Testing storage of a single node...');
36 |     const nodeInfo = {
37 |       nodeType: 'n8n-nodes-base.If',
38 |       name: 'If',
39 |       displayName: 'If',
40 |       description: 'Route items based on comparison operations',
41 |       sourceCode: 'test source code',
42 |       packageName: 'n8n-nodes-base',
43 |       hasCredentials: false,
44 |       isTrigger: false,
45 |       isWebhook: false
46 |     };
47 |     
48 |     await service.storeNode(nodeInfo);
49 |     console.log('   ✅ Successfully stored test node');
50 |     
51 |     // Check if it was stored
52 |     const retrievedNode = await service.getNodeInfo('n8n-nodes-base.If');
53 |     console.log('   Retrieved node:', retrievedNode ? 'Found' : 'Not found');
54 |     
55 |   } catch (error) {
56 |     console.error('❌ Test failed:', error);
57 |   } finally {
58 |     service.close();
59 |   }
60 | }
61 | 
62 | testSmallRebuild();
```

--------------------------------------------------------------------------------
/src/mcp/workflow-examples.ts:
--------------------------------------------------------------------------------

```typescript
  1 | /**
  2 |  * Example workflows for n8n AI agents to understand the structure
  3 |  */
  4 | 
  5 | export const MINIMAL_WORKFLOW_EXAMPLE = {
  6 |   nodes: [
  7 |     {
  8 |       name: "Webhook",
  9 |       type: "n8n-nodes-base.webhook",
 10 |       typeVersion: 2,
 11 |       position: [250, 300],
 12 |       parameters: {
 13 |         httpMethod: "POST",
 14 |         path: "webhook"
 15 |       }
 16 |     }
 17 |   ],
 18 |   connections: {}
 19 | };
 20 | 
 21 | export const SIMPLE_WORKFLOW_EXAMPLE = {
 22 |   nodes: [
 23 |     {
 24 |       name: "Webhook",
 25 |       type: "n8n-nodes-base.webhook",
 26 |       typeVersion: 2,
 27 |       position: [250, 300],
 28 |       parameters: {
 29 |         httpMethod: "POST",
 30 |         path: "webhook"
 31 |       }
 32 |     },
 33 |     {
 34 |       name: "Set",
 35 |       type: "n8n-nodes-base.set",
 36 |       typeVersion: 2,
 37 |       position: [450, 300],
 38 |       parameters: {
 39 |         mode: "manual",
 40 |         assignments: {
 41 |           assignments: [
 42 |             {
 43 |               name: "message",
 44 |               type: "string",
 45 |               value: "Hello"
 46 |             }
 47 |           ]
 48 |         }
 49 |       }
 50 |     },
 51 |     {
 52 |       name: "Respond to Webhook",
 53 |       type: "n8n-nodes-base.respondToWebhook",
 54 |       typeVersion: 1,
 55 |       position: [650, 300],
 56 |       parameters: {
 57 |         respondWith: "firstIncomingItem"
 58 |       }
 59 |     }
 60 |   ],
 61 |   connections: {
 62 |     "Webhook": {
 63 |       "main": [
 64 |         [
 65 |           {
 66 |             "node": "Set",
 67 |             "type": "main",
 68 |             "index": 0
 69 |           }
 70 |         ]
 71 |       ]
 72 |     },
 73 |     "Set": {
 74 |       "main": [
 75 |         [
 76 |           {
 77 |             "node": "Respond to Webhook",
 78 |             "type": "main",
 79 |             "index": 0
 80 |           }
 81 |         ]
 82 |       ]
 83 |     }
 84 |   }
 85 | };
 86 | 
 87 | export function getWorkflowExampleString(): string {
 88 |   return `Example workflow structure:
 89 | ${JSON.stringify(MINIMAL_WORKFLOW_EXAMPLE, null, 2)}
 90 | 
 91 | Each node MUST have:
 92 | - name: unique string identifier
 93 | - type: full node type with prefix (e.g., "n8n-nodes-base.webhook")
 94 | - typeVersion: number (usually 1 or 2)
 95 | - position: [x, y] coordinates array
 96 | - parameters: object with node-specific settings
 97 | 
 98 | Connections format:
 99 | {
100 |   "SourceNodeName": {
101 |     "main": [
102 |       [
103 |         {
104 |           "node": "TargetNodeName",
105 |           "type": "main",
106 |           "index": 0
107 |         }
108 |       ]
109 |     ]
110 |   }
111 | }`;
112 | }
```

--------------------------------------------------------------------------------
/src/mcp/tool-docs/workflow_management/n8n-get-workflow-minimal.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { ToolDocumentation } from '../types';
 2 | 
 3 | export const n8nGetWorkflowMinimalDoc: ToolDocumentation = {
 4 |   name: 'n8n_get_workflow_minimal',
 5 |   category: 'workflow_management',
 6 |   essentials: {
 7 |     description: 'Get minimal info: ID, name, active status, tags. Fast for listings.',
 8 |     keyParameters: ['id'],
 9 |     example: 'n8n_get_workflow_minimal({id: "workflow_123"})',
10 |     performance: 'Very fast (<50ms)',
11 |     tips: [
12 |       'Fastest way to check workflow exists',
13 |       'Perfect for status checks',
14 |       'Use in list displays'
15 |     ]
16 |   },
17 |   full: {
18 |     description: 'Retrieves only essential workflow information without nodes or connections. Returns minimal data needed for listings, status checks, and quick lookups. Optimized for performance when full workflow data is not needed.',
19 |     parameters: {
20 |       id: { type: 'string', required: true, description: 'Workflow ID to retrieve minimal info for' }
21 |     },
22 |     returns: 'Minimal workflow object with: id, name, active status, tags array, createdAt, updatedAt. No nodes, connections, or settings included.',
23 |     examples: [
24 |       'n8n_get_workflow_minimal({id: "abc123"}) - Quick existence check',
25 |       'const info = n8n_get_workflow_minimal({id: "xyz789"}); // Check if active'
26 |     ],
27 |     useCases: [
28 |       'Quick workflow existence checks',
29 |       'Display workflow lists',
30 |       'Check active/inactive status',
31 |       'Get workflow tags',
32 |       'Performance-critical operations'
33 |     ],
34 |     performance: 'Extremely fast - typically under 50ms. Returns only database metadata without loading workflow definition.',
35 |     bestPractices: [
36 |       'Use for list displays and dashboards',
37 |       'Ideal for existence checks before operations',
38 |       'Cache results for UI responsiveness',
39 |       'Combine with list_workflows for bulk checks'
40 |     ],
41 |     pitfalls: [
42 |       'Requires N8N_API_URL and N8N_API_KEY configured',
43 |       'No workflow content - cannot edit or validate',
44 |       'Tags may be empty array',
45 |       'Must use get_workflow for actual workflow data'
46 |     ],
47 |     relatedTools: ['n8n_list_workflows', 'n8n_get_workflow', 'n8n_get_workflow_structure', 'n8n_update_partial_workflow']
48 |   }
49 | };
```

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

```bash
 1 | #!/bin/bash
 2 | # Test script to verify Docker optimization (no n8n deps)
 3 | 
 4 | set -e
 5 | 
 6 | echo "🧪 Testing Docker optimization..."
 7 | echo ""
 8 | 
 9 | # Check if nodes.db exists
10 | if [ ! -f "data/nodes.db" ]; then
11 |     echo "❌ ERROR: data/nodes.db not found!"
12 |     echo "   Run 'npm run rebuild' first to create the database"
13 |     exit 1
14 | fi
15 | 
16 | # Build the image
17 | echo "📦 Building Docker image..."
18 | DOCKER_BUILDKIT=1 docker build -t n8n-mcp:test . > /dev/null 2>&1
19 | 
20 | # Check image size
21 | echo "📊 Checking image size..."
22 | SIZE=$(docker images n8n-mcp:test --format "{{.Size}}")
23 | echo "   Image size: $SIZE"
24 | 
25 | # Test that n8n is NOT in the image
26 | echo ""
27 | echo "🔍 Verifying no n8n dependencies..."
28 | if docker run --rm n8n-mcp:test sh -c "ls node_modules | grep -E '^n8n$|^n8n-|^@n8n'" 2>/dev/null; then
29 |     echo "❌ ERROR: Found n8n dependencies in runtime image!"
30 |     exit 1
31 | else
32 |     echo "✅ No n8n dependencies found (as expected)"
33 | fi
34 | 
35 | # Test that runtime dependencies ARE present
36 | echo ""
37 | echo "🔍 Verifying runtime dependencies..."
38 | EXPECTED_DEPS=("@modelcontextprotocol" "better-sqlite3" "express" "dotenv")
39 | for dep in "${EXPECTED_DEPS[@]}"; do
40 |     if docker run --rm n8n-mcp:test sh -c "ls node_modules | grep -q '$dep'" 2>/dev/null; then
41 |         echo "✅ Found: $dep"
42 |     else
43 |         echo "❌ Missing: $dep"
44 |         exit 1
45 |     fi
46 | done
47 | 
48 | # Test that the server starts
49 | echo ""
50 | echo "🚀 Testing server startup..."
51 | docker run --rm -d \
52 |     --name n8n-mcp-test \
53 |     -e MCP_MODE=http \
54 |     -e AUTH_TOKEN=test-token \
55 |     -e LOG_LEVEL=error \
56 |     n8n-mcp:test > /dev/null 2>&1
57 | 
58 | # Wait for startup
59 | sleep 3
60 | 
61 | # Check if running
62 | if docker ps | grep -q n8n-mcp-test; then
63 |     echo "✅ Server started successfully"
64 |     docker stop n8n-mcp-test > /dev/null 2>&1
65 | else
66 |     echo "❌ Server failed to start"
67 |     docker logs n8n-mcp-test 2>&1
68 |     exit 1
69 | fi
70 | 
71 | # Clean up
72 | docker rmi n8n-mcp:test > /dev/null 2>&1
73 | 
74 | echo ""
75 | echo "🎉 All tests passed! Docker optimization is working correctly."
76 | echo ""
77 | echo "📈 Benefits:"
78 | echo "   - No n8n dependencies in runtime image"
79 | echo "   - Image size: ~200MB (vs ~1.5GB with n8n)"
80 | echo "   - Build time: ~1-2 minutes (vs ~12 minutes)"
81 | echo "   - No version conflicts at runtime"
```

--------------------------------------------------------------------------------
/.github/BENCHMARK_THRESHOLDS.md:
--------------------------------------------------------------------------------

```markdown
 1 | # Performance Benchmark Thresholds
 2 | 
 3 | This file defines the expected performance thresholds for n8n-mcp operations.
 4 | 
 5 | ## Critical Operations
 6 | 
 7 | | Operation | Expected Time | Warning Threshold | Error Threshold |
 8 | |-----------|---------------|-------------------|-----------------|
 9 | | Node Loading (per package) | <100ms | 150ms | 200ms |
10 | | Database Query (simple) | <5ms | 10ms | 20ms |
11 | | Search (simple word) | <10ms | 20ms | 50ms |
12 | | Search (complex query) | <50ms | 100ms | 200ms |
13 | | Validation (simple config) | <1ms | 2ms | 5ms |
14 | | Validation (complex config) | <10ms | 20ms | 50ms |
15 | | MCP Tool Execution | <50ms | 100ms | 200ms |
16 | 
17 | ## Benchmark Categories
18 | 
19 | ### Node Loading Performance
20 | - **loadPackage**: Should handle large packages efficiently
21 | - **loadNodesFromPath**: Individual file loading should be fast
22 | - **parsePackageJson**: JSON parsing overhead should be minimal
23 | 
24 | ### Database Query Performance
25 | - **getNodeByType**: Direct lookups should be instant
26 | - **searchNodes**: Full-text search should scale well
27 | - **getAllNodes**: Pagination should prevent performance issues
28 | 
29 | ### Search Operations
30 | - **OR mode**: Should handle multiple terms efficiently
31 | - **AND mode**: More restrictive but still performant
32 | - **FUZZY mode**: Slower but acceptable for typo tolerance
33 | 
34 | ### Validation Performance
35 | - **minimal profile**: Fastest, only required fields
36 | - **ai-friendly profile**: Balanced performance
37 | - **strict profile**: Comprehensive but slower
38 | 
39 | ### MCP Tool Execution
40 | - Tools should respond quickly for interactive use
41 | - Complex operations may take longer but should remain responsive
42 | 
43 | ## Regression Detection
44 | 
45 | Performance regressions are detected when:
46 | 1. Any operation exceeds its warning threshold by 10%
47 | 2. Multiple operations show degradation in the same category
48 | 3. Average performance across all benchmarks degrades by 5%
49 | 
50 | ## Optimization Targets
51 | 
52 | Future optimization efforts should focus on:
53 | 1. **Search performance**: Implement FTS5 for better full-text search
54 | 2. **Caching**: Add intelligent caching for frequently accessed nodes
55 | 3. **Lazy loading**: Defer loading of large property schemas
56 | 4. **Batch operations**: Optimize bulk inserts and updates
```

--------------------------------------------------------------------------------
/scripts/test-fuzzy-fix.ts:
--------------------------------------------------------------------------------

```typescript
 1 | #!/usr/bin/env node
 2 | 
 3 | import { N8NDocumentationMCPServer } from '../src/mcp/server';
 4 | 
 5 | async function testFuzzyFix() {
 6 |   console.log('Testing FUZZY mode fix...\n');
 7 |   
 8 |   const server = new N8NDocumentationMCPServer();
 9 |   
10 |   // Wait for initialization
11 |   await new Promise(resolve => setTimeout(resolve, 1000));
12 |   
13 |   // Test 1: FUZZY mode with typo
14 |   console.log('Test 1: FUZZY mode with "slak" (typo for "slack")');
15 |   const fuzzyResult = await server.executeTool('search_nodes', {
16 |     query: 'slak',
17 |     mode: 'FUZZY',
18 |     limit: 5
19 |   });
20 |   
21 |   console.log(`Results: ${fuzzyResult.results.length} found`);
22 |   if (fuzzyResult.results.length > 0) {
23 |     console.log('✅ FUZZY mode now finds results!');
24 |     fuzzyResult.results.forEach((node: any, i: number) => {
25 |       console.log(`  ${i + 1}. ${node.nodeType} - ${node.displayName}`);
26 |     });
27 |   } else {
28 |     console.log('❌ FUZZY mode still not working');
29 |   }
30 |   
31 |   // Test 2: AND mode with explanation
32 |   console.log('\n\nTest 2: AND mode with "send message"');
33 |   const andResult = await server.executeTool('search_nodes', {
34 |     query: 'send message',
35 |     mode: 'AND',
36 |     limit: 5
37 |   });
38 |   
39 |   console.log(`Results: ${andResult.results.length} found`);
40 |   if (andResult.searchInfo) {
41 |     console.log('✅ AND mode now includes search info:');
42 |     console.log(`   ${andResult.searchInfo.message}`);
43 |     console.log(`   Tip: ${andResult.searchInfo.tip}`);
44 |   }
45 |   
46 |   console.log('\nFirst 5 results:');
47 |   andResult.results.slice(0, 5).forEach((node: any, i: number) => {
48 |     console.log(`  ${i + 1}. ${node.nodeType} - ${node.displayName}`);
49 |   });
50 |   
51 |   // Test 3: More typos
52 |   console.log('\n\nTest 3: More FUZZY tests');
53 |   const typos = ['htpp', 'webook', 'slck', 'emial'];
54 |   
55 |   for (const typo of typos) {
56 |     const result = await server.executeTool('search_nodes', {
57 |       query: typo,
58 |       mode: 'FUZZY',
59 |       limit: 1
60 |     });
61 |     
62 |     if (result.results.length > 0) {
63 |       console.log(`✅ "${typo}" → ${result.results[0].displayName}`);
64 |     } else {
65 |       console.log(`❌ "${typo}" → No results`);
66 |     }
67 |   }
68 |   
69 |   process.exit(0);
70 | }
71 | 
72 | // Run tests
73 | testFuzzyFix().catch(error => {
74 |   console.error('Test failed:', error);
75 |   process.exit(1);
76 | });
```

--------------------------------------------------------------------------------
/src/mcp/tool-docs/workflow_management/n8n-get-workflow-structure.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { ToolDocumentation } from '../types';
 2 | 
 3 | export const n8nGetWorkflowStructureDoc: ToolDocumentation = {
 4 |   name: 'n8n_get_workflow_structure',
 5 |   category: 'workflow_management',
 6 |   essentials: {
 7 |     description: 'Get workflow structure: nodes and connections only. No parameter details.',
 8 |     keyParameters: ['id'],
 9 |     example: 'n8n_get_workflow_structure({id: "workflow_123"})',
10 |     performance: 'Fast (75-150ms)',
11 |     tips: [
12 |       'Shows workflow topology',
13 |       'Node types without parameters',
14 |       'Perfect for visualization'
15 |     ]
16 |   },
17 |   full: {
18 |     description: 'Retrieves workflow structural information including node types, positions, and connections, but without detailed node parameters. Ideal for understanding workflow topology, creating visualizations, or analyzing workflow complexity without the overhead of full parameter data.',
19 |     parameters: {
20 |       id: { type: 'string', required: true, description: 'Workflow ID to retrieve structure for' }
21 |     },
22 |     returns: 'Workflow structure with: id, name, nodes array (id, name, type, position only), connections object. No node parameters, credentials, or settings included.',
23 |     examples: [
24 |       'n8n_get_workflow_structure({id: "abc123"}) - Visualize workflow',
25 |       'const structure = n8n_get_workflow_structure({id: "xyz789"}); // Analyze complexity'
26 |     ],
27 |     useCases: [
28 |       'Generate workflow visualizations',
29 |       'Analyze workflow complexity',
30 |       'Understand node relationships',
31 |       'Create workflow diagrams',
32 |       'Quick topology validation'
33 |     ],
34 |     performance: 'Fast retrieval - typically 75-150ms. Faster than get_workflow as parameters are stripped.',
35 |     bestPractices: [
36 |       'Use for visualization tools',
37 |       'Ideal for workflow analysis',
38 |       'Good for connection validation',
39 |       'Cache for UI diagram rendering'
40 |     ],
41 |     pitfalls: [
42 |       'Requires N8N_API_URL and N8N_API_KEY configured',
43 |       'No parameter data for configuration',
44 |       'Cannot validate node settings',
45 |       'Must use get_workflow for editing'
46 |     ],
47 |     relatedTools: ['n8n_get_workflow', 'n8n_validate_workflow_connections', 'n8n_get_workflow_minimal', 'validate_workflow_connections']
48 |   }
49 | };
```

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

```typescript
 1 | import { ToolDocumentation } from '../types';
 2 | 
 3 | export const n8nGetWorkflowDoc: ToolDocumentation = {
 4 |   name: 'n8n_get_workflow',
 5 |   category: 'workflow_management',
 6 |   essentials: {
 7 |     description: 'Get a workflow by ID. Returns the complete workflow including nodes, connections, and settings.',
 8 |     keyParameters: ['id'],
 9 |     example: 'n8n_get_workflow({id: "workflow_123"})',
10 |     performance: 'Fast (50-200ms)',
11 |     tips: [
12 |       'Returns complete workflow JSON',
13 |       'Includes all node parameters',
14 |       'Use get_workflow_minimal for faster listings'
15 |     ]
16 |   },
17 |   full: {
18 |     description: 'Retrieves a complete workflow from n8n by its ID. Returns full workflow definition including all nodes with their parameters, connections between nodes, and workflow settings. This is the primary tool for fetching workflows for viewing, editing, or cloning.',
19 |     parameters: {
20 |       id: { type: 'string', required: true, description: 'Workflow ID to retrieve' }
21 |     },
22 |     returns: 'Complete workflow object containing: id, name, active status, nodes array (with full parameters), connections object, settings, createdAt, updatedAt',
23 |     examples: [
24 |       'n8n_get_workflow({id: "abc123"}) - Get workflow for editing',
25 |       'const wf = n8n_get_workflow({id: "xyz789"}); // Clone workflow structure'
26 |     ],
27 |     useCases: [
28 |       'View workflow configuration',
29 |       'Export workflow for backup',
30 |       'Clone workflow structure',
31 |       'Debug workflow issues',
32 |       'Prepare for updates'
33 |     ],
34 |     performance: 'Fast retrieval - typically 50-200ms depending on workflow size. Cached by n8n for performance.',
35 |     bestPractices: [
36 |       'Check workflow exists before updating',
37 |       'Use for complete workflow data needs',
38 |       'Cache results when making multiple operations',
39 |       'Validate after retrieving if modifying'
40 |     ],
41 |     pitfalls: [
42 |       'Requires N8N_API_URL and N8N_API_KEY configured',
43 |       'Returns all data - use minimal/structure for performance',
44 |       'Workflow must exist or returns 404',
45 |       'Credentials are referenced but not included'
46 |     ],
47 |     relatedTools: ['n8n_get_workflow_minimal', 'n8n_get_workflow_structure', 'n8n_update_full_workflow', 'n8n_validate_workflow']
48 |   }
49 | };
```

--------------------------------------------------------------------------------
/tests/unit/database/__mocks__/better-sqlite3.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { vi } from 'vitest';
 2 | 
 3 | export class MockDatabase {
 4 |   private data = new Map<string, any[]>();
 5 |   private prepared = new Map<string, any>();
 6 |   public inTransaction = false;
 7 |   
 8 |   constructor() {
 9 |     this.data.set('nodes', []);
10 |     this.data.set('templates', []);
11 |     this.data.set('tools_documentation', []);
12 |   }
13 |   
14 |   prepare(sql: string) {
15 |     const key = this.extractTableName(sql);
16 |     const self = this;
17 |     
18 |     return {
19 |       all: vi.fn(() => self.data.get(key) || []),
20 |       get: vi.fn((id: string) => {
21 |         const items = self.data.get(key) || [];
22 |         return items.find(item => item.id === id);
23 |       }),
24 |       run: vi.fn((params: any) => {
25 |         const items = self.data.get(key) || [];
26 |         items.push(params);
27 |         self.data.set(key, items);
28 |         return { changes: 1, lastInsertRowid: items.length };
29 |       }),
30 |       iterate: vi.fn(function* () {
31 |         const items = self.data.get(key) || [];
32 |         for (const item of items) {
33 |           yield item;
34 |         }
35 |       }),
36 |       pluck: vi.fn(function(this: any) { return this; }),
37 |       expand: vi.fn(function(this: any) { return this; }),
38 |       raw: vi.fn(function(this: any) { return this; }),
39 |       columns: vi.fn(() => []),
40 |       bind: vi.fn(function(this: any) { return this; })
41 |     };
42 |   }
43 |   
44 |   exec(sql: string) {
45 |     // Mock schema creation
46 |     return true;
47 |   }
48 |   
49 |   close() {
50 |     // Mock close
51 |     return true;
52 |   }
53 |   
54 |   pragma(key: string, value?: any) {
55 |     // Mock pragma
56 |     if (key === 'journal_mode' && value === 'WAL') {
57 |       return 'wal';
58 |     }
59 |     return null;
60 |   }
61 |   
62 |   transaction<T>(fn: () => T): T {
63 |     this.inTransaction = true;
64 |     try {
65 |       const result = fn();
66 |       this.inTransaction = false;
67 |       return result;
68 |     } catch (error) {
69 |       this.inTransaction = false;
70 |       throw error;
71 |     }
72 |   }
73 |   
74 |   // Helper to extract table name from SQL
75 |   private extractTableName(sql: string): string {
76 |     const match = sql.match(/FROM\s+(\w+)|INTO\s+(\w+)|UPDATE\s+(\w+)/i);
77 |     return match ? (match[1] || match[2] || match[3]) : 'nodes';
78 |   }
79 |   
80 |   // Test helper to seed data
81 |   _seedData(table: string, data: any[]) {
82 |     this.data.set(table, data);
83 |   }
84 | }
85 | 
86 | export default vi.fn(() => new MockDatabase());
```

--------------------------------------------------------------------------------
/src/mcp/tool-docs/workflow_management/n8n-get-workflow-details.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { ToolDocumentation } from '../types';
 2 | 
 3 | export const n8nGetWorkflowDetailsDoc: ToolDocumentation = {
 4 |   name: 'n8n_get_workflow_details',
 5 |   category: 'workflow_management',
 6 |   essentials: {
 7 |     description: 'Get workflow details with metadata, version, execution stats. More info than get_workflow.',
 8 |     keyParameters: ['id'],
 9 |     example: 'n8n_get_workflow_details({id: "workflow_123"})',
10 |     performance: 'Fast (100-300ms)',
11 |     tips: [
12 |       'Includes execution statistics',
13 |       'Shows version history info',
14 |       'Contains metadata like tags'
15 |     ]
16 |   },
17 |   full: {
18 |     description: 'Retrieves comprehensive workflow details including metadata, execution statistics, version information, and usage analytics. Provides more information than get_workflow, including data not typically needed for editing but useful for monitoring and analysis.',
19 |     parameters: {
20 |       id: { type: 'string', required: true, description: 'Workflow ID to retrieve details for' }
21 |     },
22 |     returns: 'Extended workflow object with: id, name, nodes, connections, settings, plus metadata (tags, owner, shared users), execution stats (success/error counts, average runtime), version info, created/updated timestamps',
23 |     examples: [
24 |       'n8n_get_workflow_details({id: "abc123"}) - Get workflow with stats',
25 |       'const details = n8n_get_workflow_details({id: "xyz789"}); // Analyze performance'
26 |     ],
27 |     useCases: [
28 |       'Monitor workflow performance',
29 |       'Analyze execution patterns',
30 |       'View workflow metadata',
31 |       'Check version information',
32 |       'Audit workflow usage'
33 |     ],
34 |     performance: 'Slightly slower than get_workflow due to additional metadata - typically 100-300ms. Stats may be cached.',
35 |     bestPractices: [
36 |       'Use for monitoring and analysis',
37 |       'Check execution stats before optimization',
38 |       'Review error counts for debugging',
39 |       'Monitor average execution times'
40 |     ],
41 |     pitfalls: [
42 |       'Requires N8N_API_URL and N8N_API_KEY configured',
43 |       'More data than needed for simple edits',
44 |       'Stats may have slight delay',
45 |       'Not all n8n versions support all fields'
46 |     ],
47 |     relatedTools: ['n8n_get_workflow', 'n8n_list_executions', 'n8n_get_execution', 'n8n_list_workflows']
48 |   }
49 | };
```

--------------------------------------------------------------------------------
/vitest.config.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { defineConfig } from 'vitest/config';
 2 | import path from 'path';
 3 | 
 4 | export default defineConfig({
 5 |   test: {
 6 |     globals: true,
 7 |     environment: 'node',
 8 |     // Only include global-setup.ts, remove msw-setup.ts from global setup
 9 |     setupFiles: ['./tests/setup/global-setup.ts'],
10 |     // Load environment variables from .env.test
11 |     env: {
12 |       NODE_ENV: 'test'
13 |     },
14 |     // Test execution settings
15 |     pool: 'threads',
16 |     poolOptions: {
17 |       threads: {
18 |         singleThread: process.env.TEST_PARALLEL !== 'true',
19 |         maxThreads: parseInt(process.env.TEST_MAX_WORKERS || '4', 10),
20 |         minThreads: 1
21 |       }
22 |     },
23 |     // Retry configuration
24 |     retry: parseInt(process.env.TEST_RETRY_ATTEMPTS || '2', 10),
25 |     // Test reporter - reduce reporters in CI to prevent hanging
26 |     reporters: process.env.CI ? ['default', 'junit'] : ['default'],
27 |     outputFile: {
28 |       junit: './test-results/junit.xml'
29 |     },
30 |     coverage: {
31 |       provider: 'v8',
32 |       enabled: process.env.FEATURE_TEST_COVERAGE !== 'false',
33 |       reporter: process.env.CI ? ['lcov', 'text-summary'] : (process.env.COVERAGE_REPORTER || 'lcov,html,text-summary').split(','),
34 |       reportsDirectory: process.env.COVERAGE_DIR || './coverage',
35 |       exclude: [
36 |         'node_modules/',
37 |         'tests/',
38 |         '**/*.d.ts',
39 |         '**/*.test.ts',
40 |         '**/*.spec.ts',
41 |         'scripts/',
42 |         'dist/',
43 |         '**/test-*.ts',
44 |         '**/mock-*.ts',
45 |         '**/__mocks__/**'
46 |       ],
47 |       thresholds: {
48 |         lines: 80,
49 |         functions: 80,
50 |         branches: 75,
51 |         statements: 80
52 |       },
53 |       // Add coverage-specific settings to prevent hanging
54 |       all: false, // Don't collect coverage for untested files
55 |       skipFull: true // Skip files with 100% coverage
56 |     },
57 |     // Test isolation
58 |     isolate: true,
59 |     // Force exit after tests complete in CI to prevent hanging
60 |     forceRerunTriggers: ['**/tests/**/*.ts'],
61 |     teardownTimeout: 1000
62 |   },
63 |   resolve: {
64 |     alias: {
65 |       '@': path.resolve(__dirname, './src'),
66 |       '@tests': path.resolve(__dirname, './tests')
67 |     }
68 |   },
69 |   // TypeScript configuration
70 |   esbuild: {
71 |     target: 'node18'
72 |   },
73 |   // Define global constants
74 |   define: {
75 |     'process.env.TEST_ENVIRONMENT': JSON.stringify('true')
76 |   }
77 | });
```

--------------------------------------------------------------------------------
/PRIVACY.md:
--------------------------------------------------------------------------------

```markdown
 1 | # Privacy Policy for n8n-mcp Telemetry
 2 | 
 3 | ## Overview
 4 | n8n-mcp collects anonymous usage statistics to help improve the tool. This data collection is designed to respect user privacy while providing valuable insights into how the tool is used.
 5 | 
 6 | ## What We Collect
 7 | - **Anonymous User ID**: A hashed identifier derived from your machine characteristics (no personal information)
 8 | - **Tool Usage**: Which MCP tools are used and their performance metrics
 9 | - **Workflow Patterns**: Sanitized workflow structures (all sensitive data removed)
10 | - **Error Types**: Categories of errors encountered (no error messages with user data)
11 | - **System Information**: Platform, architecture, Node.js version, and n8n-mcp version
12 | 
13 | ## What We DON'T Collect
14 | - Personal information or usernames
15 | - API keys, tokens, or credentials
16 | - URLs, endpoints, or hostnames
17 | - Email addresses or contact information
18 | - File paths or directory structures
19 | - Actual workflow data or parameters
20 | - Database connection strings
21 | - Any authentication information
22 | 
23 | ## Data Sanitization
24 | All collected data undergoes automatic sanitization:
25 | - URLs are replaced with `[URL]` or `[REDACTED]`
26 | - Long alphanumeric strings (potential keys) are replaced with `[KEY]`
27 | - Email addresses are replaced with `[EMAIL]`
28 | - Authentication-related fields are completely removed
29 | 
30 | ## Data Storage
31 | - Data is stored securely using Supabase
32 | - Anonymous users have write-only access (cannot read data back)
33 | - Row Level Security (RLS) policies prevent data access by anonymous users
34 | 
35 | ## Opt-Out
36 | You can disable telemetry at any time:
37 | ```bash
38 | npx n8n-mcp telemetry disable
39 | ```
40 | 
41 | To re-enable:
42 | ```bash
43 | npx n8n-mcp telemetry enable
44 | ```
45 | 
46 | To check status:
47 | ```bash
48 | npx n8n-mcp telemetry status
49 | ```
50 | 
51 | ## Data Usage
52 | Collected data is used solely to:
53 | - Understand which features are most used
54 | - Identify common error patterns
55 | - Improve tool performance and reliability
56 | - Guide development priorities
57 | 
58 | ## Data Retention
59 | - Data is retained for analysis purposes
60 | - No personal identification is possible from the collected data
61 | 
62 | ## Changes to This Policy
63 | We may update this privacy policy from time to time. Updates will be reflected in this document.
64 | 
65 | ## Contact
66 | For questions about telemetry or privacy, please open an issue on GitHub:
67 | https://github.com/czlonkowski/n8n-mcp/issues
68 | 
69 | Last updated: 2025-09-25
```

--------------------------------------------------------------------------------
/scripts/extract-changelog.js:
--------------------------------------------------------------------------------

```javascript
 1 | #!/usr/bin/env node
 2 | 
 3 | /**
 4 |  * Extract changelog content for a specific version
 5 |  * Used by GitHub Actions to extract release notes
 6 |  */
 7 | 
 8 | const fs = require('fs');
 9 | const path = require('path');
10 | 
11 | function extractChangelog(version, changelogPath) {
12 |   try {
13 |     if (!fs.existsSync(changelogPath)) {
14 |       console.error(`Changelog file not found at ${changelogPath}`);
15 |       process.exit(1);
16 |     }
17 | 
18 |     const content = fs.readFileSync(changelogPath, 'utf8');
19 |     const lines = content.split('\n');
20 |     
21 |     // Find the start of this version's section
22 |     const versionHeaderRegex = new RegExp(`^## \\[${version.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\]`);
23 |     let startIndex = -1;
24 |     let endIndex = -1;
25 |     
26 |     for (let i = 0; i < lines.length; i++) {
27 |       if (versionHeaderRegex.test(lines[i])) {
28 |         startIndex = i;
29 |         break;
30 |       }
31 |     }
32 |     
33 |     if (startIndex === -1) {
34 |       console.error(`No changelog entries found for version ${version}`);
35 |       process.exit(1);
36 |     }
37 |     
38 |     // Find the end of this version's section (next version or end of file)
39 |     for (let i = startIndex + 1; i < lines.length; i++) {
40 |       if (lines[i].startsWith('## [') && !lines[i].includes('Unreleased')) {
41 |         endIndex = i;
42 |         break;
43 |       }
44 |     }
45 |     
46 |     if (endIndex === -1) {
47 |       endIndex = lines.length;
48 |     }
49 |     
50 |     // Extract the section content
51 |     const sectionLines = lines.slice(startIndex, endIndex);
52 |     
53 |     // Remove the version header and any trailing empty lines
54 |     let contentLines = sectionLines.slice(1);
55 |     while (contentLines.length > 0 && contentLines[contentLines.length - 1].trim() === '') {
56 |       contentLines.pop();
57 |     }
58 |     
59 |     if (contentLines.length === 0) {
60 |       console.error(`No content found for version ${version}`);
61 |       process.exit(1);
62 |     }
63 |     
64 |     const releaseNotes = contentLines.join('\n').trim();
65 |     
66 |     // Write to stdout for GitHub Actions
67 |     console.log(releaseNotes);
68 |     
69 |   } catch (error) {
70 |     console.error(`Error extracting changelog: ${error.message}`);
71 |     process.exit(1);
72 |   }
73 | }
74 | 
75 | // Parse command line arguments
76 | const version = process.argv[2];
77 | const changelogPath = process.argv[3];
78 | 
79 | if (!version || !changelogPath) {
80 |   console.error('Usage: extract-changelog.js <version> <changelog-path>');
81 |   process.exit(1);
82 | }
83 | 
84 | extractChangelog(version, changelogPath);
```
Page 2/63FirstPrevNextLast