#
tokens: 49807/50000 70/628 files (page 2/46)
lines: off (toggle) GitHub
raw markdown copy
This is page 2 of 46. Use http://codebase.md/czlonkowski/n8n-mcp?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

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

```markdown
# Windsurf Setup

Connect n8n-MCP to Windsurf IDE for enhanced n8n workflow development with AI assistance.

[![n8n-mcp Windsurf Setup Tutorial](./img/windsurf_tut.png)](https://www.youtube.com/watch?v=klxxT1__izg)

## Video Tutorial

Watch the complete setup process: [n8n-MCP Windsurf Setup Tutorial](https://www.youtube.com/watch?v=klxxT1__izg)

## Setup Process

### 1. Access MCP Configuration

1. Go to Settings in Windsurf
2. Navigate to Windsurf Settings
3. Go to MCP Servers > Manage Plugins
4. Click "View Raw Config"

### 2. Add n8n-MCP Configuration

Copy the configuration from this repository and add it to your MCP config:

**Basic configuration (documentation tools only):**
```json
{
  "mcpServers": {
    "n8n-mcp": {
      "command": "npx",
      "args": ["n8n-mcp"],
      "env": {
        "MCP_MODE": "stdio",
        "LOG_LEVEL": "error",
        "DISABLE_CONSOLE_OUTPUT": "true"
      }
    }
  }
}
```

**Full configuration (with n8n management tools):**
```json
{
  "mcpServers": {
    "n8n-mcp": {
      "command": "npx",
      "args": ["n8n-mcp"],
      "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"
      }
    }
  }
}
```

### 3. Configure n8n Connection

1. Replace `https://your-n8n-instance.com` with your actual n8n URL
2. Replace `your-api-key` with your n8n API key
3. Click refresh to apply the changes

### 4. Set Up Project Instructions

1. Create a `.windsurfrules` file in your project root
2. Copy the Claude Project instructions from the [main README's Claude Project Setup section](../README.md#-claude-project-setup)

```

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

```typescript
#!/usr/bin/env npx tsx
/**
 * Debug workflow tracking in telemetry manager
 */

import { TelemetryManager } from '../src/telemetry/telemetry-manager';

// Get the singleton instance
const telemetry = TelemetryManager.getInstance();

const testWorkflow = {
  nodes: [
    {
      id: 'webhook1',
      type: 'n8n-nodes-base.webhook',
      name: 'Webhook',
      position: [0, 0],
      parameters: { 
        path: '/test-' + Date.now(),
        httpMethod: 'POST'
      }
    },
    {
      id: 'http1',
      type: 'n8n-nodes-base.httpRequest',
      name: 'HTTP Request',
      position: [250, 0],
      parameters: { 
        url: 'https://api.example.com/data',
        method: 'GET'
      }
    },
    {
      id: 'slack1',
      type: 'n8n-nodes-base.slack',
      name: 'Slack',
      position: [500, 0],
      parameters: {
        channel: '#general',
        text: 'Workflow complete!'
      }
    }
  ],
  connections: {
    'webhook1': {
      main: [[{ node: 'http1', type: 'main', index: 0 }]]
    },
    'http1': {
      main: [[{ node: 'slack1', type: 'main', index: 0 }]]
    }
  }
};

console.log('🧪 Testing Workflow Tracking\n');
console.log('Workflow has', testWorkflow.nodes.length, 'nodes');

// Track the workflow
console.log('Calling trackWorkflowCreation...');
telemetry.trackWorkflowCreation(testWorkflow, true);

console.log('Waiting for async processing...');

// Wait for setImmediate to process
setTimeout(async () => {
  console.log('\nForcing flush...');
  await telemetry.flush();
  console.log('✅ Flush complete!');
  
  console.log('\nWorkflow should now be in the telemetry_workflows table.');
  console.log('Check with: SELECT * FROM telemetry_workflows ORDER BY created_at DESC LIMIT 1;');
}, 2000);

```

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

```typescript
#!/usr/bin/env npx tsx
/**
 * Test workflow sanitizer
 */

import { WorkflowSanitizer } from '../src/telemetry/workflow-sanitizer';

const testWorkflow = {
  nodes: [
    {
      id: 'webhook1',
      type: 'n8n-nodes-base.webhook',
      name: 'Webhook',
      position: [0, 0],
      parameters: { 
        path: '/test-webhook',
        httpMethod: 'POST'
      }
    },
    {
      id: 'http1',
      type: 'n8n-nodes-base.httpRequest',
      name: 'HTTP Request',
      position: [250, 0],
      parameters: { 
        url: 'https://api.example.com/endpoint',
        method: 'GET',
        authentication: 'genericCredentialType',
        sendHeaders: true,
        headerParameters: {
          parameters: [
            {
              name: 'Authorization',
              value: 'Bearer sk-1234567890abcdef'
            }
          ]
        }
      }
    }
  ],
  connections: {
    'webhook1': {
      main: [[{ node: 'http1', type: 'main', index: 0 }]]
    }
  }
};

console.log('🧪 Testing Workflow Sanitizer\n');
console.log('Original workflow has', testWorkflow.nodes.length, 'nodes');

try {
  const sanitized = WorkflowSanitizer.sanitizeWorkflow(testWorkflow);
  
  console.log('\n✅ Sanitization successful!');
  console.log('\nSanitized output:');
  console.log(JSON.stringify(sanitized, null, 2));
  
  console.log('\n📊 Metrics:');
  console.log('- Workflow Hash:', sanitized.workflowHash);
  console.log('- Node Count:', sanitized.nodeCount);
  console.log('- Node Types:', sanitized.nodeTypes);
  console.log('- Has Trigger:', sanitized.hasTrigger);
  console.log('- Has Webhook:', sanitized.hasWebhook);
  console.log('- Complexity:', sanitized.complexity);
} catch (error) {
  console.error('❌ Sanitization failed:', error);
}

```

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

```typescript
import { ToolDocumentation } from '../types';

export const listTasksDoc: ToolDocumentation = {
  name: 'list_tasks',
  category: 'templates',
  essentials: {
    description: 'List task templates by category: HTTP/API, Webhooks, Database, AI, Data Processing, Communication.',
    keyParameters: ['category'],
    example: 'list_tasks({category: "HTTP/API"})',
    performance: 'Instant',
    tips: [
      'Categories: HTTP/API, Webhooks, Database, AI',
      'Shows pre-configured node settings',
      'Use get_node_for_task for details'
    ]
  },
  full: {
    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.',
    parameters: {
      category: { type: 'string', description: 'Filter by category (optional)' }
    },
    returns: 'Array of tasks with name, category, description, nodeType',
    examples: [
      'list_tasks() - Get all task templates',
      'list_tasks({category: "Database"}) - Database-related tasks',
      'list_tasks({category: "AI"}) - AI automation tasks'
    ],
    useCases: [
      'Discover common automation patterns',
      'Find pre-configured solutions',
      'Learn node usage patterns',
      'Quick workflow setup'
    ],
    performance: 'Instant - Static task list',
    bestPractices: [
      'Browse all categories first',
      'Use get_node_for_task for config',
      'Combine multiple tasks in workflows'
    ],
    pitfalls: [
      'Tasks are templates, customize as needed',
      'Not all nodes have task templates'
    ],
    relatedTools: ['get_node_for_task', 'search_templates', 'get_templates_for_task']
  }
};
```

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

```javascript
#!/usr/bin/env node
/**
 * Test get_node_info to diagnose timeout issues
 */

const { N8NDocumentationMCPServer } = require('../dist/mcp/server');

async function testNodeInfo() {
  console.log('🔍 Testing get_node_info...\n');
  
  try {
    const server = new N8NDocumentationMCPServer();
    await new Promise(resolve => setTimeout(resolve, 500));
    
    const nodes = [
      'nodes-base.httpRequest',
      'nodes-base.webhook',
      'nodes-langchain.agent'
    ];
    
    for (const nodeType of nodes) {
      console.log(`Testing ${nodeType}...`);
      const start = Date.now();
      
      try {
        const result = await server.executeTool('get_node_info', { nodeType });
        const elapsed = Date.now() - start;
        const size = JSON.stringify(result).length;
        
        console.log(`✅ Success in ${elapsed}ms`);
        console.log(`   Size: ${(size / 1024).toFixed(1)}KB`);
        console.log(`   Properties: ${result.properties?.length || 0}`);
        console.log(`   Operations: ${result.operations?.length || 0}`);
        
        // Check for issues
        if (size > 50000) {
          console.log(`   ⚠️  WARNING: Response over 50KB!`);
        }
        
        // Check property quality
        const propsWithoutDesc = result.properties?.filter(p => !p.description && !p.displayName).length || 0;
        if (propsWithoutDesc > 0) {
          console.log(`   ⚠️  ${propsWithoutDesc} properties without descriptions`);
        }
        
      } catch (error) {
        const elapsed = Date.now() - start;
        console.log(`❌ Failed after ${elapsed}ms: ${error.message}`);
      }
      
      console.log('');
    }
    
  } catch (error) {
    console.error('Fatal error:', error);
  }
}

testNodeInfo().catch(console.error);
```

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

```markdown
# Cursor Setup

Connect n8n-MCP to Cursor IDE for enhanced n8n workflow development with AI assistance.

[![n8n-mcp Cursor Setup Tutorial](./img/cursor_tut.png)](https://www.youtube.com/watch?v=hRmVxzLGJWI)

## Video Tutorial

Watch the complete setup process: [n8n-MCP Cursor Setup Tutorial](https://www.youtube.com/watch?v=hRmVxzLGJWI)

## Setup Process

### 1. Create MCP Configuration

1. Create a `.cursor` folder in your project root
2. Create `mcp.json` file inside the `.cursor` folder
3. Copy the configuration from this repository

**Basic configuration (documentation tools only):**
```json
{
  "mcpServers": {
    "n8n-mcp": {
      "command": "npx",
      "args": ["n8n-mcp"],
      "env": {
        "MCP_MODE": "stdio",
        "LOG_LEVEL": "error",
        "DISABLE_CONSOLE_OUTPUT": "true"
      }
    }
  }
}
```

**Full configuration (with n8n management tools):**
```json
{
  "mcpServers": {
    "n8n-mcp": {
      "command": "npx",
      "args": ["n8n-mcp"],
      "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"
      }
    }
  }
}
```

### 2. Configure n8n Connection

1. Replace `https://your-n8n-instance.com` with your actual n8n URL
2. Replace `your-api-key` with your n8n API key

### 3. Enable MCP Server

1. Click "Enable MCP Server" button in Cursor
2. Go to Cursor Settings
3. Search for "mcp"
4. Confirm MCP is working

### 4. Set Up Project Instructions

1. In your Cursor chat, invoke "create rule" and hit Tab
2. Name the rule (e.g., "n8n-mcp")
3. Set rule type to "always"
4. Copy the Claude Project instructions from the [main README's Claude Project Setup section](../README.md#-claude-project-setup)


```

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

```json
{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
  "extends": [
    "config:base"
  ],
  "schedule": ["after 9am on monday"],
  "timezone": "UTC",
  "packageRules": [
    {
      "description": "Group all n8n-related updates",
      "groupName": "n8n dependencies",
      "matchPackagePatterns": ["^n8n", "^@n8n/"],
      "matchUpdateTypes": ["minor", "patch"],
      "schedule": ["after 9am on monday"]
    },
    {
      "description": "Require approval for major n8n updates",
      "matchPackagePatterns": ["^n8n", "^@n8n/"],
      "matchUpdateTypes": ["major"],
      "dependencyDashboardApproval": true
    },
    {
      "description": "Disable updates for other dependencies",
      "excludePackagePatterns": ["^n8n", "^@n8n/"],
      "enabled": false
    }
  ],
  "postUpdateOptions": [
    "npmDedupe"
  ],
  "prConcurrentLimit": 1,
  "prCreation": "immediate",
  "labels": ["dependencies", "n8n-update"],
  "assignees": ["@czlonkowski"],
  "reviewers": ["@czlonkowski"],
  "commitMessagePrefix": "chore: ",
  "commitMessageTopic": "{{depName}}",
  "commitMessageExtra": "from {{currentVersion}} to {{newVersion}}",
  "prBodyDefinitions": {
    "Package": "{{depName}}",
    "Type": "{{depType}}",
    "Update": "{{updateType}}",
    "Current": "{{currentVersion}}",
    "New": "{{newVersion}}",
    "Change": "[Compare]({{compareUrl}})"
  },
  "prBodyColumns": ["Package", "Type", "Update", "Current", "New", "Change"],
  "prBodyNotes": [
    "**Important**: Please review the [n8n release notes](https://docs.n8n.io/release-notes/) for breaking changes.",
    "",
    "After merging, please:",
    "1. Run `npm run rebuild` to update the node database",
    "2. Run `npm run validate` to ensure all nodes are properly loaded",
    "3. Test critical functionality"
  ]
}
```

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

```typescript
import { ToolDocumentation } from '../types';

export const getNodeDocumentationDoc: ToolDocumentation = {
  name: 'get_node_documentation',
  category: 'configuration',
  essentials: {
    description: 'Get readable docs with examples/auth/patterns. Better than raw schema! 87% coverage. Format: "nodes-base.slack"',
    keyParameters: ['nodeType'],
    example: 'get_node_documentation({nodeType: "nodes-base.slack"})',
    performance: 'Fast - pre-parsed',
    tips: [
      '87% coverage',
      'Includes auth examples',
      'Human-readable format'
    ]
  },
  full: {
    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.',
    parameters: {
      nodeType: { type: 'string', required: true, description: 'Full node type with prefix (e.g., "nodes-base.slack")' }
    },
    returns: 'Parsed markdown documentation with examples, authentication guides, common patterns',
    examples: [
      'get_node_documentation({nodeType: "nodes-base.slack"}) - Slack usage guide',
      'get_node_documentation({nodeType: "nodes-base.googleSheets"}) - Sheets examples'
    ],
    useCases: [
      'Understanding authentication setup',
      'Finding usage examples',
      'Learning common patterns'
    ],
    performance: 'Fast - Pre-parsed documentation stored in database',
    bestPractices: [
      'Use for learning node usage',
      'Check coverage with get_database_statistics',
      'Combine with get_node_essentials'
    ],
    pitfalls: [
      'Not all nodes have docs (87% coverage)',
      'May be outdated for new features',
      'Requires full node type prefix'
    ],
    relatedTools: ['get_node_info', 'get_node_essentials', 'search_nodes']
  }
};
```

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

```typescript
/**
 * Test script to verify error message tracking is working
 */

import { telemetry } from '../src/telemetry';

async function testErrorTracking() {
  console.log('=== Testing Error Message Tracking ===\n');

  // Track session first
  console.log('1. Starting session...');
  telemetry.trackSessionStart();

  // Track an error WITH a message
  console.log('\n2. Tracking error WITH message:');
  const testErrorMessage = 'This is a test error message with sensitive data: password=secret123 and [email protected]';
  telemetry.trackError(
    'TypeError',
    'tool_execution',
    'test_tool',
    testErrorMessage
  );
  console.log(`   Original message: "${testErrorMessage}"`);

  // Track an error WITHOUT a message
  console.log('\n3. Tracking error WITHOUT message:');
  telemetry.trackError(
    'Error',
    'tool_execution',
    'test_tool2'
  );

  // Check the event queue
  const metrics = telemetry.getMetrics();
  console.log('\n4. Telemetry metrics:');
  console.log('   Status:', metrics.status);
  console.log('   Events queued:', metrics.tracking.eventsQueued);

  // Get raw event queue to inspect
  const eventTracker = (telemetry as any).eventTracker;
  const queue = eventTracker.getEventQueue();

  console.log('\n5. Event queue contents:');
  queue.forEach((event, i) => {
    console.log(`\n   Event ${i + 1}:`);
    console.log(`   - Type: ${event.event}`);
    console.log(`   - Properties:`, JSON.stringify(event.properties, null, 6));
  });

  // Flush to database
  console.log('\n6. Flushing to database...');
  await telemetry.flush();

  console.log('\n7. Done! Check Supabase for error events with "error" field.');
  console.log('   Query: SELECT * FROM telemetry_events WHERE event = \'error_occurred\' ORDER BY created_at DESC LIMIT 5;');
}

testErrorTracking().catch(console.error);

```

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

```javascript
#!/usr/bin/env node

/**
 * Sync version from package.json to package.runtime.json and README.md
 * This ensures all files always have the same version
 */

const fs = require('fs');
const path = require('path');

const packageJsonPath = path.join(__dirname, '..', 'package.json');
const packageRuntimePath = path.join(__dirname, '..', 'package.runtime.json');
const readmePath = path.join(__dirname, '..', 'README.md');

try {
  // Read package.json
  const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
  const version = packageJson.version;
  
  // Read package.runtime.json
  const packageRuntime = JSON.parse(fs.readFileSync(packageRuntimePath, 'utf-8'));
  
  // Update version if different
  if (packageRuntime.version !== version) {
    packageRuntime.version = version;
    
    // Write back with proper formatting
    fs.writeFileSync(
      packageRuntimePath, 
      JSON.stringify(packageRuntime, null, 2) + '\n',
      'utf-8'
    );
    
    console.log(`✅ Updated package.runtime.json version to ${version}`);
  } else {
    console.log(`✓ package.runtime.json already at version ${version}`);
  }
  
  // Update README.md version badge
  let readmeContent = fs.readFileSync(readmePath, 'utf-8');
  const versionBadgeRegex = /(\[!\[Version\]\(https:\/\/img\.shields\.io\/badge\/version-)[^-]+(-.+?\)\])/;
  const newVersionBadge = `$1${version}$2`;
  const updatedReadmeContent = readmeContent.replace(versionBadgeRegex, newVersionBadge);
  
  if (updatedReadmeContent !== readmeContent) {
    fs.writeFileSync(readmePath, updatedReadmeContent);
    console.log(`✅ Updated README.md version badge to ${version}`);
  } else {
    console.log(`✓ README.md already has version badge ${version}`);
  }
} catch (error) {
  console.error('❌ Error syncing version:', error.message);
  process.exit(1);
}
```

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

```yaml
# docker-compose.yml
# For optimized builds with BuildKit, use: docker-compose -f docker-compose.buildkit.yml up
version: '3.8'

services:
  n8n-mcp:
    image: ghcr.io/czlonkowski/n8n-mcp:latest
    container_name: n8n-mcp
    restart: unless-stopped
    
    # Environment configuration
    environment:
      # Mode configuration
      MCP_MODE: ${MCP_MODE:-http}
      USE_FIXED_HTTP: ${USE_FIXED_HTTP:-true}  # Use fixed implementation for stability
      AUTH_TOKEN: ${AUTH_TOKEN:?AUTH_TOKEN is required for HTTP mode}
      
      # Application settings
      NODE_ENV: ${NODE_ENV:-production}
      LOG_LEVEL: ${LOG_LEVEL:-info}
      PORT: ${PORT:-3000}
      
      # Database
      NODE_DB_PATH: ${NODE_DB_PATH:-/app/data/nodes.db}
      REBUILD_ON_START: ${REBUILD_ON_START:-false}

      # Telemetry: Anonymous usage statistics are ENABLED by default
      # To opt-out, uncomment and set to 'true':
      # N8N_MCP_TELEMETRY_DISABLED: ${N8N_MCP_TELEMETRY_DISABLED:-true}

      # Optional: n8n API configuration (enables 16 additional management tools)
      # Uncomment and configure to enable n8n workflow management
      # N8N_API_URL: ${N8N_API_URL}
      # N8N_API_KEY: ${N8N_API_KEY}
      # N8N_API_TIMEOUT: ${N8N_API_TIMEOUT:-30000}
      # N8N_API_MAX_RETRIES: ${N8N_API_MAX_RETRIES:-3}
    
    # Volumes for persistence
    volumes:
      - n8n-mcp-data:/app/data
    
    # Port mapping
    ports:
      - "${PORT:-3000}:3000"
    
    # Resource limits
    deploy:
      resources:
        limits:
          memory: 512M
        reservations:
          memory: 256M
    
    # Health check
    healthcheck:
      test: ["CMD", "curl", "-f", "http://127.0.0.1:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

# Named volume for data persistence
volumes:
  n8n-mcp-data:
    driver: local
```

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

```typescript
import { z } from 'zod';
import dotenv from 'dotenv';
import { logger } from '../utils/logger';

// n8n API configuration schema
const n8nApiConfigSchema = z.object({
  N8N_API_URL: z.string().url().optional(),
  N8N_API_KEY: z.string().min(1).optional(),
  N8N_API_TIMEOUT: z.coerce.number().positive().default(30000),
  N8N_API_MAX_RETRIES: z.coerce.number().positive().default(3),
});

// Track if we've loaded env vars
let envLoaded = false;

// Parse and validate n8n API configuration
export function getN8nApiConfig() {
  // Load environment variables on first access
  if (!envLoaded) {
    dotenv.config();
    envLoaded = true;
  }
  
  const result = n8nApiConfigSchema.safeParse(process.env);
  
  if (!result.success) {
    return null;
  }
  
  const config = result.data;
  
  // Check if both URL and API key are provided
  if (!config.N8N_API_URL || !config.N8N_API_KEY) {
    return null;
  }
  
  return {
    baseUrl: config.N8N_API_URL,
    apiKey: config.N8N_API_KEY,
    timeout: config.N8N_API_TIMEOUT,
    maxRetries: config.N8N_API_MAX_RETRIES,
  };
}

// Helper to check if n8n API is configured (lazy check)
export function isN8nApiConfigured(): boolean {
  const config = getN8nApiConfig();
  return config !== null;
}

/**
 * Create n8n API configuration from instance context
 * Used for flexible instance configuration support
 */
export function getN8nApiConfigFromContext(context: {
  n8nApiUrl?: string;
  n8nApiKey?: string;
  n8nApiTimeout?: number;
  n8nApiMaxRetries?: number;
}): N8nApiConfig | null {
  if (!context.n8nApiUrl || !context.n8nApiKey) {
    return null;
  }

  return {
    baseUrl: context.n8nApiUrl,
    apiKey: context.n8nApiKey,
    timeout: context.n8nApiTimeout ?? 30000,
    maxRetries: context.n8nApiMaxRetries ?? 3,
  };
}

// Type export
export type N8nApiConfig = NonNullable<ReturnType<typeof getN8nApiConfig>>;
```

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

```typescript
/**
 * Node Repository Utility for Integration Tests
 *
 * Provides a singleton NodeRepository instance for integration tests
 * that require validation or autofix functionality.
 */

import path from 'path';
import { createDatabaseAdapter, DatabaseAdapter } from '../../../../src/database/database-adapter';
import { NodeRepository } from '../../../../src/database/node-repository';

let repositoryInstance: NodeRepository | null = null;
let dbInstance: DatabaseAdapter | null = null;

/**
 * Get or create NodeRepository instance
 *
 * Uses the production nodes.db database (data/nodes.db).
 *
 * @returns Singleton NodeRepository instance
 * @throws {Error} If database file cannot be found or opened
 *
 * @example
 * const repository = await getNodeRepository();
 * const nodeInfo = await repository.getNodeByType('n8n-nodes-base.webhook');
 */
export async function getNodeRepository(): Promise<NodeRepository> {
  if (repositoryInstance) {
    return repositoryInstance;
  }

  const dbPath = path.join(process.cwd(), 'data/nodes.db');
  dbInstance = await createDatabaseAdapter(dbPath);
  repositoryInstance = new NodeRepository(dbInstance);

  return repositoryInstance;
}

/**
 * Close database and reset repository instance
 *
 * Should be called in test cleanup (afterAll) to prevent resource leaks.
 * Properly closes the database connection and resets the singleton.
 *
 * @example
 * afterAll(async () => {
 *   await closeNodeRepository();
 * });
 */
export async function closeNodeRepository(): Promise<void> {
  if (dbInstance && typeof dbInstance.close === 'function') {
    await dbInstance.close();
  }
  dbInstance = null;
  repositoryInstance = null;
}

/**
 * Reset repository instance (useful for test cleanup)
 *
 * @deprecated Use closeNodeRepository() instead to properly close database connections
 */
export function resetNodeRepository(): void {
  repositoryInstance = null;
}

```

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

```typescript
import { Factory } from 'fishery';
import { faker } from '@faker-js/faker';

/**
 * Interface for n8n node property definitions.
 * Represents the structure of properties that configure node behavior.
 */
interface PropertyDefinition {
  name: string;
  displayName: string;
  type: string;
  default?: any;
  required?: boolean;
  description?: string;
  options?: any[];
}

/**
 * Factory for generating PropertyDefinition test data.
 * Creates realistic property configurations for testing node validation and processing.
 * 
 * @example
 * ```typescript
 * // Create a single property
 * const prop = PropertyDefinitionFactory.build();
 * 
 * // Create a required string property
 * const urlProp = PropertyDefinitionFactory.build({
 *   name: 'url',
 *   displayName: 'URL',
 *   type: 'string',
 *   required: true
 * });
 * 
 * // Create an options property with choices
 * const methodProp = PropertyDefinitionFactory.build({
 *   name: 'method',
 *   type: 'options',
 *   options: [
 *     { name: 'GET', value: 'GET' },
 *     { name: 'POST', value: 'POST' }
 *   ]
 * });
 * 
 * // Create multiple properties for a node
 * const nodeProperties = PropertyDefinitionFactory.buildList(5);
 * ```
 */
export const PropertyDefinitionFactory = Factory.define<PropertyDefinition>(() => ({
  name: faker.word.noun() + faker.word.adjective().charAt(0).toUpperCase() + faker.word.adjective().slice(1),
  displayName: faker.helpers.arrayElement(['URL', 'Method', 'Headers', 'Body', 'Authentication']),
  type: faker.helpers.arrayElement(['string', 'number', 'boolean', 'options', 'json']),
  default: faker.datatype.boolean() ? faker.word.sample() : undefined,
  required: faker.datatype.boolean(),
  description: faker.lorem.sentence(),
  options: faker.datatype.boolean() ? [
    {
      name: faker.word.noun(),
      value: faker.word.noun(),
      description: faker.lorem.sentence()
    }
  ] : undefined
}));
```

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

```yaml
version: '3.8'

services:
  # n8n workflow automation
  n8n:
    image: n8nio/n8n:latest
    container_name: n8n
    restart: unless-stopped
    ports:
      - "${N8N_PORT:-5678}:5678"
    environment:
      - N8N_BASIC_AUTH_ACTIVE=${N8N_BASIC_AUTH_ACTIVE:-true}
      - N8N_BASIC_AUTH_USER=${N8N_BASIC_AUTH_USER:-admin}
      - N8N_BASIC_AUTH_PASSWORD=${N8N_BASIC_AUTH_PASSWORD:-password}
      - N8N_HOST=${N8N_HOST:-localhost}
      - N8N_PORT=5678
      - N8N_PROTOCOL=${N8N_PROTOCOL:-http}
      - WEBHOOK_URL=${N8N_WEBHOOK_URL:-http://localhost:5678/}
      - N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
    volumes:
      - n8n_data:/home/node/.n8n
    networks:
      - n8n-network
    healthcheck:
      test: ["CMD", "sh", "-c", "wget --quiet --spider --tries=1 --timeout=10 http://localhost:5678/healthz || exit 1"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 30s

  # n8n-mcp server for AI assistance
  n8n-mcp:
    build:
      context: .
      dockerfile: Dockerfile  # Uses standard Dockerfile with N8N_MODE=true env var
    image: ghcr.io/${GITHUB_REPOSITORY:-czlonkowski/n8n-mcp}/n8n-mcp:${VERSION:-latest}
    container_name: n8n-mcp
    restart: unless-stopped
    ports:
      - "${MCP_PORT:-3000}:3000"
    environment:
      - NODE_ENV=production
      - N8N_MODE=true
      - MCP_MODE=http
      - N8N_API_URL=http://n8n:5678
      - N8N_API_KEY=${N8N_API_KEY}
      - MCP_AUTH_TOKEN=${MCP_AUTH_TOKEN}
      - AUTH_TOKEN=${MCP_AUTH_TOKEN}
      - LOG_LEVEL=${LOG_LEVEL:-info}
    volumes:
      - ./data:/app/data:ro
      - mcp_logs:/app/logs
    networks:
      - n8n-network
    depends_on:
      n8n:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

volumes:
  n8n_data:
    driver: local
  mcp_logs:
    driver: local

networks:
  n8n-network:
    driver: bridge
```

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

```json
[
  {
    "nodeType": "n8n-nodes-base.Slack",
    "success": true,
    "hasPackageInfo": true,
    "hasCredentials": true,
    "sourceSize": 1007,
    "credentialSize": 7553,
    "packageName": "n8n-nodes-base",
    "packageVersion": "1.14.1"
  },
  {
    "nodeType": "n8n-nodes-base.Discord",
    "success": true,
    "hasPackageInfo": true,
    "hasCredentials": false,
    "sourceSize": 10049,
    "credentialSize": 0,
    "packageName": "n8n-nodes-base",
    "packageVersion": "1.14.1"
  },
  {
    "nodeType": "n8n-nodes-base.HttpRequest",
    "success": true,
    "hasPackageInfo": true,
    "hasCredentials": false,
    "sourceSize": 1343,
    "credentialSize": 0,
    "packageName": "n8n-nodes-base",
    "packageVersion": "1.14.1"
  },
  {
    "nodeType": "n8n-nodes-base.Webhook",
    "success": true,
    "hasPackageInfo": true,
    "hasCredentials": false,
    "sourceSize": 10667,
    "credentialSize": 0,
    "packageName": "n8n-nodes-base",
    "packageVersion": "1.14.1"
  },
  {
    "nodeType": "n8n-nodes-base.If",
    "success": true,
    "hasPackageInfo": true,
    "hasCredentials": false,
    "sourceSize": 20533,
    "credentialSize": 0,
    "packageName": "n8n-nodes-base",
    "packageVersion": "1.14.1"
  },
  {
    "nodeType": "n8n-nodes-base.SplitInBatches",
    "success": true,
    "hasPackageInfo": true,
    "hasCredentials": false,
    "sourceSize": 1135,
    "credentialSize": 0,
    "packageName": "n8n-nodes-base",
    "packageVersion": "1.14.1"
  },
  {
    "nodeType": "n8n-nodes-base.Airtable",
    "success": true,
    "hasPackageInfo": true,
    "hasCredentials": true,
    "sourceSize": 936,
    "credentialSize": 5985,
    "packageName": "n8n-nodes-base",
    "packageVersion": "1.14.1"
  },
  {
    "nodeType": "n8n-nodes-base.Function",
    "success": true,
    "hasPackageInfo": true,
    "hasCredentials": false,
    "sourceSize": 7449,
    "credentialSize": 0,
    "packageName": "n8n-nodes-base",
    "packageVersion": "1.14.1"
  }
]
```

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

```typescript
import { beforeEach, afterEach, vi } from 'vitest';
import { loadTestEnvironment, getTestConfig, getTestTimeout } from './test-env';

// CI Debug: Log environment loading in CI only
if (process.env.CI === 'true') {
  console.log('[CI-DEBUG] Global setup starting, NODE_ENV:', process.env.NODE_ENV);
}

// Load test environment configuration
loadTestEnvironment();

if (process.env.CI === 'true') {
  console.log('[CI-DEBUG] Global setup complete, N8N_API_URL:', process.env.N8N_API_URL);
}

// Get test configuration
const testConfig = getTestConfig();

// Reset mocks between tests
beforeEach(() => {
  vi.clearAllMocks();
});

// Clean up after each test
afterEach(() => {
  vi.restoreAllMocks();
  
  // Perform cleanup if enabled
  if (testConfig.cleanup.enabled) {
    // Add cleanup logic here if needed
  }
});

// Global test timeout from configuration
vi.setConfig({ testTimeout: getTestTimeout('global') });

// Configure console output based on test configuration
if (!testConfig.logging.debug) {
  global.console = {
    ...console,
    log: vi.fn(),
    debug: vi.fn(),
    info: vi.fn(),
    warn: testConfig.logging.level === 'error' ? vi.fn() : console.warn,
    error: console.error, // Always show errors
  };
}

// Set up performance monitoring if enabled
if (testConfig.performance) {
  // Use a high-resolution timer that maintains timing precision
  let startTime = process.hrtime.bigint();
  
  global.performance = global.performance || {
    now: () => {
      // Convert nanoseconds to milliseconds with high precision
      const currentTime = process.hrtime.bigint();
      return Number(currentTime - startTime) / 1000000; // Convert nanoseconds to milliseconds
    },
    mark: vi.fn(),
    measure: vi.fn(),
    getEntriesByName: vi.fn(() => []),
    getEntriesByType: vi.fn(() => []),
    clearMarks: vi.fn(),
    clearMeasures: vi.fn(),
  } as any;
}

// Export test configuration for use in tests
export { testConfig, getTestTimeout, getTestConfig };
```

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

```typescript
/**
 * Error Sanitizer for Startup Errors (v2.18.3)
 * Extracts and sanitizes error messages with security-focused patterns
 * Now uses shared sanitization utilities to avoid code duplication
 */

import { logger } from '../utils/logger';
import { sanitizeErrorMessageCore } from './error-sanitization-utils';

/**
 * Extract error message from unknown error type
 * Safely handles Error objects, strings, and other types
 */
export function extractErrorMessage(error: unknown): string {
  try {
    if (error instanceof Error) {
      // Include stack trace if available (will be truncated later)
      return error.stack || error.message || 'Unknown error';
    }

    if (typeof error === 'string') {
      return error;
    }

    if (error && typeof error === 'object') {
      // Try to extract message from object
      const errorObj = error as any;
      if (errorObj.message) {
        return String(errorObj.message);
      }
      if (errorObj.error) {
        return String(errorObj.error);
      }
      // Fall back to JSON stringify with truncation
      try {
        return JSON.stringify(error).substring(0, 500);
      } catch {
        return 'Error object (unstringifiable)';
      }
    }

    return String(error);
  } catch (extractError) {
    logger.debug('Error during message extraction:', extractError);
    return 'Error message extraction failed';
  }
}

/**
 * Sanitize startup error message to remove sensitive data
 * Now uses shared sanitization core from error-sanitization-utils.ts (v2.18.3)
 * This eliminates code duplication and the ReDoS vulnerability
 */
export function sanitizeStartupError(errorMessage: string): string {
  return sanitizeErrorMessageCore(errorMessage);
}

/**
 * Combined operation: Extract and sanitize error message
 * This is the main entry point for startup error processing
 */
export function processStartupError(error: unknown): string {
  const message = extractErrorMessage(error);
  return sanitizeStartupError(message);
}

```

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

```sql
-- Migration: Add template_node_configs table
-- Run during `npm run rebuild` or `npm run fetch:templates`
-- This migration is idempotent - safe to run multiple times

-- Create table if it doesn't exist
CREATE TABLE IF NOT EXISTS template_node_configs (
  id INTEGER PRIMARY KEY,
  node_type TEXT NOT NULL,
  template_id INTEGER NOT NULL,
  template_name TEXT NOT NULL,
  template_views INTEGER DEFAULT 0,

  -- Node configuration (extracted from workflow)
  node_name TEXT,                  -- Node name in workflow (e.g., "HTTP Request")
  parameters_json TEXT NOT NULL,   -- JSON: node.parameters
  credentials_json TEXT,            -- JSON: node.credentials (if present)

  -- Pre-calculated metadata for filtering
  has_credentials INTEGER DEFAULT 0,
  has_expressions INTEGER DEFAULT 0,  -- Contains {{...}} or $json/$node
  complexity TEXT CHECK(complexity IN ('simple', 'medium', 'complex')),
  use_cases TEXT,                   -- JSON array from template.metadata.use_cases

  -- Pre-calculated ranking (1 = best, 2 = second best, etc.)
  rank INTEGER DEFAULT 0,

  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
  FOREIGN KEY (template_id) REFERENCES templates(id) ON DELETE CASCADE
);

-- Create indexes if they don't exist
CREATE INDEX IF NOT EXISTS idx_config_node_type_rank
  ON template_node_configs(node_type, rank);

CREATE INDEX IF NOT EXISTS idx_config_complexity
  ON template_node_configs(node_type, complexity, rank);

CREATE INDEX IF NOT EXISTS idx_config_auth
  ON template_node_configs(node_type, has_credentials, rank);

-- Create view if it doesn't exist
CREATE VIEW IF NOT EXISTS ranked_node_configs AS
SELECT
  node_type,
  template_name,
  template_views,
  parameters_json,
  credentials_json,
  has_credentials,
  has_expressions,
  complexity,
  use_cases,
  rank
FROM template_node_configs
WHERE rank <= 5  -- Top 5 per node type
ORDER BY node_type, rank;

-- Note: Actual data population is handled by the fetch-templates script
-- This migration only creates the schema

```

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

```typescript
import { ToolDocumentation } from '../types';

export const validateNodeMinimalDoc: ToolDocumentation = {
  name: 'validate_node_minimal',
  category: 'validation',
  essentials: {
    description: 'Fast check for missing required fields only. No warnings/suggestions. Returns: list of missing fields.',
    keyParameters: ['nodeType', 'config'],
    example: 'validate_node_minimal("nodes-base.slack", {resource: "message"})',
    performance: 'Instant',
    tips: [
      'Returns only missing required fields',
      'No warnings or suggestions',
      'Perfect for real-time validation'
    ]
  },
  full: {
    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.',
    parameters: {
      nodeType: { type: 'string', required: true, description: 'Node type with prefix (e.g., "nodes-base.slack")' },
      config: { type: 'object', required: true, description: 'Node configuration to validate' }
    },
    returns: 'Array of missing required field names (empty if valid)',
    examples: [
      'validate_node_minimal("nodes-base.slack", {resource: "message", operation: "post"}) - Check Slack config',
      'validate_node_minimal("nodes-base.httpRequest", {method: "GET"}) - Check HTTP config'
    ],
    useCases: [
      'Real-time form validation',
      'Quick configuration checks',
      'Pre-deployment validation',
      'Interactive configuration builders'
    ],
    performance: 'Instant - Simple field checking without complex validation',
    bestPractices: [
      'Use for quick feedback loops',
      'Follow with validate_node_operation for thorough check',
      'Check return array length for validity'
    ],
    pitfalls: [
      'Only checks required fields',
      'No type validation',
      'No operation-specific validation'
    ],
    relatedTools: ['validate_node_operation', 'get_node_essentials', 'get_property_dependencies']
  }
};
```

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

```bash
#!/bin/bash
# Demonstrate the optimization concept

echo "🎯 Demonstrating Docker Optimization"
echo "===================================="

# Create a demo directory structure
DEMO_DIR="optimization-demo"
rm -rf $DEMO_DIR
mkdir -p $DEMO_DIR

# Copy only runtime files
echo -e "\n📦 Creating minimal runtime package..."
cat > $DEMO_DIR/package.json << 'EOF'
{
  "name": "n8n-mcp-optimized",
  "version": "1.0.0",
  "private": true,
  "main": "dist/mcp/index.js",
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.12.1",
    "better-sqlite3": "^11.10.0",
    "sql.js": "^1.13.0",
    "express": "^5.1.0",
    "dotenv": "^16.5.0"
  }
}
EOF

# Copy built files
echo "📁 Copying built application..."
cp -r dist $DEMO_DIR/
cp -r data $DEMO_DIR/
mkdir -p $DEMO_DIR/src/database
cp src/database/schema*.sql $DEMO_DIR/src/database/

# Calculate sizes
echo -e "\n📊 Size comparison:"
echo "Original project: $(du -sh . | cut -f1)"
echo "Optimized runtime: $(du -sh $DEMO_DIR | cut -f1)"

# Show what's included
echo -e "\n✅ Optimized package includes:"
echo "- Pre-built SQLite database with all node info"
echo "- Compiled JavaScript (dist/)"
echo "- Minimal runtime dependencies"
echo "- No n8n packages needed!"

# Create a simple test
echo -e "\n🧪 Testing database content..."
if command -v sqlite3 &> /dev/null; then
    NODE_COUNT=$(sqlite3 data/nodes.db "SELECT COUNT(*) FROM nodes;" 2>/dev/null || echo "0")
    AI_COUNT=$(sqlite3 data/nodes.db "SELECT COUNT(*) FROM nodes WHERE is_ai_tool = 1;" 2>/dev/null || echo "0")
    echo "- Total nodes in database: $NODE_COUNT"
    echo "- AI-capable nodes: $AI_COUNT"
else
    echo "- SQLite CLI not installed, skipping count"
fi

echo -e "\n💡 This demonstrates that we can run n8n-MCP with:"
echo "- ~50MB of runtime dependencies (vs 1.6GB)"
echo "- Pre-built database (11MB)"
echo "- No n8n packages at runtime"
echo "- Total optimized size: ~200MB (vs 2.6GB)"

# Cleanup
echo -e "\n🧹 Cleaning up demo..."
rm -rf $DEMO_DIR

echo -e "\n✨ Optimization concept demonstrated!"
```

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

```bash
#!/bin/bash
# Quick publish script that skips tests
set -e

# Color codes
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'

echo "🚀 Preparing n8n-mcp for npm publish (quick mode)..."

# Sync version
echo "🔄 Syncing version to package.runtime.json..."
npm run sync:runtime-version

VERSION=$(node -e "console.log(require('./package.json').version)")
echo -e "${GREEN}📌 Version: $VERSION${NC}"

# Prepare publish directory
PUBLISH_DIR="npm-publish-temp"
rm -rf $PUBLISH_DIR
mkdir -p $PUBLISH_DIR

echo "📦 Copying files..."
cp -r dist $PUBLISH_DIR/
cp -r data $PUBLISH_DIR/
cp README.md LICENSE .env.example $PUBLISH_DIR/
cp .npmignore $PUBLISH_DIR/ 2>/dev/null || true
cp package.runtime.json $PUBLISH_DIR/package.json

cd $PUBLISH_DIR

# Configure package.json
node -e "
const pkg = require('./package.json');
pkg.name = 'n8n-mcp';
pkg.description = 'Integration between n8n workflow automation and Model Context Protocol (MCP)';
pkg.bin = { 'n8n-mcp': './dist/mcp/index.js' };
pkg.repository = { type: 'git', url: 'git+https://github.com/czlonkowski/n8n-mcp.git' };
pkg.keywords = ['n8n', 'mcp', 'model-context-protocol', 'ai', 'workflow', 'automation'];
pkg.author = 'Romuald Czlonkowski @ www.aiadvisors.pl/en';
pkg.license = 'MIT';
pkg.bugs = { url: 'https://github.com/czlonkowski/n8n-mcp/issues' };
pkg.homepage = 'https://github.com/czlonkowski/n8n-mcp#readme';
pkg.files = ['dist/**/*', 'data/nodes.db', '.env.example', 'README.md', 'LICENSE'];
delete pkg.private;
require('fs').writeFileSync('./package.json', JSON.stringify(pkg, null, 2));
"

echo ""
echo "📋 Package details:"
echo -e "${GREEN}Name:${NC} $(node -e "console.log(require('./package.json').name)")"
echo -e "${GREEN}Version:${NC} $(node -e "console.log(require('./package.json').version)")"
echo -e "${GREEN}Size:${NC} ~50MB"
echo ""
echo "✅ Ready to publish!"
echo ""
echo -e "${YELLOW}⚠️  Note: Tests were skipped in quick mode${NC}"
echo ""
echo "To publish, run:"
echo -e "  ${GREEN}cd $PUBLISH_DIR${NC}"
echo -e "  ${GREEN}npm publish --otp=YOUR_OTP_CODE${NC}"
```

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

```typescript
/**
 * Console Manager for MCP HTTP Server
 * 
 * Prevents console output from interfering with StreamableHTTPServerTransport
 * by silencing console methods during MCP request handling.
 */
export class ConsoleManager {
  private originalConsole = {
    log: console.log,
    error: console.error,
    warn: console.warn,
    info: console.info,
    debug: console.debug,
    trace: console.trace
  };
  
  private isSilenced = false;
  
  /**
   * Silence all console output
   */
  public silence(): void {
    if (this.isSilenced || process.env.MCP_MODE !== 'http') {
      return;
    }
    
    this.isSilenced = true;
    process.env.MCP_REQUEST_ACTIVE = 'true';
    console.log = () => {};
    console.error = () => {};
    console.warn = () => {};
    console.info = () => {};
    console.debug = () => {};
    console.trace = () => {};
  }
  
  /**
   * Restore original console methods
   */
  public restore(): void {
    if (!this.isSilenced) {
      return;
    }
    
    this.isSilenced = false;
    process.env.MCP_REQUEST_ACTIVE = 'false';
    console.log = this.originalConsole.log;
    console.error = this.originalConsole.error;
    console.warn = this.originalConsole.warn;
    console.info = this.originalConsole.info;
    console.debug = this.originalConsole.debug;
    console.trace = this.originalConsole.trace;
  }
  
  /**
   * Wrap an operation with console silencing
   * Automatically restores console on completion or error
   */
  public async wrapOperation<T>(operation: () => T | Promise<T>): Promise<T> {
    this.silence();
    try {
      const result = operation();
      if (result instanceof Promise) {
        return await result.finally(() => this.restore());
      }
      this.restore();
      return result;
    } catch (error) {
      this.restore();
      throw error;
    }
  }
  
  /**
   * Check if console is currently silenced
   */
  public get isActive(): boolean {
    return this.isSilenced;
  }
}

// Export singleton instance for easy use
export const consoleManager = new ConsoleManager();
```

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

```typescript
/**
 * TypeScript interfaces for MCP handler responses
 *
 * These interfaces provide type safety for integration tests,
 * replacing unsafe `as any` casts with proper type definitions.
 */

/**
 * Workflow validation response from handleValidateWorkflow
 */
export interface ValidationResponse {
  valid: boolean;
  workflowId: string;
  workflowName: string;
  summary: {
    totalNodes: number;
    enabledNodes: number;
    triggerNodes: number;
    validConnections?: number;
    invalidConnections?: number;
    expressionsValidated?: number;
    errorCount: number;
    warningCount: number;
  };
  errors?: Array<{
    node: string;
    nodeName?: string;
    message: string;
    details?: {
      code?: string;
      [key: string]: unknown;
    };
    code?: string;
  }>;
  warnings?: Array<{
    node: string;
    nodeName?: string;
    message: string;
    details?: {
      code?: string;
      [key: string]: unknown;
    };
    code?: string;
  }>;
  info?: Array<{
    node: string;
    nodeName?: string;
    message: string;
    severity?: string;
    details?: unknown;
  }>;
  suggestions?: string[];
}

/**
 * Workflow autofix response from handleAutofixWorkflow
 */
export interface AutofixResponse {
  workflowId: string;
  workflowName: string;
  preview?: boolean;
  fixesAvailable?: number;
  fixesApplied?: number;
  fixes?: Array<{
    type: 'expression-format' | 'typeversion-correction' | 'error-output-config' | 'node-type-correction' | 'webhook-missing-path';
    confidence: 'high' | 'medium' | 'low';
    description: string;
    nodeName?: string;
    nodeId?: string;
    before?: unknown;
    after?: unknown;
  }>;
  summary?: {
    totalFixes: number;
    byType: Record<string, number>;
    byConfidence: Record<string, number>;
  };
  stats?: {
    expressionFormat?: number;
    typeVersionCorrection?: number;
    errorOutputConfig?: number;
    nodeTypeCorrection?: number;
    webhookMissingPath?: number;
  };
  message?: string;
  validationSummary?: {
    errors: number;
    warnings: number;
  };
}

```

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

```typescript
import { ToolDocumentation } from '../types';

export const n8nDeleteWorkflowDoc: ToolDocumentation = {
  name: 'n8n_delete_workflow',
  category: 'workflow_management',
  essentials: {
    description: 'Permanently delete a workflow. This action cannot be undone.',
    keyParameters: ['id'],
    example: 'n8n_delete_workflow({id: "workflow_123"})',
    performance: 'Fast (50-150ms)',
    tips: [
      'Action is irreversible',
      'Deletes all execution history',
      'Check workflow first with get_minimal'
    ]
  },
  full: {
    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.',
    parameters: {
      id: { type: 'string', required: true, description: 'Workflow ID to delete permanently' }
    },
    returns: 'Success confirmation or error if workflow not found/cannot be deleted',
    examples: [
      'n8n_delete_workflow({id: "abc123"}) - Delete specific workflow',
      'if (confirm) { n8n_delete_workflow({id: wf.id}); } // With confirmation'
    ],
    useCases: [
      'Remove obsolete workflows',
      'Clean up test workflows',
      'Delete failed experiments',
      'Manage workflow limits',
      'Remove duplicates'
    ],
    performance: 'Fast operation - typically 50-150ms. May take longer if workflow has extensive execution history.',
    bestPractices: [
      'Always confirm before deletion',
      'Check workflow with get_minimal first',
      'Consider deactivating instead of deleting',
      'Export workflow before deletion for backup'
    ],
    pitfalls: [
      'Requires N8N_API_URL and N8N_API_KEY configured',
      'Cannot be undone - permanent deletion',
      'Deletes all execution history',
      'Active workflows can be deleted',
      'No built-in confirmation'
    ],
    relatedTools: ['n8n_get_workflow_minimal', 'n8n_list_workflows', 'n8n_update_partial_workflow', 'n8n_delete_execution']
  }
};
```

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

```sql
-- Optimized schema with source code storage for Docker optimization
CREATE TABLE IF NOT EXISTS nodes (
  node_type TEXT PRIMARY KEY,
  package_name TEXT NOT NULL,
  display_name TEXT NOT NULL,
  description TEXT,
  category TEXT,
  development_style TEXT CHECK(development_style IN ('declarative', 'programmatic')),
  is_ai_tool INTEGER DEFAULT 0,
  is_trigger INTEGER DEFAULT 0,
  is_webhook INTEGER DEFAULT 0,
  is_versioned INTEGER DEFAULT 0,
  version TEXT,
  documentation TEXT,
  properties_schema TEXT,
  operations TEXT,
  credentials_required TEXT,
  -- New columns for source code storage
  node_source_code TEXT,
  credential_source_code TEXT,
  source_location TEXT,
  source_extracted_at DATETIME,
  -- Metadata
  updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

-- Indexes for performance
CREATE INDEX IF NOT EXISTS idx_package ON nodes(package_name);
CREATE INDEX IF NOT EXISTS idx_ai_tool ON nodes(is_ai_tool);
CREATE INDEX IF NOT EXISTS idx_category ON nodes(category);

-- FTS5 table for full-text search including source code
CREATE VIRTUAL TABLE IF NOT EXISTS nodes_fts USING fts5(
  node_type,
  display_name,
  description,
  documentation,
  operations,
  node_source_code,
  content=nodes,
  content_rowid=rowid
);

-- Trigger to keep FTS in sync
CREATE TRIGGER IF NOT EXISTS nodes_fts_insert AFTER INSERT ON nodes
BEGIN
  INSERT INTO nodes_fts(rowid, node_type, display_name, description, documentation, operations, node_source_code)
  VALUES (new.rowid, new.node_type, new.display_name, new.description, new.documentation, new.operations, new.node_source_code);
END;

CREATE TRIGGER IF NOT EXISTS nodes_fts_update AFTER UPDATE ON nodes
BEGIN
  UPDATE nodes_fts 
  SET node_type = new.node_type,
      display_name = new.display_name,
      description = new.description,
      documentation = new.documentation,
      operations = new.operations,
      node_source_code = new.node_source_code
  WHERE rowid = new.rowid;
END;

CREATE TRIGGER IF NOT EXISTS nodes_fts_delete AFTER DELETE ON nodes
BEGIN
  DELETE FROM nodes_fts WHERE rowid = old.rowid;
END;
```

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

```javascript
#!/usr/bin/env node

const { NodeDocumentationService } = require('../dist/services/node-documentation-service');

async function testSmallRebuild() {
  console.log('Testing small rebuild...\n');
  
  const service = new NodeDocumentationService('./data/nodes-v2-test.db');
  
  try {
    // First, let's just try the IF node specifically
    const extractor = service.extractor;
    console.log('1️⃣ Testing extraction of IF node...');
    
    try {
      const ifNodeData = await extractor.extractNodeSource('n8n-nodes-base.If');
      console.log('   ✅ Successfully extracted IF node');
      console.log('   Source code length:', ifNodeData.sourceCode.length);
      console.log('   Has credentials:', !!ifNodeData.credentialCode);
    } catch (error) {
      console.log('   ❌ Failed to extract IF node:', error.message);
    }
    
    // Try the Webhook node
    console.log('\n2️⃣ Testing extraction of Webhook node...');
    try {
      const webhookNodeData = await extractor.extractNodeSource('n8n-nodes-base.Webhook');
      console.log('   ✅ Successfully extracted Webhook node');
      console.log('   Source code length:', webhookNodeData.sourceCode.length);
    } catch (error) {
      console.log('   ❌ Failed to extract Webhook node:', error.message);
    }
    
    // Now try storing just these nodes
    console.log('\n3️⃣ Testing storage of a single node...');
    const nodeInfo = {
      nodeType: 'n8n-nodes-base.If',
      name: 'If',
      displayName: 'If',
      description: 'Route items based on comparison operations',
      sourceCode: 'test source code',
      packageName: 'n8n-nodes-base',
      hasCredentials: false,
      isTrigger: false,
      isWebhook: false
    };
    
    await service.storeNode(nodeInfo);
    console.log('   ✅ Successfully stored test node');
    
    // Check if it was stored
    const retrievedNode = await service.getNodeInfo('n8n-nodes-base.If');
    console.log('   Retrieved node:', retrievedNode ? 'Found' : 'Not found');
    
  } catch (error) {
    console.error('❌ Test failed:', error);
  } finally {
    service.close();
  }
}

testSmallRebuild();
```

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

```typescript
/**
 * Example workflows for n8n AI agents to understand the structure
 */

export const MINIMAL_WORKFLOW_EXAMPLE = {
  nodes: [
    {
      name: "Webhook",
      type: "n8n-nodes-base.webhook",
      typeVersion: 2,
      position: [250, 300],
      parameters: {
        httpMethod: "POST",
        path: "webhook"
      }
    }
  ],
  connections: {}
};

export const SIMPLE_WORKFLOW_EXAMPLE = {
  nodes: [
    {
      name: "Webhook",
      type: "n8n-nodes-base.webhook",
      typeVersion: 2,
      position: [250, 300],
      parameters: {
        httpMethod: "POST",
        path: "webhook"
      }
    },
    {
      name: "Set",
      type: "n8n-nodes-base.set",
      typeVersion: 2,
      position: [450, 300],
      parameters: {
        mode: "manual",
        assignments: {
          assignments: [
            {
              name: "message",
              type: "string",
              value: "Hello"
            }
          ]
        }
      }
    },
    {
      name: "Respond to Webhook",
      type: "n8n-nodes-base.respondToWebhook",
      typeVersion: 1,
      position: [650, 300],
      parameters: {
        respondWith: "firstIncomingItem"
      }
    }
  ],
  connections: {
    "Webhook": {
      "main": [
        [
          {
            "node": "Set",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set": {
      "main": [
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
};

export function getWorkflowExampleString(): string {
  return `Example workflow structure:
${JSON.stringify(MINIMAL_WORKFLOW_EXAMPLE, null, 2)}

Each node MUST have:
- name: unique string identifier
- type: full node type with prefix (e.g., "n8n-nodes-base.webhook")
- typeVersion: number (usually 1 or 2)
- position: [x, y] coordinates array
- parameters: object with node-specific settings

Connections format:
{
  "SourceNodeName": {
    "main": [
      [
        {
          "node": "TargetNodeName",
          "type": "main",
          "index": 0
        }
      ]
    ]
  }
}`;
}
```

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

```typescript
import { ToolDocumentation } from '../types';

export const n8nGetWorkflowMinimalDoc: ToolDocumentation = {
  name: 'n8n_get_workflow_minimal',
  category: 'workflow_management',
  essentials: {
    description: 'Get minimal info: ID, name, active status, tags. Fast for listings.',
    keyParameters: ['id'],
    example: 'n8n_get_workflow_minimal({id: "workflow_123"})',
    performance: 'Very fast (<50ms)',
    tips: [
      'Fastest way to check workflow exists',
      'Perfect for status checks',
      'Use in list displays'
    ]
  },
  full: {
    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.',
    parameters: {
      id: { type: 'string', required: true, description: 'Workflow ID to retrieve minimal info for' }
    },
    returns: 'Minimal workflow object with: id, name, active status, tags array, createdAt, updatedAt. No nodes, connections, or settings included.',
    examples: [
      'n8n_get_workflow_minimal({id: "abc123"}) - Quick existence check',
      'const info = n8n_get_workflow_minimal({id: "xyz789"}); // Check if active'
    ],
    useCases: [
      'Quick workflow existence checks',
      'Display workflow lists',
      'Check active/inactive status',
      'Get workflow tags',
      'Performance-critical operations'
    ],
    performance: 'Extremely fast - typically under 50ms. Returns only database metadata without loading workflow definition.',
    bestPractices: [
      'Use for list displays and dashboards',
      'Ideal for existence checks before operations',
      'Cache results for UI responsiveness',
      'Combine with list_workflows for bulk checks'
    ],
    pitfalls: [
      'Requires N8N_API_URL and N8N_API_KEY configured',
      'No workflow content - cannot edit or validate',
      'Tags may be empty array',
      'Must use get_workflow for actual workflow data'
    ],
    relatedTools: ['n8n_list_workflows', 'n8n_get_workflow', 'n8n_get_workflow_structure', 'n8n_update_partial_workflow']
  }
};
```

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

```bash
#!/bin/bash
# Test script to verify Docker optimization (no n8n deps)

set -e

echo "🧪 Testing Docker optimization..."
echo ""

# Check if nodes.db exists
if [ ! -f "data/nodes.db" ]; then
    echo "❌ ERROR: data/nodes.db not found!"
    echo "   Run 'npm run rebuild' first to create the database"
    exit 1
fi

# Build the image
echo "📦 Building Docker image..."
DOCKER_BUILDKIT=1 docker build -t n8n-mcp:test . > /dev/null 2>&1

# Check image size
echo "📊 Checking image size..."
SIZE=$(docker images n8n-mcp:test --format "{{.Size}}")
echo "   Image size: $SIZE"

# Test that n8n is NOT in the image
echo ""
echo "🔍 Verifying no n8n dependencies..."
if docker run --rm n8n-mcp:test sh -c "ls node_modules | grep -E '^n8n$|^n8n-|^@n8n'" 2>/dev/null; then
    echo "❌ ERROR: Found n8n dependencies in runtime image!"
    exit 1
else
    echo "✅ No n8n dependencies found (as expected)"
fi

# Test that runtime dependencies ARE present
echo ""
echo "🔍 Verifying runtime dependencies..."
EXPECTED_DEPS=("@modelcontextprotocol" "better-sqlite3" "express" "dotenv")
for dep in "${EXPECTED_DEPS[@]}"; do
    if docker run --rm n8n-mcp:test sh -c "ls node_modules | grep -q '$dep'" 2>/dev/null; then
        echo "✅ Found: $dep"
    else
        echo "❌ Missing: $dep"
        exit 1
    fi
done

# Test that the server starts
echo ""
echo "🚀 Testing server startup..."
docker run --rm -d \
    --name n8n-mcp-test \
    -e MCP_MODE=http \
    -e AUTH_TOKEN=test-token \
    -e LOG_LEVEL=error \
    n8n-mcp:test > /dev/null 2>&1

# Wait for startup
sleep 3

# Check if running
if docker ps | grep -q n8n-mcp-test; then
    echo "✅ Server started successfully"
    docker stop n8n-mcp-test > /dev/null 2>&1
else
    echo "❌ Server failed to start"
    docker logs n8n-mcp-test 2>&1
    exit 1
fi

# Clean up
docker rmi n8n-mcp:test > /dev/null 2>&1

echo ""
echo "🎉 All tests passed! Docker optimization is working correctly."
echo ""
echo "📈 Benefits:"
echo "   - No n8n dependencies in runtime image"
echo "   - Image size: ~200MB (vs ~1.5GB with n8n)"
echo "   - Build time: ~1-2 minutes (vs ~12 minutes)"
echo "   - No version conflicts at runtime"
```

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

```markdown
# Performance Benchmark Thresholds

This file defines the expected performance thresholds for n8n-mcp operations.

## Critical Operations

| Operation | Expected Time | Warning Threshold | Error Threshold |
|-----------|---------------|-------------------|-----------------|
| Node Loading (per package) | <100ms | 150ms | 200ms |
| Database Query (simple) | <5ms | 10ms | 20ms |
| Search (simple word) | <10ms | 20ms | 50ms |
| Search (complex query) | <50ms | 100ms | 200ms |
| Validation (simple config) | <1ms | 2ms | 5ms |
| Validation (complex config) | <10ms | 20ms | 50ms |
| MCP Tool Execution | <50ms | 100ms | 200ms |

## Benchmark Categories

### Node Loading Performance
- **loadPackage**: Should handle large packages efficiently
- **loadNodesFromPath**: Individual file loading should be fast
- **parsePackageJson**: JSON parsing overhead should be minimal

### Database Query Performance
- **getNodeByType**: Direct lookups should be instant
- **searchNodes**: Full-text search should scale well
- **getAllNodes**: Pagination should prevent performance issues

### Search Operations
- **OR mode**: Should handle multiple terms efficiently
- **AND mode**: More restrictive but still performant
- **FUZZY mode**: Slower but acceptable for typo tolerance

### Validation Performance
- **minimal profile**: Fastest, only required fields
- **ai-friendly profile**: Balanced performance
- **strict profile**: Comprehensive but slower

### MCP Tool Execution
- Tools should respond quickly for interactive use
- Complex operations may take longer but should remain responsive

## Regression Detection

Performance regressions are detected when:
1. Any operation exceeds its warning threshold by 10%
2. Multiple operations show degradation in the same category
3. Average performance across all benchmarks degrades by 5%

## Optimization Targets

Future optimization efforts should focus on:
1. **Search performance**: Implement FTS5 for better full-text search
2. **Caching**: Add intelligent caching for frequently accessed nodes
3. **Lazy loading**: Defer loading of large property schemas
4. **Batch operations**: Optimize bulk inserts and updates
```

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

```typescript
#!/usr/bin/env node

import { N8NDocumentationMCPServer } from '../src/mcp/server';

async function testFuzzyFix() {
  console.log('Testing FUZZY mode fix...\n');
  
  const server = new N8NDocumentationMCPServer();
  
  // Wait for initialization
  await new Promise(resolve => setTimeout(resolve, 1000));
  
  // Test 1: FUZZY mode with typo
  console.log('Test 1: FUZZY mode with "slak" (typo for "slack")');
  const fuzzyResult = await server.executeTool('search_nodes', {
    query: 'slak',
    mode: 'FUZZY',
    limit: 5
  });
  
  console.log(`Results: ${fuzzyResult.results.length} found`);
  if (fuzzyResult.results.length > 0) {
    console.log('✅ FUZZY mode now finds results!');
    fuzzyResult.results.forEach((node: any, i: number) => {
      console.log(`  ${i + 1}. ${node.nodeType} - ${node.displayName}`);
    });
  } else {
    console.log('❌ FUZZY mode still not working');
  }
  
  // Test 2: AND mode with explanation
  console.log('\n\nTest 2: AND mode with "send message"');
  const andResult = await server.executeTool('search_nodes', {
    query: 'send message',
    mode: 'AND',
    limit: 5
  });
  
  console.log(`Results: ${andResult.results.length} found`);
  if (andResult.searchInfo) {
    console.log('✅ AND mode now includes search info:');
    console.log(`   ${andResult.searchInfo.message}`);
    console.log(`   Tip: ${andResult.searchInfo.tip}`);
  }
  
  console.log('\nFirst 5 results:');
  andResult.results.slice(0, 5).forEach((node: any, i: number) => {
    console.log(`  ${i + 1}. ${node.nodeType} - ${node.displayName}`);
  });
  
  // Test 3: More typos
  console.log('\n\nTest 3: More FUZZY tests');
  const typos = ['htpp', 'webook', 'slck', 'emial'];
  
  for (const typo of typos) {
    const result = await server.executeTool('search_nodes', {
      query: typo,
      mode: 'FUZZY',
      limit: 1
    });
    
    if (result.results.length > 0) {
      console.log(`✅ "${typo}" → ${result.results[0].displayName}`);
    } else {
      console.log(`❌ "${typo}" → No results`);
    }
  }
  
  process.exit(0);
}

// Run tests
testFuzzyFix().catch(error => {
  console.error('Test failed:', error);
  process.exit(1);
});
```

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

```typescript
import { ToolDocumentation } from '../types';

export const n8nGetWorkflowStructureDoc: ToolDocumentation = {
  name: 'n8n_get_workflow_structure',
  category: 'workflow_management',
  essentials: {
    description: 'Get workflow structure: nodes and connections only. No parameter details.',
    keyParameters: ['id'],
    example: 'n8n_get_workflow_structure({id: "workflow_123"})',
    performance: 'Fast (75-150ms)',
    tips: [
      'Shows workflow topology',
      'Node types without parameters',
      'Perfect for visualization'
    ]
  },
  full: {
    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.',
    parameters: {
      id: { type: 'string', required: true, description: 'Workflow ID to retrieve structure for' }
    },
    returns: 'Workflow structure with: id, name, nodes array (id, name, type, position only), connections object. No node parameters, credentials, or settings included.',
    examples: [
      'n8n_get_workflow_structure({id: "abc123"}) - Visualize workflow',
      'const structure = n8n_get_workflow_structure({id: "xyz789"}); // Analyze complexity'
    ],
    useCases: [
      'Generate workflow visualizations',
      'Analyze workflow complexity',
      'Understand node relationships',
      'Create workflow diagrams',
      'Quick topology validation'
    ],
    performance: 'Fast retrieval - typically 75-150ms. Faster than get_workflow as parameters are stripped.',
    bestPractices: [
      'Use for visualization tools',
      'Ideal for workflow analysis',
      'Good for connection validation',
      'Cache for UI diagram rendering'
    ],
    pitfalls: [
      'Requires N8N_API_URL and N8N_API_KEY configured',
      'No parameter data for configuration',
      'Cannot validate node settings',
      'Must use get_workflow for editing'
    ],
    relatedTools: ['n8n_get_workflow', 'n8n_validate_workflow_connections', 'n8n_get_workflow_minimal', 'validate_workflow_connections']
  }
};
```

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

```typescript
import { ToolDocumentation } from '../types';

export const n8nGetWorkflowDoc: ToolDocumentation = {
  name: 'n8n_get_workflow',
  category: 'workflow_management',
  essentials: {
    description: 'Get a workflow by ID. Returns the complete workflow including nodes, connections, and settings.',
    keyParameters: ['id'],
    example: 'n8n_get_workflow({id: "workflow_123"})',
    performance: 'Fast (50-200ms)',
    tips: [
      'Returns complete workflow JSON',
      'Includes all node parameters',
      'Use get_workflow_minimal for faster listings'
    ]
  },
  full: {
    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.',
    parameters: {
      id: { type: 'string', required: true, description: 'Workflow ID to retrieve' }
    },
    returns: 'Complete workflow object containing: id, name, active status, nodes array (with full parameters), connections object, settings, createdAt, updatedAt',
    examples: [
      'n8n_get_workflow({id: "abc123"}) - Get workflow for editing',
      'const wf = n8n_get_workflow({id: "xyz789"}); // Clone workflow structure'
    ],
    useCases: [
      'View workflow configuration',
      'Export workflow for backup',
      'Clone workflow structure',
      'Debug workflow issues',
      'Prepare for updates'
    ],
    performance: 'Fast retrieval - typically 50-200ms depending on workflow size. Cached by n8n for performance.',
    bestPractices: [
      'Check workflow exists before updating',
      'Use for complete workflow data needs',
      'Cache results when making multiple operations',
      'Validate after retrieving if modifying'
    ],
    pitfalls: [
      'Requires N8N_API_URL and N8N_API_KEY configured',
      'Returns all data - use minimal/structure for performance',
      'Workflow must exist or returns 404',
      'Credentials are referenced but not included'
    ],
    relatedTools: ['n8n_get_workflow_minimal', 'n8n_get_workflow_structure', 'n8n_update_full_workflow', 'n8n_validate_workflow']
  }
};
```

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

```typescript
import { vi } from 'vitest';

export class MockDatabase {
  private data = new Map<string, any[]>();
  private prepared = new Map<string, any>();
  public inTransaction = false;
  
  constructor() {
    this.data.set('nodes', []);
    this.data.set('templates', []);
    this.data.set('tools_documentation', []);
  }
  
  prepare(sql: string) {
    const key = this.extractTableName(sql);
    const self = this;
    
    return {
      all: vi.fn(() => self.data.get(key) || []),
      get: vi.fn((id: string) => {
        const items = self.data.get(key) || [];
        return items.find(item => item.id === id);
      }),
      run: vi.fn((params: any) => {
        const items = self.data.get(key) || [];
        items.push(params);
        self.data.set(key, items);
        return { changes: 1, lastInsertRowid: items.length };
      }),
      iterate: vi.fn(function* () {
        const items = self.data.get(key) || [];
        for (const item of items) {
          yield item;
        }
      }),
      pluck: vi.fn(function(this: any) { return this; }),
      expand: vi.fn(function(this: any) { return this; }),
      raw: vi.fn(function(this: any) { return this; }),
      columns: vi.fn(() => []),
      bind: vi.fn(function(this: any) { return this; })
    };
  }
  
  exec(sql: string) {
    // Mock schema creation
    return true;
  }
  
  close() {
    // Mock close
    return true;
  }
  
  pragma(key: string, value?: any) {
    // Mock pragma
    if (key === 'journal_mode' && value === 'WAL') {
      return 'wal';
    }
    return null;
  }
  
  transaction<T>(fn: () => T): T {
    this.inTransaction = true;
    try {
      const result = fn();
      this.inTransaction = false;
      return result;
    } catch (error) {
      this.inTransaction = false;
      throw error;
    }
  }
  
  // Helper to extract table name from SQL
  private extractTableName(sql: string): string {
    const match = sql.match(/FROM\s+(\w+)|INTO\s+(\w+)|UPDATE\s+(\w+)/i);
    return match ? (match[1] || match[2] || match[3]) : 'nodes';
  }
  
  // Test helper to seed data
  _seedData(table: string, data: any[]) {
    this.data.set(table, data);
  }
}

export default vi.fn(() => new MockDatabase());
```

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

```typescript
import { ToolDocumentation } from '../types';

export const n8nGetWorkflowDetailsDoc: ToolDocumentation = {
  name: 'n8n_get_workflow_details',
  category: 'workflow_management',
  essentials: {
    description: 'Get workflow details with metadata, version, execution stats. More info than get_workflow.',
    keyParameters: ['id'],
    example: 'n8n_get_workflow_details({id: "workflow_123"})',
    performance: 'Fast (100-300ms)',
    tips: [
      'Includes execution statistics',
      'Shows version history info',
      'Contains metadata like tags'
    ]
  },
  full: {
    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.',
    parameters: {
      id: { type: 'string', required: true, description: 'Workflow ID to retrieve details for' }
    },
    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',
    examples: [
      'n8n_get_workflow_details({id: "abc123"}) - Get workflow with stats',
      'const details = n8n_get_workflow_details({id: "xyz789"}); // Analyze performance'
    ],
    useCases: [
      'Monitor workflow performance',
      'Analyze execution patterns',
      'View workflow metadata',
      'Check version information',
      'Audit workflow usage'
    ],
    performance: 'Slightly slower than get_workflow due to additional metadata - typically 100-300ms. Stats may be cached.',
    bestPractices: [
      'Use for monitoring and analysis',
      'Check execution stats before optimization',
      'Review error counts for debugging',
      'Monitor average execution times'
    ],
    pitfalls: [
      'Requires N8N_API_URL and N8N_API_KEY configured',
      'More data than needed for simple edits',
      'Stats may have slight delay',
      'Not all n8n versions support all fields'
    ],
    relatedTools: ['n8n_get_workflow', 'n8n_list_executions', 'n8n_get_execution', 'n8n_list_workflows']
  }
};
```

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

```typescript
import { defineConfig } from 'vitest/config';
import path from 'path';

export default defineConfig({
  test: {
    globals: true,
    environment: 'node',
    // Only include global-setup.ts, remove msw-setup.ts from global setup
    setupFiles: ['./tests/setup/global-setup.ts'],
    // Load environment variables from .env.test
    env: {
      NODE_ENV: 'test'
    },
    // Test execution settings
    pool: 'threads',
    poolOptions: {
      threads: {
        singleThread: process.env.TEST_PARALLEL !== 'true',
        maxThreads: parseInt(process.env.TEST_MAX_WORKERS || '4', 10),
        minThreads: 1
      }
    },
    // Retry configuration
    retry: parseInt(process.env.TEST_RETRY_ATTEMPTS || '2', 10),
    // Test reporter - reduce reporters in CI to prevent hanging
    reporters: process.env.CI ? ['default', 'junit'] : ['default'],
    outputFile: {
      junit: './test-results/junit.xml'
    },
    coverage: {
      provider: 'v8',
      enabled: process.env.FEATURE_TEST_COVERAGE !== 'false',
      reporter: process.env.CI ? ['lcov', 'text-summary'] : (process.env.COVERAGE_REPORTER || 'lcov,html,text-summary').split(','),
      reportsDirectory: process.env.COVERAGE_DIR || './coverage',
      exclude: [
        'node_modules/',
        'tests/',
        '**/*.d.ts',
        '**/*.test.ts',
        '**/*.spec.ts',
        'scripts/',
        'dist/',
        '**/test-*.ts',
        '**/mock-*.ts',
        '**/__mocks__/**'
      ],
      thresholds: {
        lines: 80,
        functions: 80,
        branches: 75,
        statements: 80
      },
      // Add coverage-specific settings to prevent hanging
      all: false, // Don't collect coverage for untested files
      skipFull: true // Skip files with 100% coverage
    },
    // Test isolation
    isolate: true,
    // Force exit after tests complete in CI to prevent hanging
    forceRerunTriggers: ['**/tests/**/*.ts'],
    teardownTimeout: 1000
  },
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src'),
      '@tests': path.resolve(__dirname, './tests')
    }
  },
  // TypeScript configuration
  esbuild: {
    target: 'node18'
  },
  // Define global constants
  define: {
    'process.env.TEST_ENVIRONMENT': JSON.stringify('true')
  }
});
```

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

```markdown
# Privacy Policy for n8n-mcp Telemetry

## Overview
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.

## What We Collect
- **Anonymous User ID**: A hashed identifier derived from your machine characteristics (no personal information)
- **Tool Usage**: Which MCP tools are used and their performance metrics
- **Workflow Patterns**: Sanitized workflow structures (all sensitive data removed)
- **Error Types**: Categories of errors encountered (no error messages with user data)
- **System Information**: Platform, architecture, Node.js version, and n8n-mcp version

## What We DON'T Collect
- Personal information or usernames
- API keys, tokens, or credentials
- URLs, endpoints, or hostnames
- Email addresses or contact information
- File paths or directory structures
- Actual workflow data or parameters
- Database connection strings
- Any authentication information

## Data Sanitization
All collected data undergoes automatic sanitization:
- URLs are replaced with `[URL]` or `[REDACTED]`
- Long alphanumeric strings (potential keys) are replaced with `[KEY]`
- Email addresses are replaced with `[EMAIL]`
- Authentication-related fields are completely removed

## Data Storage
- Data is stored securely using Supabase
- Anonymous users have write-only access (cannot read data back)
- Row Level Security (RLS) policies prevent data access by anonymous users

## Opt-Out
You can disable telemetry at any time:
```bash
npx n8n-mcp telemetry disable
```

To re-enable:
```bash
npx n8n-mcp telemetry enable
```

To check status:
```bash
npx n8n-mcp telemetry status
```

## Data Usage
Collected data is used solely to:
- Understand which features are most used
- Identify common error patterns
- Improve tool performance and reliability
- Guide development priorities

## Data Retention
- Data is retained for analysis purposes
- No personal identification is possible from the collected data

## Changes to This Policy
We may update this privacy policy from time to time. Updates will be reflected in this document.

## Contact
For questions about telemetry or privacy, please open an issue on GitHub:
https://github.com/czlonkowski/n8n-mcp/issues

Last updated: 2025-09-25
```

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

```javascript
#!/usr/bin/env node

/**
 * Extract changelog content for a specific version
 * Used by GitHub Actions to extract release notes
 */

const fs = require('fs');
const path = require('path');

function extractChangelog(version, changelogPath) {
  try {
    if (!fs.existsSync(changelogPath)) {
      console.error(`Changelog file not found at ${changelogPath}`);
      process.exit(1);
    }

    const content = fs.readFileSync(changelogPath, 'utf8');
    const lines = content.split('\n');
    
    // Find the start of this version's section
    const versionHeaderRegex = new RegExp(`^## \\[${version.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\]`);
    let startIndex = -1;
    let endIndex = -1;
    
    for (let i = 0; i < lines.length; i++) {
      if (versionHeaderRegex.test(lines[i])) {
        startIndex = i;
        break;
      }
    }
    
    if (startIndex === -1) {
      console.error(`No changelog entries found for version ${version}`);
      process.exit(1);
    }
    
    // Find the end of this version's section (next version or end of file)
    for (let i = startIndex + 1; i < lines.length; i++) {
      if (lines[i].startsWith('## [') && !lines[i].includes('Unreleased')) {
        endIndex = i;
        break;
      }
    }
    
    if (endIndex === -1) {
      endIndex = lines.length;
    }
    
    // Extract the section content
    const sectionLines = lines.slice(startIndex, endIndex);
    
    // Remove the version header and any trailing empty lines
    let contentLines = sectionLines.slice(1);
    while (contentLines.length > 0 && contentLines[contentLines.length - 1].trim() === '') {
      contentLines.pop();
    }
    
    if (contentLines.length === 0) {
      console.error(`No content found for version ${version}`);
      process.exit(1);
    }
    
    const releaseNotes = contentLines.join('\n').trim();
    
    // Write to stdout for GitHub Actions
    console.log(releaseNotes);
    
  } catch (error) {
    console.error(`Error extracting changelog: ${error.message}`);
    process.exit(1);
  }
}

// Parse command line arguments
const version = process.argv[2];
const changelogPath = process.argv[3];

if (!version || !changelogPath) {
  console.error('Usage: extract-changelog.js <version> <changelog-path>');
  process.exit(1);
}

extractChangelog(version, changelogPath);
```

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

```typescript
#!/usr/bin/env npx tsx

import { EnhancedConfigValidator } from '../src/services/enhanced-config-validator.js';

console.log('🧪 Testing $helpers Validation\n');

const testCases = [
  {
    name: 'Incorrect $helpers.getWorkflowStaticData',
    config: {
      language: 'javaScript',
      jsCode: `const data = $helpers.getWorkflowStaticData('global');
data.counter = 1;
return [{json: {counter: data.counter}}];`
    }
  },
  {
    name: 'Correct $getWorkflowStaticData',
    config: {
      language: 'javaScript',
      jsCode: `const data = $getWorkflowStaticData('global');
data.counter = 1;
return [{json: {counter: data.counter}}];`
    }
  },
  {
    name: '$helpers without check',
    config: {
      language: 'javaScript',
      jsCode: `const response = await $helpers.httpRequest({
  method: 'GET',
  url: 'https://api.example.com'
});
return [{json: response}];`
    }
  },
  {
    name: '$helpers with proper check',
    config: {
      language: 'javaScript',
      jsCode: `if (typeof $helpers !== 'undefined' && $helpers.httpRequest) {
  const response = await $helpers.httpRequest({
    method: 'GET',
    url: 'https://api.example.com'
  });
  return [{json: response}];
}
return [{json: {error: 'HTTP not available'}}];`
    }
  },
  {
    name: 'Crypto without require',
    config: {
      language: 'javaScript',
      jsCode: `const token = crypto.randomBytes(32).toString('hex');
return [{json: {token}}];`
    }
  },
  {
    name: 'Crypto with require',
    config: {
      language: 'javaScript',
      jsCode: `const crypto = require('crypto');
const token = crypto.randomBytes(32).toString('hex');
return [{json: {token}}];`
    }
  }
];

for (const test of testCases) {
  console.log(`Test: ${test.name}`);
  const result = EnhancedConfigValidator.validateWithMode(
    'nodes-base.code',
    test.config,
    [
      { name: 'language', type: 'options', options: ['javaScript', 'python'] },
      { name: 'jsCode', type: 'string' }
    ],
    'operation',
    'ai-friendly'
  );
  
  console.log(`  Valid: ${result.valid}`);
  if (result.errors.length > 0) {
    console.log(`  Errors: ${result.errors.map(e => e.message).join(', ')}`);
  }
  if (result.warnings.length > 0) {
    console.log(`  Warnings: ${result.warnings.map(w => w.message).join(', ')}`);
  }
  console.log();
}

console.log('✅ $helpers validation tests completed!');
```

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

```
server {
    listen 80;
    server_name n8ndocumentation.aiservices.pl;

    # Redirect HTTP to HTTPS
    location / {
        return 301 https://$server_name$request_uri;
    }
}

server {
    listen 443 ssl http2;
    server_name n8ndocumentation.aiservices.pl;

    # SSL configuration (managed by Certbot)
    ssl_certificate /etc/letsencrypt/live/n8ndocumentation.aiservices.pl/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/n8ndocumentation.aiservices.pl/privkey.pem;
    
    # SSL security settings
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;
    
    # Security headers
    add_header X-Content-Type-Options nosniff;
    add_header X-Frame-Options DENY;
    add_header X-XSS-Protection "1; mode=block";
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    
    # Logging
    access_log /var/log/nginx/n8n-mcp-access.log;
    error_log /var/log/nginx/n8n-mcp-error.log;
    
    # Proxy settings
    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
        
        # Timeouts for MCP operations
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
        
        # Increase buffer sizes for large responses
        proxy_buffer_size 16k;
        proxy_buffers 8 16k;
        proxy_busy_buffers_size 32k;
    }
    
    # Rate limiting for API endpoints
    location /mcp {
        limit_req zone=mcp_limit burst=10 nodelay;
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        
        # Larger timeouts for MCP
        proxy_connect_timeout 120s;
        proxy_send_timeout 120s;
        proxy_read_timeout 120s;
    }
}

# Rate limiting zone
limit_req_zone $binary_remote_addr zone=mcp_limit:10m rate=10r/s;
```

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

```typescript
import { logger } from './logger';

export class MCPError extends Error {
  public code: string;
  public statusCode?: number;
  public data?: any;

  constructor(message: string, code: string, statusCode?: number, data?: any) {
    super(message);
    this.name = 'MCPError';
    this.code = code;
    this.statusCode = statusCode;
    this.data = data;
  }
}

export class N8NConnectionError extends MCPError {
  constructor(message: string, data?: any) {
    super(message, 'N8N_CONNECTION_ERROR', 503, data);
    this.name = 'N8NConnectionError';
  }
}

export class AuthenticationError extends MCPError {
  constructor(message: string = 'Authentication failed') {
    super(message, 'AUTH_ERROR', 401);
    this.name = 'AuthenticationError';
  }
}

export class ValidationError extends MCPError {
  constructor(message: string, data?: any) {
    super(message, 'VALIDATION_ERROR', 400, data);
    this.name = 'ValidationError';
  }
}

export class ToolNotFoundError extends MCPError {
  constructor(toolName: string) {
    super(`Tool '${toolName}' not found`, 'TOOL_NOT_FOUND', 404);
    this.name = 'ToolNotFoundError';
  }
}

export class ResourceNotFoundError extends MCPError {
  constructor(resourceUri: string) {
    super(`Resource '${resourceUri}' not found`, 'RESOURCE_NOT_FOUND', 404);
    this.name = 'ResourceNotFoundError';
  }
}

export function handleError(error: any): MCPError {
  if (error instanceof MCPError) {
    return error;
  }

  if (error.response) {
    // HTTP error from n8n API
    const status = error.response.status;
    const message = error.response.data?.message || error.message;
    
    if (status === 401) {
      return new AuthenticationError(message);
    } else if (status === 404) {
      return new MCPError(message, 'NOT_FOUND', 404);
    } else if (status >= 500) {
      return new N8NConnectionError(message);
    }
    
    return new MCPError(message, 'API_ERROR', status);
  }

  if (error.code === 'ECONNREFUSED') {
    return new N8NConnectionError('Cannot connect to n8n API');
  }

  // Generic error
  return new MCPError(
    error.message || 'An unexpected error occurred',
    'UNKNOWN_ERROR',
    500
  );
}

export async function withErrorHandling<T>(
  operation: () => Promise<T>,
  context: string
): Promise<T> {
  try {
    return await operation();
  } catch (error) {
    logger.error(`Error in ${context}:`, error);
    throw handleError(error);
  }
}
```

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

```typescript
#!/usr/bin/env npx tsx
/**
 * Integration test for the telemetry manager
 */

import { telemetry } from '../src/telemetry/telemetry-manager';

async function testIntegration() {
  console.log('🧪 Testing Telemetry Manager Integration\n');

  // Check status
  console.log('Status:', telemetry.getStatus());

  // Track session start
  console.log('\nTracking session start...');
  telemetry.trackSessionStart();

  // Track tool usage
  console.log('Tracking tool usage...');
  telemetry.trackToolUsage('search_nodes', true, 150);
  telemetry.trackToolUsage('get_node_info', true, 75);
  telemetry.trackToolUsage('validate_workflow', false, 200);

  // Track errors
  console.log('Tracking errors...');
  telemetry.trackError('ValidationError', 'workflow_validation', 'validate_workflow', 'Required field missing: nodes array is empty');

  // Track a test workflow
  console.log('Tracking workflow creation...');
  const testWorkflow = {
    nodes: [
      {
        id: '1',
        type: 'n8n-nodes-base.webhook',
        name: 'Webhook',
        position: [0, 0],
        parameters: {
          path: '/test-webhook',
          httpMethod: 'POST'
        }
      },
      {
        id: '2',
        type: 'n8n-nodes-base.httpRequest',
        name: 'HTTP Request',
        position: [250, 0],
        parameters: {
          url: 'https://api.example.com/endpoint',
          method: 'POST',
          authentication: 'genericCredentialType',
          genericAuthType: 'httpHeaderAuth',
          sendHeaders: true,
          headerParameters: {
            parameters: [
              {
                name: 'Authorization',
                value: 'Bearer sk-1234567890abcdef'
              }
            ]
          }
        }
      },
      {
        id: '3',
        type: 'n8n-nodes-base.slack',
        name: 'Slack',
        position: [500, 0],
        parameters: {
          channel: '#notifications',
          text: 'Workflow completed!'
        }
      }
    ],
    connections: {
      '1': {
        main: [[{ node: '2', type: 'main', index: 0 }]]
      },
      '2': {
        main: [[{ node: '3', type: 'main', index: 0 }]]
      }
    }
  };

  telemetry.trackWorkflowCreation(testWorkflow, true);

  // Force flush
  console.log('\nFlushing telemetry data...');
  await telemetry.flush();

  console.log('\n✅ Telemetry integration test completed!');
  console.log('Check your Supabase dashboard for the telemetry data.');
}

testIntegration().catch(console.error);

```

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

```typescript
#!/usr/bin/env node
import axios from 'axios';
import { spawn } from 'child_process';

async function testMaliciousHeaders() {
  console.log('🔒 Testing Security Fixes...\n');
  
  // Start server with TRUST_PROXY enabled
  const serverProcess = spawn('node', ['dist/mcp/index.js'], {
    env: {
      ...process.env,
      MCP_MODE: 'http',
      AUTH_TOKEN: 'test-security-token-32-characters-long',
      PORT: '3999',
      TRUST_PROXY: '1'
    }
  });

  // Wait for server to start
  await new Promise(resolve => {
    serverProcess.stdout.on('data', (data) => {
      if (data.toString().includes('Press Ctrl+C to stop')) {
        resolve(undefined);
      }
    });
  });

  const testCases = [
    {
      name: 'Valid proxy headers',
      headers: {
        'X-Forwarded-Host': 'example.com',
        'X-Forwarded-Proto': 'https'
      }
    },
    {
      name: 'Malicious host header (with path)',
      headers: {
        'X-Forwarded-Host': 'evil.com/path/to/evil',
        'X-Forwarded-Proto': 'https'
      }
    },
    {
      name: 'Malicious host header (with @)',
      headers: {
        'X-Forwarded-Host': '[email protected]',
        'X-Forwarded-Proto': 'https'
      }
    },
    {
      name: 'Invalid hostname (multiple dots)',
      headers: {
        'X-Forwarded-Host': '.....',
        'X-Forwarded-Proto': 'https'
      }
    },
    {
      name: 'IPv6 address',
      headers: {
        'X-Forwarded-Host': '[::1]:3000',
        'X-Forwarded-Proto': 'https'
      }
    }
  ];

  for (const testCase of testCases) {
    try {
      const response = await axios.get('http://localhost:3999/', {
        headers: testCase.headers,
        timeout: 2000
      });
      
      const endpoints = response.data.endpoints;
      const healthUrl = endpoints?.health?.url || 'N/A';
      
      console.log(`✅ ${testCase.name}`);
      console.log(`   Response: ${healthUrl}`);
      
      // Check if malicious headers were blocked
      if (testCase.name.includes('Malicious') || testCase.name.includes('Invalid')) {
        if (healthUrl.includes('evil.com') || healthUrl.includes('@') || healthUrl.includes('.....')) {
          console.log('   ❌ SECURITY ISSUE: Malicious header was not blocked!');
        } else {
          console.log('   ✅ Malicious header was blocked');
        }
      }
    } catch (error) {
      console.log(`❌ ${testCase.name} - Request failed`);
    }
    console.log('');
  }

  serverProcess.kill();
}

testMaliciousHeaders().catch(console.error);
```

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

```typescript
/**
 * SQLiteStorageService - A simple wrapper around DatabaseAdapter for benchmarks
 */
import { DatabaseAdapter, createDatabaseAdapter } from '../database/database-adapter';

export class SQLiteStorageService {
  private adapter: DatabaseAdapter | null = null;
  private dbPath: string;

  constructor(dbPath: string = ':memory:') {
    this.dbPath = dbPath;
    this.initSync();
  }

  private initSync() {
    // For benchmarks, we'll use synchronous initialization
    // In real usage, this should be async
    const Database = require('better-sqlite3');
    const db = new Database(this.dbPath);
    
    // Create a simple adapter
    this.adapter = {
      prepare: (sql: string) => db.prepare(sql),
      exec: (sql: string) => db.exec(sql),
      close: () => db.close(),
      pragma: (key: string, value?: any) => db.pragma(`${key}${value !== undefined ? ` = ${value}` : ''}`),
      inTransaction: db.inTransaction,
      transaction: (fn: () => any) => db.transaction(fn)(),
      checkFTS5Support: () => {
        try {
          db.exec("CREATE VIRTUAL TABLE test_fts USING fts5(content)");
          db.exec("DROP TABLE test_fts");
          return true;
        } catch {
          return false;
        }
      }
    };
    
    // Initialize schema
    this.initializeSchema();
  }
  
  private initializeSchema() {
    const schema = `
      CREATE TABLE IF NOT EXISTS nodes (
        node_type TEXT PRIMARY KEY,
        package_name TEXT NOT NULL,
        display_name TEXT NOT NULL,
        description TEXT,
        category TEXT,
        development_style TEXT CHECK(development_style IN ('declarative', 'programmatic')),
        is_ai_tool INTEGER DEFAULT 0,
        is_trigger INTEGER DEFAULT 0,
        is_webhook INTEGER DEFAULT 0,
        is_versioned INTEGER DEFAULT 0,
        version TEXT,
        documentation TEXT,
        properties_schema TEXT,
        operations TEXT,
        credentials_required TEXT,
        updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
      );
      
      CREATE INDEX IF NOT EXISTS idx_package ON nodes(package_name);
      CREATE INDEX IF NOT EXISTS idx_ai_tool ON nodes(is_ai_tool);
      CREATE INDEX IF NOT EXISTS idx_category ON nodes(category);
    `;
    
    this.adapter!.exec(schema);
  }

  get db(): DatabaseAdapter {
    if (!this.adapter) {
      throw new Error('Database not initialized');
    }
    return this.adapter;
  }

  close() {
    if (this.adapter) {
      this.adapter.close();
      this.adapter = null;
    }
  }
}
```

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

```javascript
#!/usr/bin/env node

const markdown = `
## Operations

* **Channel**
    * **Archive** a channel.
    * **Close** a direct message or multi-person direct message.
    * **Create** a public or private channel-based conversation.
    * **Get** information about a channel.
    * **Get Many**: Get a list of channels in Slack.
* **File**
    * **Get** a file.
    * **Get Many**: Get and filter team files.
    * **Upload**: Create or upload an existing file.

## Templates and examples
`;

function extractOperations(markdown) {
  const operations = [];
  
  // Find operations section
  const operationsMatch = markdown.match(/##\s+Operations\s*\n([\s\S]*?)(?=\n##|\n#|$)/i);
  if (!operationsMatch) {
    console.log('No operations section found');
    return operations;
  }
  
  const operationsText = operationsMatch[1];
  console.log('Operations text:', operationsText.substring(0, 200));
  
  // Parse operation structure
  let currentResource = null;
  const lines = operationsText.split('\n');
  
  for (let i = 0; i < lines.length; i++) {
    const line = lines[i];
    const trimmedLine = line.trim();
    
    // Resource level (e.g., "* **Channel**")
    if (trimmedLine.match(/^\*\s+\*\*([^*]+)\*\*/)) {
      currentResource = trimmedLine.match(/^\*\s+\*\*([^*]+)\*\*/)[1].trim();
      console.log(`Found resource: ${currentResource}`);
      continue;
    }
    
    // Skip if we don't have a current resource
    if (!currentResource) continue;
    
    // Operation level - look for indented bullets (4 spaces + *)
    if (line.match(/^\s{4}\*\s+/)) {
      console.log(`Found operation line: "${line}"`);
      
      // Extract operation name and description
      const operationMatch = trimmedLine.match(/^\*\s+\*\*([^*]+)\*\*(.*)$/);
      if (operationMatch) {
        const operation = operationMatch[1].trim();
        let description = operationMatch[2].trim();
        
        // Clean up description
        description = description.replace(/^:\s*/, '').replace(/\.$/, '').trim();
        
        operations.push({
          resource: currentResource,
          operation,
          description: description || operation,
        });
        console.log(`  Parsed: ${operation} - ${description}`);
      }
    }
  }
  
  return operations;
}

const operations = extractOperations(markdown);
console.log('\nTotal operations found:', operations.length);
console.log('\nOperations:');
operations.forEach(op => {
  console.log(`- ${op.resource}.${op.operation}: ${op.description}`);
});
```

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

```markdown
# Claude Code Setup

Connect n8n-MCP to Claude Code CLI for enhanced n8n workflow development from the command line.

## Quick Setup via CLI

### Basic configuration (documentation tools only):
```bash
claude mcp add n8n-mcp \
  -e MCP_MODE=stdio \
  -e LOG_LEVEL=error \
  -e DISABLE_CONSOLE_OUTPUT=true \
  -- npx n8n-mcp
```

![Adding n8n-MCP server in Claude Code](./img/cc_command.png)

### Full configuration (with n8n management tools):
```bash
claude mcp add n8n-mcp \
  -e MCP_MODE=stdio \
  -e LOG_LEVEL=error \
  -e DISABLE_CONSOLE_OUTPUT=true \
  -e N8N_API_URL=https://your-n8n-instance.com \
  -e N8N_API_KEY=your-api-key \
  -- npx n8n-mcp
```

Make sure to replace `https://your-n8n-instance.com` with your actual n8n URL and `your-api-key` with your n8n API key.

## Alternative Setup Methods

### Option 1: Import from Claude Desktop

If you already have n8n-MCP configured in Claude Desktop:
```bash
claude mcp add-from-claude-desktop
```

### Option 2: Project Configuration

For team sharing, add to `.mcp.json` in your project root:
```json
{
  "mcpServers": {
    "n8n-mcp": {
      "command": "npx",
      "args": ["n8n-mcp"],
      "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"
      }
    }
  }
}
```

Then use with scope flag:
```bash
claude mcp add n8n-mcp --scope project
```

## Managing Your MCP Server

Check server status:
```bash
claude mcp list
claude mcp get n8n-mcp
```

During a conversation, use the `/mcp` command to see server status and available tools.

![n8n-MCP connected and showing 39 tools available](./img/cc_connected.png)

Remove the server:
```bash
claude mcp remove n8n-mcp
```

## Project Instructions

For optimal results, create a `CLAUDE.md` file in your project root with the instructions from the [main README's Claude Project Setup section](../README.md#-claude-project-setup).

## Tips

- If you're running n8n locally, use `http://localhost:5678` as the N8N_API_URL
- The n8n API credentials are optional - without them, you'll have documentation and validation tools only
- With API credentials, you'll get full workflow management capabilities
- Use `--scope local` (default) to keep your API credentials private
- Use `--scope project` to share configuration with your team (put credentials in environment variables)
- Claude Code will automatically start the MCP server when you begin a conversation

```

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

```typescript
#!/usr/bin/env npx tsx
/**
 * Test that RLS properly protects data
 */

import { createClient } from '@supabase/supabase-js';
import dotenv from 'dotenv';

dotenv.config();

async function testSecurity() {
  const supabaseUrl = process.env.SUPABASE_URL!;
  const supabaseAnonKey = process.env.SUPABASE_ANON_KEY!;

  console.log('🔒 Testing Telemetry Security (RLS)\n');

  const supabase = createClient(supabaseUrl, supabaseAnonKey, {
    auth: {
      persistSession: false,
      autoRefreshToken: false,
    }
  });

  // Test 1: Verify anon can INSERT
  console.log('Test 1: Anonymous INSERT (should succeed)...');
  const testData = {
    user_id: 'security-test-' + Date.now(),
    event: 'security_test',
    properties: { test: true }
  };

  const { error: insertError } = await supabase
    .from('telemetry_events')
    .insert([testData]);

  if (insertError) {
    console.error('❌ Insert failed:', insertError.message);
  } else {
    console.log('✅ Insert succeeded (as expected)');
  }

  // Test 2: Verify anon CANNOT SELECT
  console.log('\nTest 2: Anonymous SELECT (should fail)...');
  const { data, error: selectError } = await supabase
    .from('telemetry_events')
    .select('*')
    .limit(1);

  if (selectError) {
    console.log('✅ Select blocked by RLS (as expected):', selectError.message);
  } else if (data && data.length > 0) {
    console.error('❌ SECURITY ISSUE: Anon can read data!', data);
  } else if (data && data.length === 0) {
    console.log('⚠️  Select returned empty array (might be RLS working)');
  }

  // Test 3: Verify anon CANNOT UPDATE
  console.log('\nTest 3: Anonymous UPDATE (should fail)...');
  const { error: updateError } = await supabase
    .from('telemetry_events')
    .update({ event: 'hacked' })
    .eq('user_id', 'test');

  if (updateError) {
    console.log('✅ Update blocked (as expected):', updateError.message);
  } else {
    console.error('❌ SECURITY ISSUE: Anon can update data!');
  }

  // Test 4: Verify anon CANNOT DELETE
  console.log('\nTest 4: Anonymous DELETE (should fail)...');
  const { error: deleteError } = await supabase
    .from('telemetry_events')
    .delete()
    .eq('user_id', 'test');

  if (deleteError) {
    console.log('✅ Delete blocked (as expected):', deleteError.message);
  } else {
    console.error('❌ SECURITY ISSUE: Anon can delete data!');
  }

  console.log('\n✨ Security test completed!');
  console.log('Summary: Anonymous users can INSERT (for telemetry) but cannot READ/UPDATE/DELETE');
}

testSecurity().catch(console.error);
```

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

```javascript
#!/usr/bin/env node

const fs = require('fs');
const path = require('path');

/**
 * Formats Vitest benchmark results for github-action-benchmark
 * Converts from Vitest format to the expected format
 */
function formatBenchmarkResults() {
  const resultsPath = path.join(process.cwd(), 'benchmark-results.json');
  
  if (!fs.existsSync(resultsPath)) {
    console.error('benchmark-results.json not found');
    process.exit(1);
  }

  const vitestResults = JSON.parse(fs.readFileSync(resultsPath, 'utf8'));
  
  // Convert to github-action-benchmark format
  const formattedResults = [];

  // Vitest benchmark JSON reporter format
  if (vitestResults.files) {
    for (const file of vitestResults.files) {
      const suiteName = path.basename(file.filepath, '.bench.ts');
      
      // Process each suite in the file
      if (file.groups) {
        for (const group of file.groups) {
          for (const benchmark of group.benchmarks || []) {
            if (benchmark.result) {
              formattedResults.push({
                name: `${suiteName} - ${benchmark.name}`,
                unit: 'ms',
                value: benchmark.result.mean || 0,
                range: (benchmark.result.max - benchmark.result.min) || 0,
                extra: `${benchmark.result.hz?.toFixed(0) || 0} ops/sec`
              });
            }
          }
        }
      }
    }
  } else if (Array.isArray(vitestResults)) {
    // Alternative format handling
    for (const result of vitestResults) {
      if (result.name && result.result) {
        formattedResults.push({
          name: result.name,
          unit: 'ms',
          value: result.result.mean || 0,
          range: (result.result.max - result.result.min) || 0,
          extra: `${result.result.hz?.toFixed(0) || 0} ops/sec`
        });
      }
    }
  }

  // Write formatted results
  const outputPath = path.join(process.cwd(), 'benchmark-results-formatted.json');
  fs.writeFileSync(outputPath, JSON.stringify(formattedResults, null, 2));
  
  // Also create a summary for PR comments
  const summary = {
    timestamp: new Date().toISOString(),
    benchmarks: formattedResults.map(b => ({
      name: b.name,
      time: `${b.value.toFixed(3)}ms`,
      opsPerSec: b.extra,
      range: `±${(b.range / 2).toFixed(3)}ms`
    }))
  };
  
  fs.writeFileSync(
    path.join(process.cwd(), 'benchmark-summary.json'),
    JSON.stringify(summary, null, 2)
  );

  console.log(`Formatted ${formattedResults.length} benchmark results`);
}

// Run if called directly
if (require.main === module) {
  formatBenchmarkResults();
}
```

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

```typescript
import type { Task, TaskResult, BenchmarkResult } from 'vitest';
import { writeFileSync } from 'fs';
import { resolve } from 'path';

interface BenchmarkJsonResult {
  timestamp: string;
  files: Array<{
    filepath: string;
    groups: Array<{
      name: string;
      benchmarks: Array<{
        name: string;
        result: {
          mean: number;
          min: number;
          max: number;
          hz: number;
          p75: number;
          p99: number;
          p995: number;
          p999: number;
          rme: number;
          samples: number;
        };
      }>;
    }>;
  }>;
}

export class BenchmarkJsonReporter {
  private results: BenchmarkJsonResult = {
    timestamp: new Date().toISOString(),
    files: []
  };

  onInit() {
    console.log('[BenchmarkJsonReporter] Initialized');
  }

  onFinished(files?: Task[]) {
    console.log('[BenchmarkJsonReporter] onFinished called');
    
    if (!files) {
      console.log('[BenchmarkJsonReporter] No files provided');
      return;
    }

    for (const file of files) {
      const fileResult = {
        filepath: file.filepath || 'unknown',
        groups: [] as any[]
      };

      this.processTask(file, fileResult);

      if (fileResult.groups.length > 0) {
        this.results.files.push(fileResult);
      }
    }

    // Write results
    const outputPath = resolve(process.cwd(), 'benchmark-results.json');
    writeFileSync(outputPath, JSON.stringify(this.results, null, 2));
    console.log(`[BenchmarkJsonReporter] Results written to ${outputPath}`);
  }

  private processTask(task: Task, fileResult: any) {
    if (task.type === 'suite' && task.tasks) {
      const group = {
        name: task.name,
        benchmarks: [] as any[]
      };

      for (const benchmark of task.tasks) {
        const result = benchmark.result as TaskResult & { benchmark?: BenchmarkResult };
        if (result?.benchmark) {
          group.benchmarks.push({
            name: benchmark.name,
            result: {
              mean: result.benchmark.mean || 0,
              min: result.benchmark.min || 0,
              max: result.benchmark.max || 0,
              hz: result.benchmark.hz || 0,
              p75: result.benchmark.p75 || 0,
              p99: result.benchmark.p99 || 0,
              p995: result.benchmark.p995 || 0,
              p999: result.benchmark.p999 || 0,
              rme: result.benchmark.rme || 0,
              samples: result.benchmark.samples?.length || 0
            }
          });
        }
      }

      if (group.benchmarks.length > 0) {
        fileResult.groups.push(group);
      }
    }
  }
}
```

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

```typescript
import { Factory } from 'fishery';
import { faker } from '@faker-js/faker';

interface NodeDefinition {
  name: string;
  displayName: string;
  description: string;
  version: number;
  defaults: { name: string };
  inputs: string[];
  outputs: string[];
  properties: any[];
  credentials?: any[];
  group?: string[];
}

export const nodeFactory = Factory.define<NodeDefinition>(() => ({
  name: faker.helpers.slugify(faker.word.noun()),
  displayName: faker.company.name(),
  description: faker.lorem.sentence(),
  version: faker.number.int({ min: 1, max: 5 }),
  defaults: {
    name: faker.word.noun()
  },
  inputs: ['main'],
  outputs: ['main'],
  group: [faker.helpers.arrayElement(['transform', 'trigger', 'output'])],
  properties: [
    {
      displayName: 'Resource',
      name: 'resource',
      type: 'options',
      default: 'user',
      options: [
        { name: 'User', value: 'user' },
        { name: 'Post', value: 'post' }
      ]
    }
  ],
  credentials: []
}));

// Specific node factories
export const webhookNodeFactory = nodeFactory.params({
  name: 'webhook',
  displayName: 'Webhook',
  description: 'Starts the workflow when a webhook is called',
  group: ['trigger'],
  properties: [
    {
      displayName: 'Path',
      name: 'path',
      type: 'string',
      default: 'webhook',
      required: true
    },
    {
      displayName: 'Method',
      name: 'method',
      type: 'options',
      default: 'GET',
      options: [
        { name: 'GET', value: 'GET' },
        { name: 'POST', value: 'POST' }
      ]
    }
  ]
});

export const slackNodeFactory = nodeFactory.params({
  name: 'slack',
  displayName: 'Slack',
  description: 'Send messages to Slack',
  group: ['output'],
  credentials: [
    {
      name: 'slackApi',
      required: true
    }
  ],
  properties: [
    {
      displayName: 'Resource',
      name: 'resource',
      type: 'options',
      default: 'message',
      options: [
        { name: 'Message', value: 'message' },
        { name: 'Channel', value: 'channel' }
      ]
    },
    {
      displayName: 'Operation',
      name: 'operation',
      type: 'options',
      displayOptions: {
        show: {
          resource: ['message']
        }
      },
      default: 'post',
      options: [
        { name: 'Post', value: 'post' },
        { name: 'Update', value: 'update' }
      ]
    },
    {
      displayName: 'Channel',
      name: 'channel',
      type: 'string',
      required: true,
      displayOptions: {
        show: {
          resource: ['message'],
          operation: ['post']
        }
      },
      default: ''
    }
  ]
});
```

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

```typescript
/**
 * Type definitions for test environment variables
 */

declare global {
  namespace NodeJS {
    interface ProcessEnv {
      // Core Environment
      NODE_ENV: 'test' | 'development' | 'production';
      MCP_MODE?: 'test' | 'http' | 'stdio';
      TEST_ENVIRONMENT?: string;

      // Database Configuration
      NODE_DB_PATH?: string;
      REBUILD_ON_START?: string;
      TEST_SEED_DATABASE?: string;
      TEST_SEED_TEMPLATES?: string;

      // API Configuration
      N8N_API_URL?: string;
      N8N_API_KEY?: string;
      N8N_WEBHOOK_BASE_URL?: string;
      N8N_WEBHOOK_TEST_URL?: string;

      // Server Configuration
      PORT?: string;
      HOST?: string;
      CORS_ORIGIN?: string;

      // Authentication
      AUTH_TOKEN?: string;
      MCP_AUTH_TOKEN?: string;

      // Logging
      LOG_LEVEL?: 'debug' | 'info' | 'warn' | 'error';
      DEBUG?: string;
      TEST_LOG_VERBOSE?: string;
      ERROR_SHOW_STACK?: string;
      ERROR_SHOW_DETAILS?: string;

      // Test Timeouts
      TEST_TIMEOUT_UNIT?: string;
      TEST_TIMEOUT_INTEGRATION?: string;
      TEST_TIMEOUT_E2E?: string;
      TEST_TIMEOUT_GLOBAL?: string;

      // Test Execution
      TEST_RETRY_ATTEMPTS?: string;
      TEST_RETRY_DELAY?: string;
      TEST_PARALLEL?: string;
      TEST_MAX_WORKERS?: string;

      // Feature Flags
      FEATURE_TEST_COVERAGE?: string;
      FEATURE_TEST_SCREENSHOTS?: string;
      FEATURE_TEST_VIDEOS?: string;
      FEATURE_TEST_TRACE?: string;
      FEATURE_MOCK_EXTERNAL_APIS?: string;
      FEATURE_USE_TEST_CONTAINERS?: string;

      // Mock Services
      MSW_ENABLED?: string;
      MSW_API_DELAY?: string;
      REDIS_MOCK_ENABLED?: string;
      REDIS_MOCK_PORT?: string;
      ELASTICSEARCH_MOCK_ENABLED?: string;
      ELASTICSEARCH_MOCK_PORT?: string;

      // Test Paths
      TEST_FIXTURES_PATH?: string;
      TEST_DATA_PATH?: string;
      TEST_SNAPSHOTS_PATH?: string;

      // Performance Thresholds
      PERF_THRESHOLD_API_RESPONSE?: string;
      PERF_THRESHOLD_DB_QUERY?: string;
      PERF_THRESHOLD_NODE_PARSE?: string;

      // Rate Limiting
      RATE_LIMIT_MAX?: string;
      RATE_LIMIT_WINDOW?: string;

      // Caching
      CACHE_TTL?: string;
      CACHE_ENABLED?: string;

      // Cleanup
      TEST_CLEANUP_ENABLED?: string;
      TEST_CLEANUP_ON_FAILURE?: string;

      // Network
      NETWORK_TIMEOUT?: string;
      NETWORK_RETRY_COUNT?: string;

      // Memory
      TEST_MEMORY_LIMIT?: string;

      // Coverage
      COVERAGE_DIR?: string;
      COVERAGE_REPORTER?: string;
    }
  }
}

// Export empty object to make this a module
export {};
```

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

```javascript
#!/usr/bin/env node

/**
 * HTTP-to-stdio bridge for n8n-MCP
 * Connects to n8n-MCP HTTP server and bridges stdio communication
 */

const http = require('http');
const readline = require('readline');

// Use MCP_URL from environment or construct from HOST/PORT if available
const defaultHost = process.env.HOST || 'localhost';
const defaultPort = process.env.PORT || '3000';
const MCP_URL = process.env.MCP_URL || `http://${defaultHost}:${defaultPort}/mcp`;
const AUTH_TOKEN = process.env.AUTH_TOKEN || process.argv[2];

if (!AUTH_TOKEN) {
  console.error('Error: AUTH_TOKEN environment variable or first argument required');
  process.exit(1);
}

// Parse URL
const url = new URL(MCP_URL);

// Create readline interface for stdio
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
  terminal: false
});

// Buffer for incomplete JSON messages
let buffer = '';

// Handle incoming stdio messages
rl.on('line', async (line) => {
  try {
    const message = JSON.parse(line);
    
    // Forward to HTTP server
    const options = {
      hostname: url.hostname,
      port: url.port || 80,
      path: url.pathname,
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${AUTH_TOKEN}`,
        'Accept': 'application/json, text/event-stream'
      }
    };

    const req = http.request(options, (res) => {
      let responseData = '';

      res.on('data', (chunk) => {
        responseData += chunk;
      });

      res.on('end', () => {
        try {
          // Try to parse as JSON
          const response = JSON.parse(responseData);
          console.log(JSON.stringify(response));
        } catch (e) {
          // Handle SSE format
          const lines = responseData.split('\n');
          for (const line of lines) {
            if (line.startsWith('data: ')) {
              try {
                const data = JSON.parse(line.substring(6));
                console.log(JSON.stringify(data));
              } catch (e) {
                // Ignore parse errors
              }
            }
          }
        }
      });
    });

    req.on('error', (error) => {
      console.error(JSON.stringify({
        jsonrpc: '2.0',
        error: {
          code: -32000,
          message: `HTTP request failed: ${error.message}`
        },
        id: message.id || null
      }));
    });

    req.write(JSON.stringify(message));
    req.end();
  } catch (error) {
    // Not valid JSON, ignore
  }
});

// Handle process termination
process.on('SIGTERM', () => {
  process.exit(0);
});

process.on('SIGINT', () => {
  process.exit(0);
});
```

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

```typescript
import { beforeAll, afterAll, afterEach } from 'vitest';
import { setupServer } from 'msw/node';
import { handlers as defaultHandlers } from '../../mocks/n8n-api/handlers';

// Create the MSW server instance with default handlers
export const server = setupServer(...defaultHandlers);

// Enable request logging in development/debugging
if (process.env.MSW_DEBUG === 'true' || process.env.TEST_DEBUG === 'true') {
  server.events.on('request:start', ({ request }) => {
    console.log('[MSW] %s %s', request.method, request.url);
  });

  server.events.on('request:match', ({ request }) => {
    console.log('[MSW] Request matched:', request.method, request.url);
  });

  server.events.on('request:unhandled', ({ request }) => {
    console.warn('[MSW] Unhandled request:', request.method, request.url);
  });

  server.events.on('response:mocked', ({ request, response }) => {
    console.log('[MSW] Mocked response for %s %s: %d', 
      request.method, 
      request.url, 
      response.status
    );
  });
}

// Start server before all tests
beforeAll(() => {
  server.listen({
    onUnhandledRequest: process.env.CI === 'true' ? 'error' : 'warn',
  });
});

// Reset handlers after each test (important for test isolation)
afterEach(() => {
  server.resetHandlers();
});

// Clean up after all tests
afterAll(() => {
  // Remove all event listeners to prevent memory leaks
  server.events.removeAllListeners();
  
  // Close the server
  server.close();
});

// Export the server and utility functions for use in integration tests
export { server as integrationServer };
export { http, HttpResponse } from 'msw';

/**
 * Utility function to add temporary handlers for specific tests
 * @param handlers Array of MSW request handlers
 */
export function useHandlers(...handlers: any[]) {
  server.use(...handlers);
}

/**
 * Utility to wait for a specific request to be made
 * Useful for testing async operations
 */
export function waitForRequest(method: string, url: string | RegExp, timeout = 5000): Promise<Request> {
  return new Promise((resolve, reject) => {
    let timeoutId: NodeJS.Timeout;
    
    const handler = ({ request }: { request: Request }) => {
      if (request.method === method && 
          (typeof url === 'string' ? request.url === url : url.test(request.url))) {
        clearTimeout(timeoutId);
        server.events.removeListener('request:match', handler);
        resolve(request);
      }
    };
    
    // Set timeout
    timeoutId = setTimeout(() => {
      server.events.removeListener('request:match', handler);
      reject(new Error(`Timeout waiting for ${method} request to ${url}`));
    }, timeout);
    
    server.events.on('request:match', handler);
  });
}
```

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

```javascript
#!/usr/bin/env node

const fs = require('fs');
const path = require('path');

// Import the NodeSourceExtractor
const { NodeSourceExtractor } = require('../dist/utils/node-source-extractor');

async function testExtraction() {
  console.log('=== Direct Node Extraction Test ===\n');
  
  const extractor = new NodeSourceExtractor();
  
  // Test extraction of AI Agent node
  const nodeType = '@n8n/n8n-nodes-langchain.Agent';
  
  console.log(`Testing extraction of: ${nodeType}`);
  
  // First, let's debug what paths are being searched
  console.log('\nSearching in paths:');
  const searchPaths = [
    '/usr/local/lib/node_modules/n8n/node_modules',
    '/app/node_modules',
    '/home/node/.n8n/custom/nodes',
    './node_modules'
  ];
  
  for (const basePath of searchPaths) {
    console.log(`- ${basePath}`);
    try {
      const exists = fs.existsSync(basePath);
      console.log(`  Exists: ${exists}`);
      if (exists) {
        const items = fs.readdirSync(basePath).slice(0, 5);
        console.log(`  Sample items: ${items.join(', ')}...`);
      }
    } catch (e) {
      console.log(`  Error: ${e.message}`);
    }
  }
  
  try {
    const result = await extractor.extractNodeSource(nodeType, true);
    
    console.log('\n✅ Extraction successful!');
    console.log(`Source file: ${result.location}`);
    console.log(`Code length: ${result.sourceCode.length} characters`);
    console.log(`Credential code found: ${result.credentialCode ? 'Yes' : 'No'}`);
    console.log(`Package.json found: ${result.packageInfo ? 'Yes' : 'No'}`);
    
    // Show first 500 characters of the code
    console.log('\nFirst 500 characters of code:');
    console.log('=' .repeat(60));
    console.log(result.sourceCode.substring(0, 500) + '...');
    console.log('=' .repeat(60));
    
    // Show credential code if found
    if (result.credentialCode) {
      console.log('\nCredential code found!');
      console.log('First 200 characters of credential code:');
      console.log(result.credentialCode.substring(0, 200) + '...');
    }
    
    // Check if we can find it in Docker volume
    const dockerPath = '/usr/local/lib/node_modules/n8n/node_modules/.pnpm/@n8n+n8n-nodes-langchain@file+packages+@n8n+nodes-langchain_f35e7d377a7fe4d08dc2766706b5dbff/node_modules/@n8n/n8n-nodes-langchain/dist/nodes/agents/Agent/Agent.node.js';
    
    if (fs.existsSync(dockerPath)) {
      console.log('\n✅ File also found in expected Docker path');
      const dockerContent = fs.readFileSync(dockerPath, 'utf8');
      console.log(`Docker file size: ${dockerContent.length} bytes`);
    }
    
  } catch (error) {
    console.error('\n❌ Extraction failed:', error.message);
    console.error('Stack trace:', error.stack);
  }
}

testExtraction().catch(console.error);
```

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

```typescript
import { describe, it, expect } from 'vitest';
import { N8NDocumentationMCPServer } from '../../../src/mcp/server';

describe('Basic MCP Connection', () => {
  it('should initialize MCP server', async () => {
    const server = new N8NDocumentationMCPServer();
    
    // Test executeTool directly - it returns raw data
    const result = await server.executeTool('get_database_statistics', {});
    expect(result).toBeDefined();
    expect(typeof result).toBe('object');
    expect(result.totalNodes).toBeDefined();
    expect(result.statistics).toBeDefined();
    
    await server.shutdown();
  });
  
  it('should execute list_nodes tool', async () => {
    const server = new N8NDocumentationMCPServer();
    
    // First check if we have any nodes in the database
    const stats = await server.executeTool('get_database_statistics', {});
    const hasNodes = stats.totalNodes > 0;
    
    const result = await server.executeTool('list_nodes', { limit: 5 });
    expect(result).toBeDefined();
    expect(typeof result).toBe('object');
    expect(result.nodes).toBeDefined();
    expect(Array.isArray(result.nodes)).toBe(true);
    
    if (hasNodes) {
      // If database has nodes, we should get up to 5
      expect(result.nodes.length).toBeLessThanOrEqual(5);
      expect(result.nodes.length).toBeGreaterThan(0);
      expect(result.nodes[0]).toHaveProperty('nodeType');
      expect(result.nodes[0]).toHaveProperty('displayName');
    } else {
      // In test environment with empty database, we expect empty results
      expect(result.nodes).toHaveLength(0);
    }
    
    await server.shutdown();
  });
  
  it('should search nodes', async () => {
    const server = new N8NDocumentationMCPServer();
    
    // First check if we have any nodes in the database
    const stats = await server.executeTool('get_database_statistics', {});
    const hasNodes = stats.totalNodes > 0;
    
    const result = await server.executeTool('search_nodes', { query: 'webhook' });
    expect(result).toBeDefined();
    expect(typeof result).toBe('object');
    expect(result.results).toBeDefined();
    expect(Array.isArray(result.results)).toBe(true);
    
    // Only expect results if the database has nodes
    if (hasNodes) {
      expect(result.results.length).toBeGreaterThan(0);
      expect(result.totalCount).toBeGreaterThan(0);
      
      // Should find webhook node
      const webhookNode = result.results.find((n: any) => n.nodeType === 'nodes-base.webhook');
      expect(webhookNode).toBeDefined();
      expect(webhookNode.displayName).toContain('Webhook');
    } else {
      // In test environment with empty database, we expect empty results
      expect(result.results).toHaveLength(0);
      expect(result.totalCount).toBe(0);
    }
    
    await server.shutdown();
  });
});
```

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

```typescript
import { ToolDocumentation } from '../types';

export const n8nUpdateFullWorkflowDoc: ToolDocumentation = {
  name: 'n8n_update_full_workflow',
  category: 'workflow_management',
  essentials: {
    description: 'Full workflow update. Requires complete nodes[] and connections{}. For incremental use n8n_update_partial_workflow.',
    keyParameters: ['id', 'nodes', 'connections'],
    example: 'n8n_update_full_workflow({id: "wf_123", nodes: [...], connections: {...}})',
    performance: 'Network-dependent',
    tips: [
      'Must provide complete workflow',
      'Use update_partial for small changes',
      'Validate before updating'
    ]
  },
  full: {
    description: 'Performs a complete workflow update by replacing the entire workflow definition. Requires providing the complete nodes array and connections object, even for small changes. This is a full replacement operation - any nodes or connections not included will be removed.',
    parameters: {
      id: { type: 'string', required: true, description: 'Workflow ID to update' },
      name: { type: 'string', description: 'New workflow name (optional)' },
      nodes: { type: 'array', description: 'Complete array of workflow nodes (required if modifying structure)' },
      connections: { type: 'object', description: 'Complete connections object (required if modifying structure)' },
      settings: { type: 'object', description: 'Workflow settings to update (timezone, error handling, etc.)' }
    },
    returns: 'Updated workflow object with all fields including the changes applied',
    examples: [
      'n8n_update_full_workflow({id: "abc", name: "New Name"}) - Rename only',
      'n8n_update_full_workflow({id: "xyz", nodes: [...], connections: {...}}) - Full structure update',
      'const wf = n8n_get_workflow({id}); wf.nodes.push(newNode); n8n_update_full_workflow(wf); // Add node'
    ],
    useCases: [
      'Major workflow restructuring',
      'Bulk node updates',
      'Workflow imports/cloning',
      'Complete workflow replacement',
      'Settings changes'
    ],
    performance: 'Network-dependent - typically 200-500ms. Larger workflows take longer. Consider update_partial for better performance.',
    bestPractices: [
      'Get workflow first, modify, then update',
      'Validate with validate_workflow before updating',
      'Use update_partial for small changes',
      'Test updates in non-production first'
    ],
    pitfalls: [
      'Requires N8N_API_URL and N8N_API_KEY configured',
      'Must include ALL nodes/connections',
      'Missing nodes will be deleted',
      'Can break active workflows',
      'No partial updates - use update_partial instead'
    ],
    relatedTools: ['n8n_get_workflow', 'n8n_update_partial_workflow', 'validate_workflow', 'n8n_create_workflow']
  }
};
```

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

```typescript
/**
 * Shared Error Sanitization Utilities
 * Used by both error-sanitizer.ts and event-tracker.ts to avoid code duplication
 *
 * Security patterns from v2.15.3 with ReDoS fix from v2.18.3
 */

import { logger } from '../utils/logger';

/**
 * Core error message sanitization with security-focused patterns
 *
 * Sanitization order (critical for preventing leakage):
 * 1. Early truncation (ReDoS prevention)
 * 2. Stack trace limitation
 * 3. URLs (most encompassing) - fully redact
 * 4. Specific credentials (AWS, GitHub, JWT, Bearer)
 * 5. Emails (after URLs)
 * 6. Long keys and tokens
 * 7. Generic credential patterns
 * 8. Final truncation
 *
 * @param errorMessage - Raw error message to sanitize
 * @returns Sanitized error message safe for telemetry
 */
export function sanitizeErrorMessageCore(errorMessage: string): string {
  try {
    // Early truncate to prevent ReDoS and performance issues
    const maxLength = 1500;
    const trimmed = errorMessage.length > maxLength
      ? errorMessage.substring(0, maxLength)
      : errorMessage;

    // Handle stack traces - keep only first 3 lines (message + top stack frames)
    const lines = trimmed.split('\n');
    let sanitized = lines.slice(0, 3).join('\n');

    // Sanitize sensitive data in correct order to prevent leakage

    // 1. URLs first (most encompassing) - fully redact to prevent path leakage
    sanitized = sanitized.replace(/https?:\/\/\S+/gi, '[URL]');

    // 2. Specific credential patterns (before generic patterns)
    sanitized = sanitized
      .replace(/AKIA[A-Z0-9]{16}/g, '[AWS_KEY]')
      .replace(/ghp_[a-zA-Z0-9]{36,}/g, '[GITHUB_TOKEN]')
      .replace(/eyJ[a-zA-Z0-9_-]+\.eyJ[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+/g, '[JWT]')
      .replace(/Bearer\s+[^\s]+/gi, 'Bearer [TOKEN]');

    // 3. Emails (after URLs to avoid partial matches)
    sanitized = sanitized.replace(/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g, '[EMAIL]');

    // 4. Long keys and quoted tokens
    sanitized = sanitized
      .replace(/\b[a-zA-Z0-9_-]{32,}\b/g, '[KEY]')
      .replace(/(['"])[a-zA-Z0-9_-]{16,}\1/g, '$1[TOKEN]$1');

    // 5. Generic credential patterns (after specific ones to avoid conflicts)
    // FIX (v2.18.3): Replaced negative lookbehind with simpler regex to prevent ReDoS
    sanitized = sanitized
      .replace(/password\s*[=:]\s*\S+/gi, 'password=[REDACTED]')
      .replace(/api[_-]?key\s*[=:]\s*\S+/gi, 'api_key=[REDACTED]')
      .replace(/\btoken\s*[=:]\s*[^\s;,)]+/gi, 'token=[REDACTED]'); // Simplified regex (no negative lookbehind)

    // Final truncate to 500 chars
    if (sanitized.length > 500) {
      sanitized = sanitized.substring(0, 500) + '...';
    }

    return sanitized;
  } catch (error) {
    logger.debug('Error message sanitization failed:', error);
    return '[SANITIZATION_FAILED]';
  }
}

```

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

```typescript
#!/usr/bin/env node

import * as dotenv from 'dotenv';
import { NodeDocumentationService } from '../services/node-documentation-service';
import { logger } from '../utils/logger';

// Load environment variables
dotenv.config();

/**
 * Rebuild the enhanced documentation database
 */
async function rebuildDocumentationDatabase() {
  console.log('🔄 Starting enhanced documentation database rebuild...\n');
  
  const startTime = Date.now();
  const service = new NodeDocumentationService();
  
  try {
    // Run the rebuild
    const results = await service.rebuildDatabase();
    
    const duration = ((Date.now() - startTime) / 1000).toFixed(2);
    
    console.log('\n✅ Enhanced documentation database rebuild completed!\n');
    console.log('📊 Results:');
    console.log(`   Total nodes found: ${results.total}`);
    console.log(`   Successfully processed: ${results.successful}`);
    console.log(`   Failed: ${results.failed}`);
    console.log(`   Duration: ${duration}s`);
    
    if (results.errors.length > 0) {
      console.log(`\n⚠️  First ${Math.min(5, results.errors.length)} errors:`);
      results.errors.slice(0, 5).forEach(err => {
        console.log(`   - ${err}`);
      });
      
      if (results.errors.length > 5) {
        console.log(`   ... and ${results.errors.length - 5} more errors`);
      }
    }
    
    // Get and display statistics
    const stats = await service.getStatistics();
    console.log('\n📈 Database Statistics:');
    console.log(`   Total nodes: ${stats.totalNodes}`);
    console.log(`   Nodes with documentation: ${stats.nodesWithDocs}`);
    console.log(`   Nodes with examples: ${stats.nodesWithExamples}`);
    console.log(`   Nodes with credentials: ${stats.nodesWithCredentials}`);
    console.log(`   Trigger nodes: ${stats.triggerNodes}`);
    console.log(`   Webhook nodes: ${stats.webhookNodes}`);
    
    console.log('\n📦 Package distribution:');
    stats.packageDistribution.slice(0, 10).forEach((pkg: any) => {
      console.log(`   ${pkg.package}: ${pkg.count} nodes`);
    });
    
    // Close database connection
    await service.close();
    
    console.log('\n✨ Enhanced documentation database is ready!');
    console.log('💡 The database now includes:');
    console.log('   - Complete node source code');
    console.log('   - Enhanced documentation with operations and API methods');
    console.log('   - Code examples and templates');
    console.log('   - Related resources and required scopes');
    
  } catch (error) {
    console.error('\n❌ Documentation database rebuild failed:', error);
    service.close();
    process.exit(1);
  }
}

// Run if called directly
if (require.main === module) {
  rebuildDocumentationDatabase().catch(error => {
    console.error('Fatal error:', error);
    process.exit(1);
  });
}

export { rebuildDocumentationDatabase };
```

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

```typescript
import { ToolDocumentation } from '../types';

export const getDatabaseStatisticsDoc: ToolDocumentation = {
  name: 'get_database_statistics',
  category: 'discovery',
  essentials: {
    description: 'Returns database health metrics and node inventory. Shows 525 total nodes, 263 AI-capable nodes, 104 triggers, with 87% documentation coverage. Primary use: verify MCP connection is working correctly.',
    keyParameters: [],
    example: 'get_database_statistics()',
    performance: 'Instant',
    tips: [
      'First tool to call when testing MCP connection',
      'Shows exact counts for all node categories',
      'Documentation coverage indicates data quality'
    ]
  },
  full: {
    description: 'Returns comprehensive database statistics showing the complete inventory of n8n nodes, their categories, documentation coverage, and package distribution. Essential for verifying MCP connectivity and understanding available resources.',
    parameters: {},
    returns: `Object containing:
{
  "total_nodes": 525,              // All nodes in database
  "nodes_with_properties": 520,    // Nodes with extracted properties (99%)
  "nodes_with_operations": 334,    // Nodes with multiple operations (64%)
  "ai_tools": 263,                 // AI-capable nodes
  "triggers": 104,                 // Workflow trigger nodes
  "documentation_coverage": "87%", // Nodes with official docs
  "packages": {
    "n8n-nodes-base": 456,         // Core n8n nodes
    "@n8n/n8n-nodes-langchain": 69 // AI/LangChain nodes
  },
  "categories": {
    "trigger": 104,
    "transform": 250,
    "output": 45,
    "input": 38,
    "AI": 88
  }
}`,
    examples: [
      'get_database_statistics() - Returns complete statistics object',
      '// Common check:',
      'const stats = get_database_statistics();',
      'if (stats.total_nodes < 500) console.error("Database incomplete!");'
    ],
    useCases: [
      'Verify MCP server is connected and responding',
      'Check if database rebuild is needed (low node count)',
      'Monitor documentation coverage improvements',
      'Validate AI tools availability for workflows',
      'Audit node distribution across packages'
    ],
    performance: 'Instant (<1ms) - Statistics are pre-calculated and cached',
    bestPractices: [
      'Call this first to verify MCP connection before other operations',
      'Check total_nodes >= 500 to ensure complete database',
      'Monitor documentation_coverage for data quality',
      'Use ai_tools count to verify AI capabilities'
    ],
    pitfalls: [
      'Statistics are cached at database build time, not real-time',
      'Won\'t reflect changes until database is rebuilt',
      'Package counts may vary with n8n version updates'
    ],
    relatedTools: ['list_nodes for detailed node listing', 'list_ai_tools for AI nodes', 'n8n_health_check for API connectivity']
  }
};
```

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

```typescript
#!/usr/bin/env npx tsx

import { createDatabaseAdapter } from '../database/database-adapter';
import { NodeRepository } from '../database/node-repository';
import { NodeSimilarityService } from '../services/node-similarity-service';
import path from 'path';

async function testSummary() {
  const dbPath = path.join(process.cwd(), 'data/nodes.db');
  const db = await createDatabaseAdapter(dbPath);
  const repository = new NodeRepository(db);
  const similarityService = new NodeSimilarityService(repository);

  const testCases = [
    { invalid: 'HttpRequest', expected: 'nodes-base.httpRequest' },
    { invalid: 'HTTPRequest', expected: 'nodes-base.httpRequest' },
    { invalid: 'Webhook', expected: 'nodes-base.webhook' },
    { invalid: 'WebHook', expected: 'nodes-base.webhook' },
    { invalid: 'slack', expected: 'nodes-base.slack' },
    { invalid: 'googleSheets', expected: 'nodes-base.googleSheets' },
    { invalid: 'telegram', expected: 'nodes-base.telegram' },
    { invalid: 'htpRequest', expected: 'nodes-base.httpRequest' },
    { invalid: 'webook', expected: 'nodes-base.webhook' },
    { invalid: 'slak', expected: 'nodes-base.slack' },
    { invalid: 'http', expected: 'nodes-base.httpRequest' },
    { invalid: 'sheet', expected: 'nodes-base.googleSheets' },
    { invalid: 'nodes-base.openai', expected: 'nodes-langchain.openAi' },
    { invalid: 'n8n-nodes-base.httpRequest', expected: 'nodes-base.httpRequest' },
    { invalid: 'foobar', expected: null },
    { invalid: 'xyz123', expected: null },
  ];

  let passed = 0;
  let failed = 0;

  console.log('Test Results Summary:');
  console.log('='.repeat(60));

  for (const testCase of testCases) {
    const suggestions = await similarityService.findSimilarNodes(testCase.invalid, 3);

    let result = '❌';
    let status = 'FAILED';

    if (testCase.expected === null) {
      // Should have no suggestions
      if (suggestions.length === 0) {
        result = '✅';
        status = 'PASSED';
        passed++;
      } else {
        failed++;
      }
    } else {
      // Should have the expected suggestion
      const found = suggestions.some(s => s.nodeType === testCase.expected);
      if (found) {
        const suggestion = suggestions.find(s => s.nodeType === testCase.expected);
        const isAutoFixable = suggestion && suggestion.confidence >= 0.9;
        result = '✅';
        status = isAutoFixable ? 'PASSED (auto-fixable)' : 'PASSED';
        passed++;
      } else {
        failed++;
      }
    }

    console.log(`${result} "${testCase.invalid}" → ${testCase.expected || 'no suggestions'}: ${status}`);
  }

  console.log('='.repeat(60));
  console.log(`\nTotal: ${passed}/${testCases.length} tests passed`);

  if (failed === 0) {
    console.log('🎉 All tests passed!');
  } else {
    console.log(`⚠️  ${failed} tests failed`);
  }
}

testSummary().catch(console.error);
```

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

```sql
-- Auto-generated SQL for n8n nodes

-- Node: n8n-nodes-base.Function
INSERT INTO nodes (node_type, name, package_name, code_hash, code_length, source_location, has_credentials)
VALUES ('n8n-nodes-base.Function', 'Function', 'n8n-nodes-base', 'd68f1ab94b190161e2ec2c56ec6631f6c3992826557c100ec578efff5de96a70', 7449, 'node_modules/n8n-nodes-base/dist/nodes/Function/Function.node.js', false);

-- Node: n8n-nodes-base.Webhook
INSERT INTO nodes (node_type, name, package_name, code_hash, code_length, source_location, has_credentials)
VALUES ('n8n-nodes-base.Webhook', 'Webhook', 'n8n-nodes-base', '143d6bbdce335c5a9204112b2c1e8b92e4061d75ba3cb23301845f6fed9e6c71', 10667, 'node_modules/n8n-nodes-base/dist/nodes/Webhook/Webhook.node.js', false);

-- Node: n8n-nodes-base.HttpRequest
INSERT INTO nodes (node_type, name, package_name, code_hash, code_length, source_location, has_credentials)
VALUES ('n8n-nodes-base.HttpRequest', 'HttpRequest', 'n8n-nodes-base', '5b5e2328474b7e85361c940dfe942e167b3f0057f38062f56d6b693f0a7ffe7e', 1343, 'node_modules/n8n-nodes-base/dist/nodes/HttpRequest/HttpRequest.node.js', false);

-- Node: n8n-nodes-base.If
INSERT INTO nodes (node_type, name, package_name, code_hash, code_length, source_location, has_credentials)
VALUES ('n8n-nodes-base.If', 'If', 'n8n-nodes-base', '7910ed9177a946b76f04ca847defb81226c37c698e4cdb63913f038c6c257ee1', 20533, 'node_modules/n8n-nodes-base/dist/nodes/If/If.node.js', false);

-- Node: n8n-nodes-base.SplitInBatches
INSERT INTO nodes (node_type, name, package_name, code_hash, code_length, source_location, has_credentials)
VALUES ('n8n-nodes-base.SplitInBatches', 'SplitInBatches', 'n8n-nodes-base', 'c751422a11e30bf361a6c4803376289740a40434aeb77f90e18cd4dd7ba5c019', 1135, 'node_modules/n8n-nodes-base/dist/nodes/SplitInBatches/SplitInBatches.node.js', false);

-- Node: n8n-nodes-base.Airtable
INSERT INTO nodes (node_type, name, package_name, code_hash, code_length, source_location, has_credentials)
VALUES ('n8n-nodes-base.Airtable', 'Airtable', 'n8n-nodes-base', '2d67e72931697178946f5127b43e954649c4c5e7ad9e29764796404ae96e7db5', 936, 'node_modules/n8n-nodes-base/dist/nodes/Airtable/Airtable.node.js', false);

-- Node: n8n-nodes-base.Slack
INSERT INTO nodes (node_type, name, package_name, code_hash, code_length, source_location, has_credentials)
VALUES ('n8n-nodes-base.Slack', 'Slack', 'n8n-nodes-base', '0ed10d0646f3c595406359edfa2c293dac41991cee59ad4fb3ccf2bb70eca6fc', 1007, 'node_modules/n8n-nodes-base/dist/nodes/Slack/Slack.node.js', false);

-- Node: n8n-nodes-base.Discord
INSERT INTO nodes (node_type, name, package_name, code_hash, code_length, source_location, has_credentials)
VALUES ('n8n-nodes-base.Discord', 'Discord', 'n8n-nodes-base', '4995f9ca5c5b57d2486c2e320cc7505238e7f2260861f7e321b44b45ccabeb00', 10049, 'node_modules/n8n-nodes-base/dist/nodes/Discord/Discord.node.js', false);


```

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

```typescript
import { ToolDocumentation } from '../types';

export const validateWorkflowConnectionsDoc: ToolDocumentation = {
  name: 'validate_workflow_connections',
  category: 'validation',
  essentials: {
    description: 'Check workflow connections only: valid nodes, no cycles, proper triggers, AI tool links. Fast structure validation.',
    keyParameters: ['workflow'],
    example: 'validate_workflow_connections({workflow: {nodes: [...], connections: {...}}})',
    performance: 'Fast (<100ms)',
    tips: [
      'Use for quick structure checks when editing connections',
      'Detects orphaned nodes and circular dependencies',
      'Validates AI Agent tool connections to ensure proper node references'
    ]
  },
  full: {
    description: 'Validates only the connection structure of a workflow without checking node configurations or expressions. This focused validation checks that all referenced nodes exist, detects circular dependencies, ensures proper trigger node placement, validates AI tool connections, and identifies orphaned or unreachable nodes.',
    parameters: {
      workflow: { 
        type: 'object', 
        required: true, 
        description: 'The workflow JSON with nodes array and connections object.' 
      }
    },
    returns: 'Object with valid (boolean), errors (array), warnings (array), and statistics about connections',
    examples: [
      'validate_workflow_connections({workflow: myWorkflow}) - Check all connections',
      'validate_workflow_connections({workflow: {nodes: [...], connections: {...}}}) - Validate structure only'
    ],
    useCases: [
      'Quick validation when modifying workflow connections',
      'Ensure all node references in connections are valid',
      'Detect circular dependencies that would cause infinite loops',
      'Validate AI Agent nodes have proper tool connections',
      'Check workflow has at least one trigger node',
      'Find orphaned nodes not connected to any flow'
    ],
    performance: 'Fast (<100ms). Only validates structure, not node content. Scales linearly with connection count.',
    bestPractices: [
      'Run after adding or removing connections',
      'Use before validate_workflow for quick structural checks',
      'Check for warnings about orphaned nodes',
      'Ensure trigger nodes are properly positioned',
      'Validate after using n8n_update_partial_workflow with connection operations'
    ],
    pitfalls: [
      'Does not validate node configurations - use validate_workflow for full validation',
      'Cannot detect logical errors in connection flow',
      'Some valid workflows may have intentionally disconnected nodes',
      'Circular dependency detection only catches direct loops',
      'Does not validate connection types match node capabilities'
    ],
    relatedTools: ['validate_workflow', 'validate_workflow_expressions', 'n8n_update_partial_workflow']
  }
};
```

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

```typescript
import { ToolDocumentation } from '../types';

export const n8nListWorkflowsDoc: ToolDocumentation = {
  name: 'n8n_list_workflows',
  category: 'workflow_management',
  essentials: {
    description: 'List workflows (minimal metadata only - no nodes/connections). Supports pagination via cursor.',
    keyParameters: ['limit', 'active', 'tags'],
    example: 'n8n_list_workflows({limit: 20, active: true})',
    performance: 'Fast (100-300ms)',
    tips: [
      'Use cursor for pagination',
      'Filter by active status',
      'Tag filtering for organization'
    ]
  },
  full: {
    description: 'Lists workflows from n8n with powerful filtering options. Returns ONLY minimal metadata (id, name, active, dates, tags, nodeCount) - no workflow structure, nodes, or connections. Use n8n_get_workflow to fetch full workflow details.',
    parameters: {
      limit: { type: 'number', description: 'Number of workflows to return (1-100, default: 100)' },
      cursor: { type: 'string', description: 'Pagination cursor from previous response for next page' },
      active: { type: 'boolean', description: 'Filter by active/inactive status' },
      tags: { type: 'array', description: 'Filter by exact tag matches (AND logic)' },
      projectId: { type: 'string', description: 'Filter by project ID (enterprise feature)' },
      excludePinnedData: { type: 'boolean', description: 'Exclude pinned data from response (default: true)' }
    },
    returns: 'Object with: workflows array (minimal fields: id, name, active, createdAt, updatedAt, tags, nodeCount), returned (count in this response), hasMore (boolean), nextCursor (for pagination), and _note (guidance when more data exists)',
    examples: [
      'n8n_list_workflows({limit: 20}) - First 20 workflows',
      'n8n_list_workflows({active: true, tags: ["production"]}) - Active production workflows',
      'n8n_list_workflows({cursor: "abc123", limit: 50}) - Next page of results'
    ],
    useCases: [
      'Build workflow dashboards',
      'Find workflows by status',
      'Organize by tags',
      'Bulk workflow operations',
      'Generate workflow reports'
    ],
    performance: 'Very fast - typically 50-200ms. Returns only minimal metadata without workflow structure.',
    bestPractices: [
      'Always check hasMore flag to determine if pagination is needed',
      'Use cursor from previous response to get next page',
      'The returned count is NOT the total in the system',
      'Iterate with cursor until hasMore is false for complete list'
    ],
    pitfalls: [
      'Requires N8N_API_URL and N8N_API_KEY configured',
      'Maximum 100 workflows per request',
      'Server may return fewer than requested limit',
      'returned field is count of current page only, not system total'
    ],
    relatedTools: ['n8n_get_workflow_minimal', 'n8n_get_workflow', 'n8n_update_partial_workflow', 'n8n_list_executions']
  }
};
```

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

```bash
#!/bin/bash

# Test script to verify re-initialization fix works

echo "Starting n8n MCP server..."
AUTH_TOKEN=test123456789012345678901234567890 npm run start:http &
SERVER_PID=$!

# Wait for server to start
sleep 3

echo "Testing multiple initialize requests..."

# First initialize request
echo "1. First initialize request:"
RESPONSE1=$(curl -s -X POST http://localhost:3000/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "Authorization: Bearer test123456789012345678901234567890" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "initialize",
    "params": {
      "protocolVersion": "2024-11-05",
      "capabilities": {
        "roots": {
          "listChanged": false
        }
      },
      "clientInfo": {
        "name": "test-client-1",
        "version": "1.0.0"
      }
    }
  }')

if echo "$RESPONSE1" | grep -q '"result"'; then
    echo "✅ First initialize request succeeded"
else
    echo "❌ First initialize request failed: $RESPONSE1"
fi

# Second initialize request (this was failing before)
echo "2. Second initialize request (this was failing before the fix):"
RESPONSE2=$(curl -s -X POST http://localhost:3000/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "Authorization: Bearer test123456789012345678901234567890" \
  -d '{
    "jsonrpc": "2.0",
    "id": 2,
    "method": "initialize",
    "params": {
      "protocolVersion": "2024-11-05",
      "capabilities": {
        "roots": {
          "listChanged": false
        }
      },
      "clientInfo": {
        "name": "test-client-2",
        "version": "1.0.0"
      }
    }
  }')

if echo "$RESPONSE2" | grep -q '"result"'; then
    echo "✅ Second initialize request succeeded - FIX WORKING!"
else
    echo "❌ Second initialize request failed: $RESPONSE2"
fi

# Third initialize request to be sure
echo "3. Third initialize request:"
RESPONSE3=$(curl -s -X POST http://localhost:3000/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "Authorization: Bearer test123456789012345678901234567890" \
  -d '{
    "jsonrpc": "2.0",
    "id": 3,
    "method": "initialize",
    "params": {
      "protocolVersion": "2024-11-05",
      "capabilities": {
        "roots": {
          "listChanged": false
        }
      },
      "clientInfo": {
        "name": "test-client-3",
        "version": "1.0.0"
      }
    }
  }')

if echo "$RESPONSE3" | grep -q '"result"'; then
    echo "✅ Third initialize request succeeded"
else
    echo "❌ Third initialize request failed: $RESPONSE3"
fi

# Check health to see active transports
echo "4. Checking server health for active transports:"
HEALTH=$(curl -s -X GET http://localhost:3000/health)
echo "$HEALTH" | python3 -m json.tool

# Cleanup
echo "Stopping server..."
kill $SERVER_PID
wait $SERVER_PID 2>/dev/null

echo "Test completed!"
```

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

```typescript
import { ToolDocumentation } from '../types';

export const validateWorkflowExpressionsDoc: ToolDocumentation = {
  name: 'validate_workflow_expressions',
  category: 'validation',
  essentials: {
    description: 'Validate n8n expressions: syntax {{}}, variables ($json/$node), references. Returns errors with locations.',
    keyParameters: ['workflow'],
    example: 'validate_workflow_expressions({workflow: {nodes: [...], connections: {...}}})',
    performance: 'Fast (<100ms)',
    tips: [
      'Catches syntax errors in {{}} expressions before runtime',
      'Validates $json, $node, and other n8n variables',
      'Shows exact location of expression errors in node parameters'
    ]
  },
  full: {
    description: 'Validates all n8n expressions within a workflow for syntax correctness and reference validity. This tool scans all node parameters for n8n expressions (enclosed in {{}}), checks expression syntax, validates variable references like $json and $node("NodeName"), ensures referenced nodes exist in the workflow, and provides detailed error locations for debugging.',
    parameters: {
      workflow: { 
        type: 'object', 
        required: true, 
        description: 'The workflow JSON to check for expression errors.' 
      }
    },
    returns: 'Object with valid (boolean), errors (array with node ID, parameter path, and error details), and expression count',
    examples: [
      'validate_workflow_expressions({workflow: myWorkflow}) - Check all expressions',
      'validate_workflow_expressions({workflow: {nodes: [...], connections: {...}}}) - Validate expression syntax'
    ],
    useCases: [
      'Catch expression syntax errors before workflow execution',
      'Validate node references in $node() expressions exist',
      'Find typos in variable names like $json or $input',
      'Ensure complex expressions are properly formatted',
      'Debug expression errors with exact parameter locations',
      'Validate expressions after workflow modifications'
    ],
    performance: 'Fast (<100ms). Scans all string parameters in all nodes. Performance scales with workflow size and expression count.',
    bestPractices: [
      'Run after modifying any expressions in node parameters',
      'Check all $node() references when renaming nodes',
      'Validate expressions before workflow deployment',
      'Pay attention to nested object paths in expressions',
      'Use with validate_workflow for comprehensive validation'
    ],
    pitfalls: [
      'Cannot validate expression logic, only syntax',
      'Runtime data availability not checked (e.g., if $json.field exists)',
      'Complex JavaScript in expressions may need runtime testing',
      'Does not validate expression return types',
      'Some valid expressions may use advanced features not fully parsed'
    ],
    relatedTools: ['validate_workflow', 'validate_workflow_connections', 'validate_node_operation']
  }
};
```

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

```bash
#!/bin/bash
# Test script for optimized Docker build

set -e

echo "🧪 Testing Optimized Docker Build"
echo "================================="

# Colors for output
GREEN='\033[0;32m'
RED='\033[0;31m'
NC='\033[0m' # No Color

# Build the optimized image
echo -e "\n📦 Building optimized Docker image..."
docker build -f Dockerfile.optimized -t n8n-mcp:optimized .

# Check image size
echo -e "\n📊 Checking image size..."
SIZE=$(docker images n8n-mcp:optimized --format "{{.Size}}")
echo "Image size: $SIZE"

# Extract size in MB for comparison
SIZE_MB=$(docker images n8n-mcp:optimized --format "{{.Size}}" | sed 's/MB//' | sed 's/GB/*1024/' | bc 2>/dev/null || echo "0")
if [ "$SIZE_MB" != "0" ] && [ "$SIZE_MB" -lt "300" ]; then
    echo -e "${GREEN}✅ Image size is optimized (<300MB)${NC}"
else
    echo -e "${RED}⚠️  Image might be larger than expected${NC}"
fi

# Test stdio mode
echo -e "\n🔍 Testing stdio mode..."
TEST_RESULT=$(echo '{"jsonrpc":"2.0","method":"tools/list","id":1}' | \
    docker run --rm -i -e MCP_MODE=stdio n8n-mcp:optimized 2>/dev/null | \
    grep -o '"name":"[^"]*"' | head -1)

if [ -n "$TEST_RESULT" ]; then
    echo -e "${GREEN}✅ Stdio mode working${NC}"
else
    echo -e "${RED}❌ Stdio mode failed${NC}"
fi

# Test HTTP mode
echo -e "\n🌐 Testing HTTP mode..."
docker run -d --name test-optimized \
    -e MCP_MODE=http \
    -e AUTH_TOKEN=test-token \
    -p 3002:3000 \
    n8n-mcp:optimized

# Wait for startup
sleep 5

# Test health endpoint
HEALTH=$(curl -s http://localhost:3002/health | grep -o '"status":"healthy"' || echo "")
if [ -n "$HEALTH" ]; then
    echo -e "${GREEN}✅ Health endpoint working${NC}"
else
    echo -e "${RED}❌ Health endpoint failed${NC}"
fi

# Test MCP endpoint
MCP_TEST=$(curl -s -H "Authorization: Bearer test-token" \
    -H "Content-Type: application/json" \
    -d '{"jsonrpc":"2.0","method":"tools/list","id":1}' \
    http://localhost:3002/mcp | grep -o '"tools":\[' || echo "")

if [ -n "$MCP_TEST" ]; then
    echo -e "${GREEN}✅ MCP endpoint working${NC}"
else
    echo -e "${RED}❌ MCP endpoint failed${NC}"
fi

# Test database statistics tool
STATS_TEST=$(curl -s -H "Authorization: Bearer test-token" \
    -H "Content-Type: application/json" \
    -d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"get_database_statistics","arguments":{}},"id":2}' \
    http://localhost:3002/mcp | grep -o '"totalNodes":[0-9]*' || echo "")

if [ -n "$STATS_TEST" ]; then
    echo -e "${GREEN}✅ Database statistics tool working${NC}"
    echo "Database stats: $STATS_TEST"
else
    echo -e "${RED}❌ Database statistics tool failed${NC}"
fi

# Cleanup
docker stop test-optimized >/dev/null 2>&1
docker rm test-optimized >/dev/null 2>&1

# Compare with original image
echo -e "\n📊 Size Comparison:"
echo "Original image: $(docker images n8n-mcp:latest --format "{{.Size}}" 2>/dev/null || echo "Not built")"
echo "Optimized image: $SIZE"

echo -e "\n✨ Testing complete!"
```

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

```javascript
#!/usr/bin/env node

/**
 * Test MCP Server extraction functionality
 * Simulates an MCP client calling the get_node_source_code tool
 */

const { spawn } = require('child_process');
const path = require('path');

// MCP request to get AI Agent node source code
const mcpRequest = {
  jsonrpc: '2.0',
  id: 1,
  method: 'tools/call',
  params: {
    name: 'get_node_source_code',
    arguments: {
      nodeType: '@n8n/n8n-nodes-langchain.Agent',
      includeCredentials: true
    }
  }
};

async function testMCPExtraction() {
  console.log('=== MCP Server Node Extraction Test ===\n');
  console.log('Starting MCP server...');
  
  // Start the MCP server
  const serverPath = path.join(__dirname, '../dist/index.js');
  const server = spawn('node', [serverPath], {
    env: {
      ...process.env,
      MCP_SERVER_PORT: '3000',
      MCP_SERVER_HOST: '0.0.0.0',
      N8N_API_URL: 'http://n8n:5678',
      N8N_API_KEY: 'test-api-key',
      MCP_AUTH_TOKEN: 'test-token',
      LOG_LEVEL: 'info'
    },
    stdio: ['pipe', 'pipe', 'pipe']
  });

  let responseBuffer = '';
  let errorBuffer = '';

  server.stdout.on('data', (data) => {
    responseBuffer += data.toString();
  });

  server.stderr.on('data', (data) => {
    errorBuffer += data.toString();
  });

  // Give server time to start
  await new Promise(resolve => setTimeout(resolve, 2000));

  console.log('Sending MCP request...');
  console.log(JSON.stringify(mcpRequest, null, 2));
  
  // Send the request via stdin (MCP uses stdio transport)
  server.stdin.write(JSON.stringify(mcpRequest) + '\n');
  
  // Wait for response
  await new Promise(resolve => setTimeout(resolve, 3000));
  
  // Kill the server
  server.kill();
  
  console.log('\n=== Server Output ===');
  console.log(responseBuffer);
  
  if (errorBuffer) {
    console.log('\n=== Server Errors ===');
    console.log(errorBuffer);
  }
  
  // Try to parse any JSON responses
  const lines = responseBuffer.split('\n').filter(line => line.trim());
  for (const line of lines) {
    try {
      const data = JSON.parse(line);
      if (data.id === 1 && data.result) {
        console.log('\n✅ MCP Response received!');
        console.log(`Node type: ${data.result.nodeType}`);
        console.log(`Source code length: ${data.result.sourceCode ? data.result.sourceCode.length : 0} characters`);
        console.log(`Location: ${data.result.location}`);
        console.log(`Has credentials: ${data.result.credentialCode ? 'Yes' : 'No'}`);
        console.log(`Has package info: ${data.result.packageInfo ? 'Yes' : 'No'}`);
        
        if (data.result.sourceCode) {
          console.log('\nFirst 300 characters of extracted code:');
          console.log('='.repeat(60));
          console.log(data.result.sourceCode.substring(0, 300) + '...');
          console.log('='.repeat(60));
        }
      }
    } catch (e) {
      // Not JSON, skip
    }
  }
}

testMCPExtraction().catch(console.error);
```

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

```typescript
/**
 * Database Schema Coverage Audit Script
 *
 * Audits the database to determine how many nodes have complete schema information
 * for resourceLocator mode validation. This helps assess the coverage of our
 * schema-driven validation approach.
 */

import Database from 'better-sqlite3';
import path from 'path';

const dbPath = path.join(__dirname, '../data/nodes.db');
const db = new Database(dbPath, { readonly: true });

console.log('=== Schema Coverage Audit ===\n');

// Query 1: How many nodes have resourceLocator properties?
const totalResourceLocator = db.prepare(`
  SELECT COUNT(*) as count FROM nodes
  WHERE properties_schema LIKE '%resourceLocator%'
`).get() as { count: number };

console.log(`Nodes with resourceLocator properties: ${totalResourceLocator.count}`);

// Query 2: Of those, how many have modes defined?
const withModes = db.prepare(`
  SELECT COUNT(*) as count FROM nodes
  WHERE properties_schema LIKE '%resourceLocator%'
    AND properties_schema LIKE '%modes%'
`).get() as { count: number };

console.log(`Nodes with modes defined: ${withModes.count}`);

// Query 3: Which nodes have resourceLocator but NO modes?
const withoutModes = db.prepare(`
  SELECT node_type, display_name
  FROM nodes
  WHERE properties_schema LIKE '%resourceLocator%'
    AND properties_schema NOT LIKE '%modes%'
  LIMIT 10
`).all() as Array<{ node_type: string; display_name: string }>;

console.log(`\nSample nodes WITHOUT modes (showing 10):`);
withoutModes.forEach(node => {
  console.log(`  - ${node.display_name} (${node.node_type})`);
});

// Calculate coverage percentage
const coverage = totalResourceLocator.count > 0
  ? (withModes.count / totalResourceLocator.count) * 100
  : 0;

console.log(`\nSchema coverage: ${coverage.toFixed(1)}% of resourceLocator nodes have modes defined`);

// Query 4: Get some examples of nodes WITH modes for verification
console.log('\nSample nodes WITH modes (showing 5):');
const withModesExamples = db.prepare(`
  SELECT node_type, display_name
  FROM nodes
  WHERE properties_schema LIKE '%resourceLocator%'
    AND properties_schema LIKE '%modes%'
  LIMIT 5
`).all() as Array<{ node_type: string; display_name: string }>;

withModesExamples.forEach(node => {
  console.log(`  - ${node.display_name} (${node.node_type})`);
});

// Summary
console.log('\n=== Summary ===');
console.log(`Total nodes in database: ${db.prepare('SELECT COUNT(*) as count FROM nodes').get() as any as { count: number }.count}`);
console.log(`Nodes with resourceLocator: ${totalResourceLocator.count}`);
console.log(`Nodes with complete mode schemas: ${withModes.count}`);
console.log(`Nodes without mode schemas: ${totalResourceLocator.count - withModes.count}`);
console.log(`\nImplication: Schema-driven validation will apply to ${withModes.count} nodes.`);
console.log(`For the remaining ${totalResourceLocator.count - withModes.count} nodes, validation will be skipped (graceful degradation).`);

db.close();

```

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

```typescript
#!/usr/bin/env npx tsx

import { createDatabaseAdapter } from '../database/database-adapter';
import { NodeRepository } from '../database/node-repository';
import { NodeSimilarityService } from '../services/node-similarity-service';
import path from 'path';

async function debugHttpSearch() {
  const dbPath = path.join(process.cwd(), 'data/nodes.db');
  const db = await createDatabaseAdapter(dbPath);
  const repository = new NodeRepository(db);
  const service = new NodeSimilarityService(repository);

  console.log('Testing "http" search...\n');

  // Check if httpRequest exists
  const httpNode = repository.getNode('nodes-base.httpRequest');
  console.log('HTTP Request node exists:', httpNode ? 'Yes' : 'No');
  if (httpNode) {
    console.log('  Display name:', httpNode.displayName);
  }

  // Test the search with internal details
  const suggestions = await service.findSimilarNodes('http', 5);
  console.log('\nSuggestions for "http":', suggestions.length);
  suggestions.forEach(s => {
    console.log(`  - ${s.nodeType} (${Math.round(s.confidence * 100)}%)`);
  });

  // Manually calculate score for httpRequest
  console.log('\nManual score calculation for httpRequest:');
  const testNode = {
    nodeType: 'nodes-base.httpRequest',
    displayName: 'HTTP Request',
    category: 'Core Nodes'
  };

  const cleanInvalid = 'http';
  const cleanValid = 'nodesbasehttprequest';
  const displayNameClean = 'httprequest';

  // Check substring
  const hasSubstring = cleanValid.includes(cleanInvalid) || displayNameClean.includes(cleanInvalid);
  console.log(`  Substring match: ${hasSubstring}`);

  // This should give us pattern match score
  const patternScore = hasSubstring ? 35 : 0; // Using 35 for short searches
  console.log(`  Pattern score: ${patternScore}`);

  // Name similarity would be low
  console.log(`  Total score would need to be >= 50 to appear`);

  // Get all nodes and check which ones contain 'http'
  const allNodes = repository.getAllNodes();
  const httpNodes = allNodes.filter(n =>
    n.nodeType.toLowerCase().includes('http') ||
    (n.displayName && n.displayName.toLowerCase().includes('http'))
  );

  console.log('\n\nNodes containing "http" in name:');
  httpNodes.slice(0, 5).forEach(n => {
    console.log(`  - ${n.nodeType} (${n.displayName})`);

    // Calculate score for this node
    const normalizedSearch = 'http';
    const normalizedType = n.nodeType.toLowerCase().replace(/[^a-z0-9]/g, '');
    const normalizedDisplay = (n.displayName || '').toLowerCase().replace(/[^a-z0-9]/g, '');

    const containsInType = normalizedType.includes(normalizedSearch);
    const containsInDisplay = normalizedDisplay.includes(normalizedSearch);

    console.log(`    Type check: "${normalizedType}" includes "${normalizedSearch}" = ${containsInType}`);
    console.log(`    Display check: "${normalizedDisplay}" includes "${normalizedSearch}" = ${containsInDisplay}`);
  });
}

debugHttpSearch().catch(console.error);
```
Page 2/46FirstPrevNextLast