#
tokens: 49677/50000 41/630 files (page 4/48)
lines: off (toggle) GitHub
raw markdown copy
This is page 4 of 48. 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
│   ├── MULTI_APP_INTEGRATION.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
│   │   ├── session-restoration-warmstart.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

--------------------------------------------------------------------------------
/src/utils/logger.ts:
--------------------------------------------------------------------------------

```typescript
export enum LogLevel {
  ERROR = 0,
  WARN = 1,
  INFO = 2,
  DEBUG = 3,
}

export interface LoggerConfig {
  level: LogLevel;
  prefix?: string;
  timestamp?: boolean;
}

export class Logger {
  private config: LoggerConfig;
  private static instance: Logger;
  private useFileLogging = false;
  private fileStream: any = null;
  // Cache environment variables for performance
  private readonly isStdio = process.env.MCP_MODE === 'stdio';
  private readonly isDisabled = process.env.DISABLE_CONSOLE_OUTPUT === 'true';
  private readonly isHttp = process.env.MCP_MODE === 'http';
  private readonly isTest = process.env.NODE_ENV === 'test' || process.env.TEST_ENVIRONMENT === 'true';

  constructor(config?: Partial<LoggerConfig>) {
    this.config = {
      level: LogLevel.INFO,
      prefix: 'n8n-mcp',
      timestamp: true,
      ...config,
    };
  }

  static getInstance(config?: Partial<LoggerConfig>): Logger {
    if (!Logger.instance) {
      Logger.instance = new Logger(config);
    }
    return Logger.instance;
  }

  private formatMessage(level: string, message: string): string {
    const parts: string[] = [];
    
    if (this.config.timestamp) {
      parts.push(`[${new Date().toISOString()}]`);
    }
    
    if (this.config.prefix) {
      parts.push(`[${this.config.prefix}]`);
    }
    
    parts.push(`[${level}]`);
    parts.push(message);
    
    return parts.join(' ');
  }

  private log(level: LogLevel, levelName: string, message: string, ...args: any[]): void {
    // Allow ERROR level logs through in more cases for debugging
    const allowErrorLogs = level === LogLevel.ERROR && (this.isHttp || process.env.DEBUG === 'true');
    
    // Check environment variables FIRST, before level check
    // In stdio mode, suppress ALL console output to avoid corrupting JSON-RPC (except errors when debugging)
    // Also suppress in test mode unless debug is explicitly enabled
    if (this.isStdio || this.isDisabled || (this.isTest && process.env.DEBUG !== 'true')) {
      // Allow error logs through if debugging is enabled
      if (!allowErrorLogs) {
        return;
      }
    }
    
    if (level <= this.config.level || allowErrorLogs) {
      const formattedMessage = this.formatMessage(levelName, message);
      
      // In HTTP mode during request handling, suppress console output (except errors)
      // The ConsoleManager will handle this, but we add a safety check
      if (this.isHttp && process.env.MCP_REQUEST_ACTIVE === 'true' && !allowErrorLogs) {
        // Silently drop the log during active MCP requests (except errors)
        return;
      }
      
      switch (level) {
        case LogLevel.ERROR:
          console.error(formattedMessage, ...args);
          break;
        case LogLevel.WARN:
          console.warn(formattedMessage, ...args);
          break;
        default:
          console.log(formattedMessage, ...args);
      }
    }
  }

  error(message: string, ...args: any[]): void {
    this.log(LogLevel.ERROR, 'ERROR', message, ...args);
  }

  warn(message: string, ...args: any[]): void {
    this.log(LogLevel.WARN, 'WARN', message, ...args);
  }

  info(message: string, ...args: any[]): void {
    this.log(LogLevel.INFO, 'INFO', message, ...args);
  }

  debug(message: string, ...args: any[]): void {
    this.log(LogLevel.DEBUG, 'DEBUG', message, ...args);
  }

  setLevel(level: LogLevel): void {
    this.config.level = level;
  }

  static parseLogLevel(level: string): LogLevel {
    switch (level.toLowerCase()) {
      case 'error':
        return LogLevel.ERROR;
      case 'warn':
        return LogLevel.WARN;
      case 'debug':
        return LogLevel.DEBUG;
      case 'info':
      default:
        return LogLevel.INFO;
    }
  }
}

// Create a default logger instance
export const logger = Logger.getInstance({
  level: Logger.parseLogLevel(process.env.LOG_LEVEL || 'info'),
});
```

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

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

export const getNodeEssentialsDoc: ToolDocumentation = {
  name: 'get_node_essentials',
  category: 'configuration',
  essentials: {
    description: 'Returns only the most commonly-used properties for a node (10-20 fields). Response is 95% smaller than get_node_info (5KB vs 100KB+). Essential properties include required fields, common options, and authentication settings. Use validate_node_operation for working configurations.',
    keyParameters: ['nodeType'],
    example: 'get_node_essentials({nodeType: "nodes-base.slack"})',
    performance: '<10ms, ~5KB response',
    tips: [
      'Always use this before get_node_info',
      'Use validate_node_operation for examples',
      'Perfect for understanding node structure'
    ]
  },
  full: {
    description: 'Returns a curated subset of node properties focusing on the most commonly-used fields. Essential properties are hand-picked for each node type and include: required fields, primary operations, authentication options, and the most frequent configuration patterns. NOTE: Examples have been removed to avoid confusion - use validate_node_operation to get working configurations with proper validation.',
    parameters: {
      nodeType: { type: 'string', description: 'Full node type with prefix, e.g., "nodes-base.slack", "nodes-base.httpRequest"', required: true }
    },
    returns: `Object containing:
{
  "nodeType": "nodes-base.slack",
  "displayName": "Slack",
  "description": "Consume Slack API",
  "category": "output",
  "version": "2.3",
  "requiredProperties": [],  // Most nodes have no strictly required fields
  "commonProperties": [
    {
      "name": "resource",
      "displayName": "Resource",
      "type": "options",
      "options": ["channel", "message", "user"],
      "default": "message"
    },
    {
      "name": "operation",
      "displayName": "Operation", 
      "type": "options",
      "options": ["post", "update", "delete"],
      "default": "post"
    },
    // ... 10-20 most common properties
  ],
  "operations": [
    {"name": "Post", "description": "Post a message"},
    {"name": "Update", "description": "Update a message"}
  ],
  "metadata": {
    "totalProperties": 121,
    "isAITool": false,
    "hasCredentials": true
  }
}`,
    examples: [
      'get_node_essentials({nodeType: "nodes-base.httpRequest"}) - HTTP configuration basics',
      'get_node_essentials({nodeType: "nodes-base.slack"}) - Slack messaging essentials',
      'get_node_essentials({nodeType: "nodes-base.googleSheets"}) - Sheets operations',
      '// Workflow: search → essentials → validate',
      'const nodes = search_nodes({query: "database"});',
      'const mysql = get_node_essentials({nodeType: "nodes-base.mySql"});',
      'validate_node_operation("nodes-base.mySql", {operation: "select"}, "minimal");'
    ],
    useCases: [
      'Quickly understand node structure without information overload',
      'Identify which properties are most important',
      'Learn node basics before diving into advanced features',
      'Build workflows faster with curated property sets'
    ],
    performance: '<10ms response time, ~5KB payload (vs 100KB+ for full schema)',
    bestPractices: [
      'Always start with essentials, only use get_node_info if needed',
      'Use validate_node_operation to get working configurations',
      'Check authentication requirements first',
      'Use search_node_properties if specific property not in essentials'
    ],
    pitfalls: [
      'Advanced properties not included - use get_node_info for complete schema',
      'Node-specific validators may require additional fields',
      'Some nodes have 50+ properties, essentials shows only top 10-20'
    ],
    relatedTools: ['get_node_info for complete schema', 'search_node_properties for finding specific fields', 'validate_node_minimal to check configuration']
  }
};
```

--------------------------------------------------------------------------------
/scripts/test-http.sh:
--------------------------------------------------------------------------------

```bash
#!/bin/bash
# Test script for n8n-MCP HTTP Server

set -e

# Configuration
URL="${1:-http://localhost:3000}"
TOKEN="${AUTH_TOKEN:-test-token}"
VERBOSE="${VERBOSE:-0}"

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

echo "🧪 Testing n8n-MCP HTTP Server"
echo "================================"
echo "Server URL: $URL"
echo ""

# Check if jq is installed
if ! command -v jq &> /dev/null; then
    echo -e "${YELLOW}Warning: jq not installed. Output will not be formatted.${NC}"
    echo "Install with: brew install jq (macOS) or apt-get install jq (Linux)"
    echo ""
    JQ="cat"
else
    JQ="jq ."
fi

# Function to make requests
make_request() {
    local method="$1"
    local endpoint="$2"
    local data="$3"
    local headers="$4"
    local expected_status="$5"
    
    if [ "$VERBOSE" = "1" ]; then
        echo -e "${YELLOW}Request:${NC} $method $URL$endpoint"
        [ -n "$data" ] && echo -e "${YELLOW}Data:${NC} $data"
    fi
    
    # Build curl command
    local cmd="curl -s -w '\n%{http_code}' -X $method '$URL$endpoint'"
    [ -n "$headers" ] && cmd="$cmd $headers"
    [ -n "$data" ] && cmd="$cmd -d '$data'"
    
    # Execute and capture response
    local response=$(eval "$cmd")
    local body=$(echo "$response" | sed '$d')
    local status=$(echo "$response" | tail -n 1)
    
    # Check status
    if [ "$status" = "$expected_status" ]; then
        echo -e "${GREEN}✓${NC} $method $endpoint - Status: $status"
    else
        echo -e "${RED}✗${NC} $method $endpoint - Expected: $expected_status, Got: $status"
    fi
    
    # Show response body
    if [ -n "$body" ]; then
        echo "$body" | $JQ
    fi
    echo ""
}

# Test 1: Health check
echo "1. Testing health endpoint..."
make_request "GET" "/health" "" "" "200"

# Test 2: OPTIONS request (CORS preflight)
echo "2. Testing CORS preflight..."
make_request "OPTIONS" "/mcp" "" "-H 'Origin: http://localhost' -H 'Access-Control-Request-Method: POST'" "204"

# Test 3: Authentication failure
echo "3. Testing authentication (should fail)..."
make_request "POST" "/mcp" \
    '{"jsonrpc":"2.0","method":"tools/list","id":1}' \
    "-H 'Content-Type: application/json' -H 'Authorization: Bearer wrong-token'" \
    "401"

# Test 4: Missing authentication
echo "4. Testing missing authentication..."
make_request "POST" "/mcp" \
    '{"jsonrpc":"2.0","method":"tools/list","id":1}' \
    "-H 'Content-Type: application/json'" \
    "401"

# Test 5: Valid MCP request to list tools
echo "5. Testing valid MCP request (list tools)..."
make_request "POST" "/mcp" \
    '{"jsonrpc":"2.0","method":"tools/list","id":1}' \
    "-H 'Content-Type: application/json' -H 'Authorization: Bearer $TOKEN' -H 'Accept: application/json, text/event-stream'" \
    "200"

# Test 6: 404 for unknown endpoint
echo "6. Testing 404 response..."
make_request "GET" "/unknown" "" "" "404"

# Test 7: Invalid JSON
echo "7. Testing invalid JSON..."
make_request "POST" "/mcp" \
    '{invalid json}' \
    "-H 'Content-Type: application/json' -H 'Authorization: Bearer $TOKEN'" \
    "400"

# Test 8: Request size limit
echo "8. Testing request size limit..."
# Use a different approach for large data
echo "Skipping large payload test (would exceed bash limits)"

# Test 9: MCP initialization
if [ "$VERBOSE" = "1" ]; then
    echo "9. Testing MCP initialization..."
    make_request "POST" "/mcp" \
        '{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{"roots":{}}},"id":1}' \
        "-H 'Content-Type: application/json' -H 'Authorization: Bearer $TOKEN' -H 'Accept: text/event-stream'" \
        "200"
fi

echo "================================"
echo "🎉 Tests completed!"
echo ""
echo "To run with verbose output: VERBOSE=1 $0"
echo "To test a different server: $0 https://your-server.com"
echo "To use a different token: AUTH_TOKEN=your-token $0"
```

--------------------------------------------------------------------------------
/tests/unit/services/debug-validator.test.ts:
--------------------------------------------------------------------------------

```typescript
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { WorkflowValidator } from '@/services/workflow-validator';

// Mock dependencies - don't use vi.mock for complex mocks
vi.mock('@/services/expression-validator', () => ({
  ExpressionValidator: {
    validateNodeExpressions: () => ({
      valid: true,
      errors: [],
      warnings: [],
      variables: [],
      expressions: []
    })
  }
}));
vi.mock('@/utils/logger', () => ({
  Logger: vi.fn().mockImplementation(() => ({
    error: vi.fn(),
    warn: vi.fn(),
    info: vi.fn(),
    debug: vi.fn()
  }))
}));

describe('Debug Validator Tests', () => {
  let validator: WorkflowValidator;
  let mockNodeRepository: any;
  let mockEnhancedConfigValidator: any;

  beforeEach(() => {
    // Create mock repository
    mockNodeRepository = {
      getNode: (nodeType: string) => {
        // Handle both n8n-nodes-base.set and nodes-base.set (normalized)
        if (nodeType === 'n8n-nodes-base.set' || nodeType === 'nodes-base.set') {
          return {
            name: 'Set',
            type: 'nodes-base.set',
            typeVersion: 1,
            properties: [],
            package: 'n8n-nodes-base',
            version: 1,
            displayName: 'Set'
          };
        }
        return null;
      }
    };
    
    // Create mock EnhancedConfigValidator
    mockEnhancedConfigValidator = {
      validateWithMode: () => ({
        valid: true,
        errors: [],
        warnings: [],
        suggestions: [],
        mode: 'operation',
        visibleProperties: [],
        hiddenProperties: []
      })
    };
    
    // Create validator instance
    validator = new WorkflowValidator(mockNodeRepository, mockEnhancedConfigValidator as any);
  });

  it('should handle nodes at extreme positions - debug', async () => {
    const workflow = {
      nodes: [
        { id: '1', name: 'FarLeft', type: 'n8n-nodes-base.set', position: [-999999, -999999] as [number, number], parameters: {} },
        { id: '2', name: 'FarRight', type: 'n8n-nodes-base.set', position: [999999, 999999] as [number, number], parameters: {} },
        { id: '3', name: 'Zero', type: 'n8n-nodes-base.set', position: [0, 0] as [number, number], parameters: {} }
      ],
      connections: {
        'FarLeft': {
          main: [[{ node: 'FarRight', type: 'main', index: 0 }]]
        },
        'FarRight': {
          main: [[{ node: 'Zero', type: 'main', index: 0 }]]
        }
      }
    };
    
    const result = await validator.validateWorkflow(workflow);
    
    
    // Test should pass with extreme positions
    expect(result.valid).toBe(true);
    expect(result.errors).toHaveLength(0);
  });

  it('should handle special characters in node names - debug', async () => {
    const workflow = {
      nodes: [
        { id: '1', name: 'Node@#$%', type: 'n8n-nodes-base.set', position: [0, 0] as [number, number], parameters: {} },
        { id: '2', name: 'Node 中文', type: 'n8n-nodes-base.set', position: [100, 0] as [number, number], parameters: {} },
        { id: '3', name: 'Node😊', type: 'n8n-nodes-base.set', position: [200, 0] as [number, number], parameters: {} }
      ],
      connections: {
        'Node@#$%': {
          main: [[{ node: 'Node 中文', type: 'main', index: 0 }]]
        },
        'Node 中文': {
          main: [[{ node: 'Node😊', type: 'main', index: 0 }]]
        }
      }
    };
    
    const result = await validator.validateWorkflow(workflow);
    
    
    // Test should pass with special characters in node names
    expect(result.valid).toBe(true);
    expect(result.errors).toHaveLength(0);
  });

  it('should handle non-array nodes - debug', async () => {
    const workflow = {
      nodes: 'not-an-array',
      connections: {}
    };
    const result = await validator.validateWorkflow(workflow as any);
    
    
    expect(result.valid).toBe(false);
    expect(result.errors[0].message).toContain('nodes must be an array');
  });
});
```

--------------------------------------------------------------------------------
/src/mcp/tool-docs/configuration/search-node-properties.ts:
--------------------------------------------------------------------------------

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

export const searchNodePropertiesDoc: ToolDocumentation = {
  name: 'search_node_properties',
  category: 'configuration',
  essentials: {
    description: 'Find specific properties in a node without downloading all 200+ properties.',
    keyParameters: ['nodeType', 'query'],
    example: 'search_node_properties({nodeType: "nodes-base.httpRequest", query: "auth"})',
    performance: 'Fast - searches indexed properties',
    tips: [
      'Search for "auth", "header", "body", "json", "credential"',
      'Returns property paths and descriptions',
      'Much faster than get_node_info for finding specific fields'
    ]
  },
  full: {
    description: `Searches for specific properties within a node's configuration schema. Essential for finding authentication fields, headers, body parameters, or any specific property without downloading the entire node schema (which can be 100KB+). Returns matching properties with their paths, types, and descriptions.`,
    parameters: {
      nodeType: {
        type: 'string',
        required: true,
        description: 'Full type with prefix',
        examples: [
          'nodes-base.httpRequest',
          'nodes-base.slack',
          'nodes-base.postgres',
          'nodes-base.googleSheets'
        ]
      },
      query: {
        type: 'string',
        required: true,
        description: 'Property to find: "auth", "header", "body", "json"',
        examples: [
          'auth',
          'header',
          'body',
          'json',
          'credential',
          'timeout',
          'retry',
          'pagination'
        ]
      },
      maxResults: {
        type: 'number',
        required: false,
        description: 'Max results (default 20)',
        default: 20
      }
    },
    returns: `Object containing:
- nodeType: The searched node type
- query: Your search term
- matches: Array of matching properties with:
  - name: Property identifier
  - displayName: Human-readable name
  - type: Property type (string, number, options, etc.)
  - description: Property description
  - path: Full path to property (for nested properties)
  - required: Whether property is required
  - default: Default value if any
  - options: Available options for selection properties
  - showWhen: Visibility conditions
- totalMatches: Number of matches found
- searchedIn: Total properties searched`,
    examples: [
      'search_node_properties({nodeType: "nodes-base.httpRequest", query: "auth"}) - Find authentication fields',
      'search_node_properties({nodeType: "nodes-base.slack", query: "channel"}) - Find channel-related properties',
      'search_node_properties({nodeType: "nodes-base.postgres", query: "query"}) - Find query fields',
      'search_node_properties({nodeType: "nodes-base.webhook", query: "response"}) - Find response options'
    ],
    useCases: [
      'Finding authentication/credential fields quickly',
      'Locating specific parameters without full node info',
      'Discovering header or body configuration options',
      'Finding nested properties in complex nodes',
      'Checking if a node supports specific features (retry, pagination, etc.)'
    ],
    performance: 'Very fast - searches pre-indexed property metadata',
    bestPractices: [
      'Use before get_node_info to find specific properties',
      'Search for common terms: auth, header, body, credential',
      'Check showWhen conditions to understand visibility',
      'Use with get_property_dependencies for complete understanding',
      'Limit results if you only need to check existence'
    ],
    pitfalls: [
      'Some properties may be hidden due to visibility conditions',
      'Property names may differ from display names',
      'Nested properties show full path (e.g., "options.retry.limit")',
      'Search is case-sensitive for property names'
    ],
    relatedTools: ['get_node_essentials', 'get_property_dependencies', 'get_node_info']
  }
};
```

--------------------------------------------------------------------------------
/scripts/vitest-benchmark-json-reporter.js:
--------------------------------------------------------------------------------

```javascript
const { writeFileSync } = require('fs');
const { resolve } = require('path');

class BenchmarkJsonReporter {
  constructor() {
    this.results = [];
    console.log('[BenchmarkJsonReporter] Initialized');
  }

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

  onCollected(files) {
    console.log('[BenchmarkJsonReporter] onCollected called with', files ? files.length : 0, 'files');
  }

  onTaskUpdate(tasks) {
    console.log('[BenchmarkJsonReporter] onTaskUpdate called');
  }

  onBenchmarkResult(file, benchmark) {
    console.log('[BenchmarkJsonReporter] onBenchmarkResult called for', benchmark.name);
  }

  onFinished(files, errors) {
    console.log('[BenchmarkJsonReporter] onFinished called with', files ? files.length : 0, 'files');
    
    const results = {
      timestamp: new Date().toISOString(),
      files: []
    };

    try {
      for (const file of files || []) {
        if (!file) continue;
        
        const fileResult = {
          filepath: file.filepath || file.name || 'unknown',
          groups: []
        };

        // Handle both file.tasks and file.benchmarks
        const tasks = file.tasks || file.benchmarks || [];
        
        // Process tasks/benchmarks
        for (const task of tasks) {
          if (task.type === 'suite' && task.tasks) {
            // This is a suite containing benchmarks
            const group = {
              name: task.name,
              benchmarks: []
            };

            for (const benchmark of task.tasks) {
              if (benchmark.result?.benchmark) {
                group.benchmarks.push({
                  name: benchmark.name,
                  result: {
                    mean: benchmark.result.benchmark.mean,
                    min: benchmark.result.benchmark.min,
                    max: benchmark.result.benchmark.max,
                    hz: benchmark.result.benchmark.hz,
                    p75: benchmark.result.benchmark.p75,
                    p99: benchmark.result.benchmark.p99,
                    p995: benchmark.result.benchmark.p995,
                    p999: benchmark.result.benchmark.p999,
                    rme: benchmark.result.benchmark.rme,
                    samples: benchmark.result.benchmark.samples
                  }
                });
              }
            }

            if (group.benchmarks.length > 0) {
              fileResult.groups.push(group);
            }
          } else if (task.result?.benchmark) {
            // This is a direct benchmark (not in a suite)
            if (!fileResult.groups.length) {
              fileResult.groups.push({
                name: 'Default',
                benchmarks: []
              });
            }
            
            fileResult.groups[0].benchmarks.push({
              name: task.name,
              result: {
                mean: task.result.benchmark.mean,
                min: task.result.benchmark.min,
                max: task.result.benchmark.max,
                hz: task.result.benchmark.hz,
                p75: task.result.benchmark.p75,
                p99: task.result.benchmark.p99,
                p995: task.result.benchmark.p995,
                p999: task.result.benchmark.p999,
                rme: task.result.benchmark.rme,
                samples: task.result.benchmark.samples
              }
            });
          }
        }

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

      // Write results
      const outputPath = resolve(process.cwd(), 'benchmark-results.json');
      writeFileSync(outputPath, JSON.stringify(results, null, 2));
      console.log(`[BenchmarkJsonReporter] Benchmark results written to ${outputPath}`);
      console.log(`[BenchmarkJsonReporter] Total files processed: ${results.files.length}`);
    } catch (error) {
      console.error('[BenchmarkJsonReporter] Error writing results:', error);
    }
  }
}

module.exports = BenchmarkJsonReporter;
```

--------------------------------------------------------------------------------
/scripts/migrate-tool-docs.ts:
--------------------------------------------------------------------------------

```typescript
#!/usr/bin/env tsx
import * as fs from 'fs';
import * as path from 'path';

// This is a helper script to migrate tool documentation to the new structure
// It creates a template file for each tool that needs to be migrated

const toolsByCategory = {
  discovery: [
    'search_nodes',
    'list_nodes', 
    'list_ai_tools',
    'get_database_statistics'
  ],
  configuration: [
    'get_node_info',
    'get_node_essentials',
    'get_node_documentation',
    'search_node_properties',
    'get_node_as_tool_info',
    'get_property_dependencies'
  ],
  validation: [
    'validate_node_minimal',
    'validate_node_operation',
    'validate_workflow',
    'validate_workflow_connections',
    'validate_workflow_expressions'
  ],
  templates: [
    'get_node_for_task',
    'list_tasks',
    'list_node_templates',
    'get_template',
    'search_templates',
    'get_templates_for_task'
  ],
  workflow_management: [
    'n8n_create_workflow',
    'n8n_get_workflow',
    'n8n_get_workflow_details',
    'n8n_get_workflow_structure',
    'n8n_get_workflow_minimal',
    'n8n_update_full_workflow',
    'n8n_update_partial_workflow',
    'n8n_delete_workflow',
    'n8n_list_workflows',
    'n8n_validate_workflow',
    'n8n_trigger_webhook_workflow',
    'n8n_get_execution',
    'n8n_list_executions',
    'n8n_delete_execution'
  ],
  system: [
    'tools_documentation',
    'n8n_diagnostic',
    'n8n_health_check',
    'n8n_list_available_tools'
  ],
  special: [
    'code_node_guide'
  ]
};

const template = (toolName: string, category: string) => `import { ToolDocumentation } from '../types';

export const ${toCamelCase(toolName)}Doc: ToolDocumentation = {
  name: '${toolName}',
  category: '${category}',
  essentials: {
    description: 'TODO: Add description from old file',
    keyParameters: ['TODO'],
    example: '${toolName}({TODO})',
    performance: 'TODO',
    tips: [
      'TODO: Add tips'
    ]
  },
  full: {
    description: 'TODO: Add full description',
    parameters: {
      // TODO: Add parameters
    },
    returns: 'TODO: Add return description',
    examples: [
      '${toolName}({TODO}) - TODO'
    ],
    useCases: [
      'TODO: Add use cases'
    ],
    performance: 'TODO: Add performance description',
    bestPractices: [
      'TODO: Add best practices'
    ],
    pitfalls: [
      'TODO: Add pitfalls'
    ],
    relatedTools: ['TODO']
  }
};`;

function toCamelCase(str: string): string {
  return str.split('_').map((part, index) => 
    index === 0 ? part : part.charAt(0).toUpperCase() + part.slice(1)
  ).join('');
}

function toKebabCase(str: string): string {
  return str.replace(/_/g, '-');
}

// Create template files for tools that don't exist yet
Object.entries(toolsByCategory).forEach(([category, tools]) => {
  tools.forEach(toolName => {
    const fileName = toKebabCase(toolName) + '.ts';
    const filePath = path.join('src/mcp/tool-docs', category, fileName);
    
    // Skip if file already exists
    if (fs.existsSync(filePath)) {
      console.log(`✓ ${filePath} already exists`);
      return;
    }
    
    // Create the file with template
    fs.writeFileSync(filePath, template(toolName, category));
    console.log(`✨ Created ${filePath}`);
  });
  
  // Create index file for the category
  const indexPath = path.join('src/mcp/tool-docs', category, 'index.ts');
  if (!fs.existsSync(indexPath)) {
    const indexContent = tools.map(toolName => 
      `export { ${toCamelCase(toolName)}Doc } from './${toKebabCase(toolName)}';`
    ).join('\n');
    
    fs.writeFileSync(indexPath, indexContent);
    console.log(`✨ Created ${indexPath}`);
  }
});

console.log('\n📝 Migration templates created!');
console.log('Next steps:');
console.log('1. Copy documentation from the old tools-documentation.ts file');
console.log('2. Update each template file with the actual documentation');
console.log('3. Update src/mcp/tool-docs/index.ts to import all tools');
console.log('4. Replace the old tools-documentation.ts with the new one');
```

--------------------------------------------------------------------------------
/tests/docker-tests-README.md:
--------------------------------------------------------------------------------

```markdown
# Docker Config File Support Tests

This directory contains comprehensive tests for the Docker config file support feature added to n8n-mcp.

## Test Structure

### Unit Tests (`tests/unit/docker/`)

1. **parse-config.test.ts** - Tests for the JSON config parser
   - Basic JSON parsing functionality
   - Environment variable precedence
   - Shell escaping and quoting
   - Nested object flattening
   - Error handling for invalid JSON

2. **serve-command.test.ts** - Tests for "n8n-mcp serve" command
   - Command transformation logic
   - Argument preservation
   - Integration with config loading
   - Backwards compatibility

3. **config-security.test.ts** - Security-focused tests
   - Command injection prevention
   - Shell metacharacter handling
   - Path traversal protection
   - Polyglot payload defense
   - Real-world attack scenarios

4. **edge-cases.test.ts** - Edge case and stress tests
   - JavaScript number edge cases
   - Unicode handling
   - Deep nesting performance
   - Large config files
   - Invalid data types

### Integration Tests (`tests/integration/docker/`)

1. **docker-config.test.ts** - Full Docker container tests with config files
   - Config file loading and parsing
   - Environment variable precedence
   - Security in container context
   - Complex configuration scenarios

2. **docker-entrypoint.test.ts** - Docker entrypoint script tests
   - MCP mode handling
   - Database initialization
   - Permission management
   - Signal handling
   - Authentication validation

## Running the Tests

### Prerequisites
- Node.js and npm installed
- Docker installed (for integration tests)
- Build the project first: `npm run build`

### Commands

```bash
# Run all Docker config tests
npm run test:docker

# Run only unit tests (no Docker required)
npm run test:docker:unit

# Run only integration tests (requires Docker)
npm run test:docker:integration

# Run security-focused tests
npm run test:docker:security

# Run with coverage
./scripts/test-docker-config.sh coverage
```

### Individual test files
```bash
# Run a specific test file
npm test -- tests/unit/docker/parse-config.test.ts

# Run with watch mode
npm run test:watch -- tests/unit/docker/

# Run with coverage
npm run test:coverage -- tests/unit/docker/config-security.test.ts
```

## Test Coverage

The tests cover:

1. **Functionality**
   - JSON parsing and environment variable conversion
   - Nested object flattening with underscore separation
   - Environment variable precedence (env vars override config)
   - "n8n-mcp serve" command auto-enables HTTP mode

2. **Security**
   - Command injection prevention through proper shell escaping
   - Protection against malicious config values
   - Safe handling of special characters and Unicode
   - Prevention of path traversal attacks

3. **Edge Cases**
   - Invalid JSON handling
   - Missing config files
   - Permission errors
   - Very large config files
   - Deep nesting performance

4. **Integration**
   - Full Docker container behavior
   - Database initialization with file locking
   - Permission handling (root vs nodejs user)
   - Signal propagation and process management

## CI/CD Considerations

Integration tests are skipped by default unless:
- Running in CI (CI=true environment variable)
- Explicitly enabled (RUN_DOCKER_TESTS=true)

This prevents test failures on developer machines without Docker.

## Security Notes

The config parser implements defense in depth:
1. All values are wrapped in single quotes for shell safety
2. Single quotes within values are escaped as '"'"'
3. No variable expansion occurs within single quotes
4. Arrays and null values are ignored (not exported)
5. The parser exits silently on any error to prevent container startup issues

## Troubleshooting

If tests fail:
1. Ensure Docker is running (for integration tests)
2. Check that the project is built (`npm run build`)
3. Verify no containers are left running: `docker ps -a | grep n8n-mcp-test`
4. Clean up test containers: `docker rm $(docker ps -aq -f name=n8n-mcp-test)`
```

--------------------------------------------------------------------------------
/tests/integration/n8n-api/workflows/get-workflow.test.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Integration Tests: handleGetWorkflow
 *
 * Tests workflow retrieval against a real n8n instance.
 * Covers successful retrieval and error handling.
 */

import { describe, it, expect, beforeEach, afterEach, afterAll } from 'vitest';
import { createTestContext, TestContext, createTestWorkflowName } from '../utils/test-context';
import { getTestN8nClient } from '../utils/n8n-client';
import { N8nApiClient } from '../../../../src/services/n8n-api-client';
import { Workflow } from '../../../../src/types/n8n-api';
import { SIMPLE_WEBHOOK_WORKFLOW } from '../utils/fixtures';
import { cleanupOrphanedWorkflows } from '../utils/cleanup-helpers';
import { createMcpContext } from '../utils/mcp-context';
import { InstanceContext } from '../../../../src/types/instance-context';
import { handleGetWorkflow } from '../../../../src/mcp/handlers-n8n-manager';

describe('Integration: handleGetWorkflow', () => {
  let context: TestContext;
  let client: N8nApiClient;
  let mcpContext: InstanceContext;

  beforeEach(() => {
    context = createTestContext();
    client = getTestN8nClient();
    mcpContext = createMcpContext();
  });

  afterEach(async () => {
    await context.cleanup();
  });

  afterAll(async () => {
    if (!process.env.CI) {
      await cleanupOrphanedWorkflows();
    }
  });

  // ======================================================================
  // Successful Retrieval
  // ======================================================================

  describe('Successful Retrieval', () => {
    it('should retrieve complete workflow data', async () => {
      // Create a workflow first
      const workflow = {
        ...SIMPLE_WEBHOOK_WORKFLOW,
        name: createTestWorkflowName('Get Workflow - Complete Data'),
        tags: ['mcp-integration-test']
      };

      const created = await client.createWorkflow(workflow);
      expect(created).toBeDefined();
      expect(created.id).toBeTruthy();

      if (!created.id) throw new Error('Workflow ID is missing');
      context.trackWorkflow(created.id);

      // Retrieve the workflow using MCP handler
      const response = await handleGetWorkflow({ id: created.id }, mcpContext);

      // Verify MCP response structure
      expect(response.success).toBe(true);
      expect(response.data).toBeDefined();

      const retrieved = response.data as Workflow;

      // Verify all expected fields are present
      expect(retrieved).toBeDefined();
      expect(retrieved.id).toBe(created.id);
      expect(retrieved.name).toBe(workflow.name);
      expect(retrieved.nodes).toBeDefined();
      expect(retrieved.nodes).toHaveLength(workflow.nodes!.length);
      expect(retrieved.connections).toBeDefined();
      expect(retrieved.active).toBeDefined();
      expect(retrieved.createdAt).toBeDefined();
      expect(retrieved.updatedAt).toBeDefined();

      // Verify node data integrity
      const retrievedNode = retrieved.nodes[0];
      const originalNode = workflow.nodes![0];
      expect(retrievedNode.name).toBe(originalNode.name);
      expect(retrievedNode.type).toBe(originalNode.type);
      expect(retrievedNode.parameters).toBeDefined();
    });
  });

  // ======================================================================
  // Error Handling
  // ======================================================================

  describe('Error Handling', () => {
    it('should return error for non-existent workflow (invalid ID)', async () => {
      const invalidId = '99999999';

      const response = await handleGetWorkflow({ id: invalidId }, mcpContext);

      // MCP handlers return success: false on error
      expect(response.success).toBe(false);
      expect(response.error).toBeDefined();
    });

    it('should return error for malformed workflow ID', async () => {
      const malformedId = 'not-a-valid-id-format';

      const response = await handleGetWorkflow({ id: malformedId }, mcpContext);

      // MCP handlers return success: false on error
      expect(response.success).toBe(false);
      expect(response.error).toBeDefined();
    });
  });
});

```

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

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

export const getNodeInfoDoc: ToolDocumentation = {
  name: 'get_node_info',
  category: 'configuration',
  essentials: {
    description: 'Returns complete node schema with ALL properties (100KB+ response). Only use when you need advanced properties not in get_node_essentials. Contains 200+ properties for complex nodes like HTTP Request. Requires full prefix like "nodes-base.httpRequest".',
    keyParameters: ['nodeType'],
    example: 'get_node_info({nodeType: "nodes-base.slack"})',
    performance: '100-500ms, 50-500KB response',
    tips: [
      'Try get_node_essentials first (95% smaller)',
      'Use only for advanced configurations',
      'Response may have 200+ properties'
    ]
  },
  full: {
    description: 'Returns the complete JSON schema for a node including all properties, operations, authentication methods, version information, and metadata. Response sizes range from 50KB to 500KB. Use this only when get_node_essentials doesn\'t provide the specific property you need.',
    parameters: {
      nodeType: { type: 'string', required: true, description: 'Full node type with prefix. Examples: "nodes-base.slack", "nodes-base.httpRequest", "nodes-langchain.openAi"' }
    },
    returns: `Complete node object containing:
{
  "displayName": "Slack",
  "name": "slack",
  "type": "nodes-base.slack",
  "typeVersion": 2.2,
  "description": "Consume Slack API",
  "defaults": {"name": "Slack"},
  "inputs": ["main"],
  "outputs": ["main"],
  "credentials": [
    {
      "name": "slackApi",
      "required": true,
      "displayOptions": {...}
    }
  ],
  "properties": [
    // 200+ property definitions including:
    {
      "displayName": "Resource",
      "name": "resource",
      "type": "options",
      "options": ["channel", "message", "user", "file", ...],
      "default": "message"
    },
    {
      "displayName": "Operation", 
      "name": "operation",
      "type": "options",
      "displayOptions": {
        "show": {"resource": ["message"]}
      },
      "options": ["post", "update", "delete", "get", ...],
      "default": "post"
    },
    // ... 200+ more properties with complex conditions
  ],
  "version": 2.2,
  "subtitle": "={{$parameter[\"operation\"] + \": \" + $parameter[\"resource\"]}}",
  "codex": {...},
  "supportedWebhooks": [...]
}`,
    examples: [
      'get_node_info({nodeType: "nodes-base.httpRequest"}) - 300+ properties for HTTP requests',
      'get_node_info({nodeType: "nodes-base.googleSheets"}) - Complex operations and auth',
      '// When to use get_node_info:',
      '// 1. First try essentials',
      'const essentials = get_node_essentials({nodeType: "nodes-base.slack"});',
      '// 2. If property missing, search for it',
      'const props = search_node_properties({nodeType: "nodes-base.slack", query: "thread"});',
      '// 3. Only if needed, get full schema',
      'const full = get_node_info({nodeType: "nodes-base.slack"});'
    ],
    useCases: [
      'Analyzing all available operations for a node',
      'Understanding complex property dependencies',
      'Discovering all authentication methods',
      'Building UI that shows all node options',
      'Debugging property visibility conditions'
    ],
    performance: '100-500ms depending on node complexity. HTTP Request node: ~300KB, Simple nodes: ~50KB',
    bestPractices: [
      'Always try get_node_essentials first - it\'s 95% smaller',
      'Use search_node_properties to find specific advanced properties',
      'Cache results locally - schemas rarely change',
      'Parse incrementally - don\'t load entire response into memory at once'
    ],
    pitfalls: [
      'Response can exceed 500KB for complex nodes',
      'Contains many rarely-used properties that add noise',
      'Property conditions can be deeply nested and complex',
      'Must use full node type with prefix (nodes-base.X not just X)'
    ],
    relatedTools: ['get_node_essentials for common properties', 'search_node_properties to find specific fields', 'get_property_dependencies to understand conditions']
  }
};
```

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

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

export const validateWorkflowDoc: ToolDocumentation = {
  name: 'validate_workflow',
  category: 'validation',
  essentials: {
    description: 'Full workflow validation: structure, connections, expressions, AI tools. Returns errors/warnings/fixes. Essential before deploy.',
    keyParameters: ['workflow', 'options'],
    example: 'validate_workflow({workflow: {nodes: [...], connections: {...}}})',
    performance: 'Moderate (100-500ms)',
    tips: [
      'Always validate before n8n_create_workflow to catch errors early',
      'Use options.profile="minimal" for quick checks during development',
      'AI tool connections are automatically validated for proper node references'
    ]
  },
  full: {
    description: 'Performs comprehensive validation of n8n workflows including structure, node configurations, connections, and expressions. This is a three-layer validation system that catches errors before deployment, validates complex multi-node workflows, checks all n8n expressions for syntax errors, and ensures proper node connections and data flow.',
    parameters: {
      workflow: { 
        type: 'object', 
        required: true, 
        description: 'The complete workflow JSON to validate. Must include nodes array and connections object.' 
      },
      options: { 
        type: 'object', 
        required: false, 
        description: 'Validation options object' 
      },
      'options.validateNodes': { 
        type: 'boolean', 
        required: false, 
        description: 'Validate individual node configurations. Default: true' 
      },
      'options.validateConnections': { 
        type: 'boolean', 
        required: false, 
        description: 'Validate node connections and flow. Default: true' 
      },
      'options.validateExpressions': { 
        type: 'boolean', 
        required: false, 
        description: 'Validate n8n expressions syntax and references. Default: true' 
      },
      'options.profile': { 
        type: 'string', 
        required: false, 
        description: 'Validation profile for node validation: minimal, runtime (default), ai-friendly, strict' 
      }
    },
    returns: 'Object with valid (boolean), errors (array), warnings (array), statistics (object), and suggestions (array)',
    examples: [
      'validate_workflow({workflow: myWorkflow}) - Full validation with default settings',
      'validate_workflow({workflow: myWorkflow, options: {profile: "minimal"}}) - Quick validation for editing',
      'validate_workflow({workflow: myWorkflow, options: {validateExpressions: false}}) - Skip expression validation'
    ],
    useCases: [
      'Pre-deployment validation to catch all workflow issues',
      'Quick validation during workflow development',
      'Validate workflows with AI Agent nodes and tool connections',
      'Check expression syntax before workflow execution',
      'Ensure workflow structure integrity after modifications'
    ],
    performance: 'Moderate (100-500ms). Depends on workflow size and validation options. Expression validation adds ~50-100ms.',
    bestPractices: [
      'Always validate workflows before creating or updating in n8n',
      'Use minimal profile during development, strict profile before production',
      'Pay attention to warnings - they often indicate potential runtime issues',
      'Validate after any workflow modifications, especially connection changes',
      'Check statistics to understand workflow complexity'
    ],
    pitfalls: [
      'Large workflows (100+ nodes) may take longer to validate',
      'Expression validation requires proper node references to exist',
      'Some warnings may be acceptable depending on use case',
      'Validation cannot catch all runtime errors (e.g., API failures)',
      'Profile setting only affects node validation, not connection/expression checks'
    ],
    relatedTools: ['validate_workflow_connections', 'validate_workflow_expressions', 'validate_node_operation', 'n8n_create_workflow', 'n8n_update_partial_workflow', 'n8n_autofix_workflow']
  }
};
```

--------------------------------------------------------------------------------
/scripts/test-expression-code-validation.ts:
--------------------------------------------------------------------------------

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

/**
 * Test script for Expression vs Code Node validation
 * Tests that we properly detect and warn about expression syntax in Code nodes
 */

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

console.log('🧪 Testing Expression vs Code Node Validation\n');

// Test cases with expression syntax that shouldn't work in Code nodes
const testCases = [
  {
    name: 'Expression syntax in Code node',
    config: {
      language: 'javaScript',
      jsCode: `// Using expression syntax
const value = {{$json.field}};
return [{json: {value}}];`
    },
    expectedError: 'Expression syntax {{...}} is not valid in Code nodes'
  },
  {
    name: 'Wrong $node syntax',
    config: {
      language: 'javaScript',
      jsCode: `// Using expression $node syntax
const data = $node['Previous Node'].json;
return [{json: data}];`
    },
    expectedWarning: 'Use $(\'Node Name\') instead of $node[\'Node Name\'] in Code nodes'
  },
  {
    name: 'Expression-only functions',
    config: {
      language: 'javaScript',
      jsCode: `// Using expression functions
const now = $now();
const unique = items.unique();
return [{json: {now, unique}}];`
    },
    expectedWarning: '$now() is an expression-only function'
  },
  {
    name: 'Wrong JMESPath parameter order',
    config: {
      language: 'javaScript',
      jsCode: `// Wrong parameter order
const result = $jmespath("users[*].name", data);
return [{json: {result}}];`
    },
    expectedWarning: 'Code node $jmespath has reversed parameter order'
  },
  {
    name: 'Correct Code node syntax',
    config: {
      language: 'javaScript',
      jsCode: `// Correct syntax
const prevData = $('Previous Node').first();
const now = DateTime.now();
const result = $jmespath(data, "users[*].name");
return [{json: {prevData, now, result}}];`
    },
    shouldBeValid: true
  }
];

// Basic node properties for Code node
const codeNodeProperties = [
  { name: 'language', type: 'options', options: ['javaScript', 'python'] },
  { name: 'jsCode', type: 'string' },
  { name: 'pythonCode', type: 'string' },
  { name: 'mode', type: 'options', options: ['runOnceForAllItems', 'runOnceForEachItem'] }
];

console.log('Running validation tests...\n');

testCases.forEach((test, index) => {
  console.log(`Test ${index + 1}: ${test.name}`);
  console.log('─'.repeat(50));
  
  const result = EnhancedConfigValidator.validateWithMode(
    'nodes-base.code',
    test.config,
    codeNodeProperties,
    'operation',
    'ai-friendly'
  );
  
  console.log(`Valid: ${result.valid}`);
  console.log(`Errors: ${result.errors.length}`);
  console.log(`Warnings: ${result.warnings.length}`);
  
  if (test.expectedError) {
    const hasExpectedError = result.errors.some(e => 
      e.message.includes(test.expectedError)
    );
    console.log(`✅ Expected error found: ${hasExpectedError}`);
    if (!hasExpectedError) {
      console.log('❌ Missing expected error:', test.expectedError);
      console.log('Actual errors:', result.errors.map(e => e.message));
    }
  }
  
  if (test.expectedWarning) {
    const hasExpectedWarning = result.warnings.some(w => 
      w.message.includes(test.expectedWarning)
    );
    console.log(`✅ Expected warning found: ${hasExpectedWarning}`);
    if (!hasExpectedWarning) {
      console.log('❌ Missing expected warning:', test.expectedWarning);
      console.log('Actual warnings:', result.warnings.map(w => w.message));
    }
  }
  
  if (test.shouldBeValid) {
    console.log(`✅ Should be valid: ${result.valid && result.errors.length === 0}`);
    if (!result.valid || result.errors.length > 0) {
      console.log('❌ Unexpected errors:', result.errors);
    }
  }
  
  // Show actual messages
  if (result.errors.length > 0) {
    console.log('\nErrors:');
    result.errors.forEach(e => console.log(`  - ${e.message}`));
  }
  
  if (result.warnings.length > 0) {
    console.log('\nWarnings:');
    result.warnings.forEach(w => console.log(`  - ${w.message}`));
  }
  
  console.log('\n');
});

console.log('✅ Expression vs Code Node validation tests completed!');
```

--------------------------------------------------------------------------------
/tests/test-sqlite-search.js:
--------------------------------------------------------------------------------

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

/**
 * Test SQLite database search functionality
 */

const { SQLiteStorageService } = require('../dist/services/sqlite-storage-service');
const { NodeSourceExtractor } = require('../dist/utils/node-source-extractor');

async function testDatabaseSearch() {
  console.log('=== SQLite Database Search Test ===\n');
  
  const storage = new SQLiteStorageService();
  const extractor = new NodeSourceExtractor();
  
  // First, ensure we have some data
  console.log('1️⃣ Checking database status...');
  let stats = await storage.getStatistics();
  
  if (stats.totalNodes === 0) {
    console.log('   Database is empty. Adding some test nodes...\n');
    
    const testNodes = [
      'n8n-nodes-base.Function',
      'n8n-nodes-base.Webhook',
      'n8n-nodes-base.HttpRequest',
      'n8n-nodes-base.If',
      'n8n-nodes-base.Slack',
      'n8n-nodes-base.Discord'
    ];
    
    for (const nodeType of testNodes) {
      try {
        const nodeInfo = await extractor.extractNodeSource(nodeType);
        await storage.storeNode(nodeInfo);
        console.log(`   ✅ Stored ${nodeType}`);
      } catch (error) {
        console.log(`   ❌ Failed to store ${nodeType}: ${error.message}`);
      }
    }
    
    stats = await storage.getStatistics();
  }
  
  console.log(`\n   Total nodes in database: ${stats.totalNodes}`);
  console.log(`   Total packages: ${stats.totalPackages}`);
  console.log(`   Database size: ${(stats.totalCodeSize / 1024).toFixed(2)} KB\n`);
  
  // Test different search scenarios
  console.log('2️⃣ Testing search functionality...\n');
  
  const searchTests = [
    {
      name: 'Search by partial name (func)',
      query: { query: 'func' }
    },
    {
      name: 'Search by partial name (web)',
      query: { query: 'web' }
    },
    {
      name: 'Search for HTTP',
      query: { query: 'http' }
    },
    {
      name: 'Search for multiple terms',
      query: { query: 'slack discord' }
    },
    {
      name: 'Filter by package',
      query: { packageName: 'n8n-nodes-base' }
    },
    {
      name: 'Search with package filter',
      query: { query: 'func', packageName: 'n8n-nodes-base' }
    },
    {
      name: 'Search by node type',
      query: { nodeType: 'Webhook' }
    },
    {
      name: 'Limit results',
      query: { query: 'node', limit: 3 }
    }
  ];
  
  for (const test of searchTests) {
    console.log(`   📍 ${test.name}:`);
    console.log(`      Query: ${JSON.stringify(test.query)}`);
    
    try {
      const results = await storage.searchNodes(test.query);
      console.log(`      Results: ${results.length} nodes found`);
      
      if (results.length > 0) {
        console.log('      Matches:');
        results.slice(0, 3).forEach(node => {
          console.log(`        - ${node.nodeType} (${node.displayName || node.name})`);
        });
        if (results.length > 3) {
          console.log(`        ... and ${results.length - 3} more`);
        }
      }
    } catch (error) {
      console.log(`      ❌ Error: ${error.message}`);
    }
    
    console.log('');
  }
  
  // Test specific node retrieval
  console.log('3️⃣ Testing specific node retrieval...\n');
  
  const specificNode = await storage.getNode('n8n-nodes-base.Function');
  if (specificNode) {
    console.log(`   ✅ Found node: ${specificNode.nodeType}`);
    console.log(`      Display name: ${specificNode.displayName}`);
    console.log(`      Code size: ${specificNode.codeLength} bytes`);
    console.log(`      Has credentials: ${specificNode.hasCredentials}`);
  } else {
    console.log('   ❌ Node not found');
  }
  
  // Test package listing
  console.log('\n4️⃣ Testing package listing...\n');
  
  const packages = await storage.getPackages();
  console.log(`   Found ${packages.length} packages:`);
  packages.forEach(pkg => {
    console.log(`     - ${pkg.name}: ${pkg.nodeCount} nodes`);
  });
  
  // Close database
  storage.close();
  
  console.log('\n✅ Search functionality test completed!');
}

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

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

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

export const validateNodeOperationDoc: ToolDocumentation = {
  name: 'validate_node_operation',
  category: 'validation',
  essentials: {
    description: 'Validates node configuration with operation awareness. Checks required fields, data types, and operation-specific rules. Returns specific errors with automated fix suggestions. Different profiles for different validation needs.',
    keyParameters: ['nodeType', 'config', 'profile'],
    example: 'validate_node_operation({nodeType: "nodes-base.slack", config: {resource: "message", operation: "post", text: "Hi"}})',
    performance: '<100ms',
    tips: [
      'Profile choices: minimal (editing), runtime (execution), ai-friendly (balanced), strict (deployment)',
      'Returns fixes you can apply directly',
      'Operation-aware - knows Slack post needs text'
    ]
  },
  full: {
    description: 'Comprehensive node configuration validation that understands operation context. For example, it knows Slack message posting requires text field, while channel listing doesn\'t. Provides different validation profiles for different stages of workflow development.',
    parameters: {
      nodeType: { type: 'string', required: true, description: 'Full node type with prefix: "nodes-base.slack", "nodes-base.httpRequest"' },
      config: { type: 'object', required: true, description: 'Node configuration. Must include operation fields (resource/operation/action) if the node has multiple operations' },
      profile: { type: 'string', required: false, description: 'Validation profile - controls what\'s checked. Default: "ai-friendly"' }
    },
    returns: `Object containing:
{
  "isValid": false,
  "errors": [
    {
      "field": "channel",
      "message": "Required field 'channel' is missing",
      "severity": "error",
      "fix": "#general"
    }
  ],
  "warnings": [
    {
      "field": "retryOnFail", 
      "message": "Consider enabling retry for reliability",
      "severity": "warning",
      "fix": true
    }
  ],
  "suggestions": [
    {
      "field": "timeout",
      "message": "Set timeout to prevent hanging",
      "fix": 30000
    }
  ],
  "fixes": {
    "channel": "#general",
    "retryOnFail": true,
    "timeout": 30000
  }
}`,
    examples: [
      '// Missing required field',
      'validate_node_operation({nodeType: "nodes-base.slack", config: {resource: "message", operation: "post"}})',
      '// Returns: {isValid: false, errors: [{field: "text", message: "Required field missing"}], fixes: {text: "Message text"}}',
      '',
      '// Validate with strict profile for production',
      'validate_node_operation({nodeType: "nodes-base.httpRequest", config: {method: "POST", url: "https://api.example.com"}, profile: "strict"})',
      '',
      '// Apply fixes automatically',
      'const result = validate_node_operation({nodeType: "nodes-base.slack", config: myConfig});',
      'if (!result.isValid) {',
      '  myConfig = {...myConfig, ...result.fixes};',
      '}'
    ],
    useCases: [
      'Validate configuration before workflow execution',
      'Debug why a node isn\'t working as expected',
      'Generate configuration fixes automatically',
      'Different validation for editing vs production'
    ],
    performance: '<100ms for most nodes, <200ms for complex nodes with many conditions',
    bestPractices: [
      'Use "minimal" profile during user editing for fast feedback',
      'Use "runtime" profile (default) before execution',
      'Use "ai-friendly" when AI configures nodes',
      'Use "strict" profile before production deployment',
      'Always include operation fields (resource/operation) in config',
      'Apply suggested fixes to resolve issues quickly'
    ],
    pitfalls: [
      'Must include operation fields for multi-operation nodes',
      'Fixes are suggestions - review before applying',
      'Profile affects what\'s validated - minimal skips many checks'
    ],
    relatedTools: ['validate_node_minimal for quick checks', 'get_node_essentials for valid examples', 'validate_workflow for complete workflow validation']
  }
};
```

--------------------------------------------------------------------------------
/src/mcp/tool-docs/system/n8n-health-check.ts:
--------------------------------------------------------------------------------

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

export const n8nHealthCheckDoc: ToolDocumentation = {
  name: 'n8n_health_check',
  category: 'system',
  essentials: {
    description: 'Check n8n instance health, API connectivity, version status, and performance metrics',
    keyParameters: [],
    example: 'n8n_health_check({})',
    performance: 'Fast - single API call (~150-200ms median)',
    tips: [
      'Use before starting workflow operations to ensure n8n is responsive',
      'Automatically checks if n8n-mcp version is outdated',
      'Returns version info, performance metrics, and next-step recommendations',
      'New: Shows cache hit rate and response time for performance monitoring'
    ]
  },
  full: {
    description: `Performs a comprehensive health check of the configured n8n instance through its API.

This tool verifies:
- API endpoint accessibility and response time
- n8n instance version and build information
- Authentication status and permissions
- Available features and enterprise capabilities
- Database connectivity (as reported by n8n)
- Queue system status (if configured)

Health checks are crucial for:
- Monitoring n8n instance availability
- Detecting performance degradation
- Verifying API compatibility before operations
- Ensuring authentication is working correctly`,
    parameters: {},
    returns: `Health status object containing:
- status: Overall health status ('healthy', 'degraded', 'error')
- n8nVersion: n8n instance version information
- instanceId: Unique identifier for the n8n instance
- features: Object listing available features and their status
- mcpVersion: Current n8n-mcp version
- supportedN8nVersion: Recommended n8n version for compatibility
- versionCheck: Version status information
  - current: Current n8n-mcp version
  - latest: Latest available version from npm
  - upToDate: Boolean indicating if version is current
  - message: Formatted version status message
  - updateCommand: Command to update (if outdated)
- performance: Performance metrics
  - responseTimeMs: API response time in milliseconds
  - cacheHitRate: Cache efficiency percentage
  - cachedInstances: Number of cached API instances
- nextSteps: Recommended actions after health check
- updateWarning: Warning if version is outdated (if applicable)`,
    examples: [
      'n8n_health_check({}) - Complete health check with version and performance data',
      '// Use in monitoring scripts\nconst health = await n8n_health_check({});\nif (health.status !== "ok") alert("n8n is down!");\nif (!health.versionCheck.upToDate) console.log("Update available:", health.versionCheck.updateCommand);',
      '// Check before critical operations\nconst health = await n8n_health_check({});\nif (health.performance.responseTimeMs > 1000) console.warn("n8n is slow");\nif (health.versionCheck.isOutdated) console.log(health.updateWarning);'
    ],
    useCases: [
      'Pre-flight checks before workflow deployments',
      'Continuous monitoring of n8n instance health',
      'Troubleshooting connectivity or performance issues',
      'Verifying n8n version compatibility with workflows',
      'Detecting feature availability (enterprise features, queue mode, etc.)'
    ],
    performance: `Fast response expected:
- Single HTTP request to /health endpoint
- Typically responds in <100ms for healthy instances
- Timeout after 10 seconds indicates severe issues
- Minimal server load - safe for frequent polling`,
    bestPractices: [
      'Run health checks before batch operations or deployments',
      'Set up automated monitoring with regular health checks',
      'Log response times to detect performance trends',
      'Check version compatibility when deploying workflows',
      'Use health status to implement circuit breaker patterns'
    ],
    pitfalls: [
      'Requires N8N_API_URL and N8N_API_KEY to be configured',
      'Network issues may cause false negatives',
      'Does not check individual workflow health',
      'Health endpoint might be cached - not real-time for all metrics'
    ],
    relatedTools: ['n8n_diagnostic', 'n8n_list_available_tools', 'n8n_list_workflows']
  }
};
```

--------------------------------------------------------------------------------
/scripts/quick-test.ts:
--------------------------------------------------------------------------------

```typescript
#!/usr/bin/env ts-node
/**
 * Quick test script to validate the essentials implementation
 */

import { spawn } from 'child_process';
import { join } from 'path';

const colors = {
  reset: '\x1b[0m',
  bright: '\x1b[1m',
  green: '\x1b[32m',
  red: '\x1b[31m',
  yellow: '\x1b[33m',
  blue: '\x1b[34m',
  cyan: '\x1b[36m'
};

function log(message: string, color: string = colors.reset) {
  console.log(`${color}${message}${colors.reset}`);
}

async function runMCPCommand(toolName: string, args: any): Promise<any> {
  return new Promise((resolve, reject) => {
    const request = {
      jsonrpc: '2.0',
      method: 'tools/call',
      params: {
        name: toolName,
        arguments: args
      },
      id: 1
    };
    
    const mcp = spawn('npm', ['start'], {
      cwd: join(__dirname, '..'),
      stdio: ['pipe', 'pipe', 'pipe']
    });
    
    let output = '';
    let error = '';
    
    mcp.stdout.on('data', (data) => {
      output += data.toString();
    });
    
    mcp.stderr.on('data', (data) => {
      error += data.toString();
    });
    
    mcp.on('close', (code) => {
      if (code !== 0) {
        reject(new Error(`Process exited with code ${code}: ${error}`));
        return;
      }
      
      try {
        // Parse JSON-RPC response
        const lines = output.split('\n');
        for (const line of lines) {
          if (line.trim() && line.includes('"jsonrpc"')) {
            const response = JSON.parse(line);
            if (response.result) {
              resolve(JSON.parse(response.result.content[0].text));
              return;
            } else if (response.error) {
              reject(new Error(response.error.message));
              return;
            }
          }
        }
        reject(new Error('No valid response found'));
      } catch (err) {
        reject(err);
      }
    });
    
    // Send request
    mcp.stdin.write(JSON.stringify(request) + '\n');
    mcp.stdin.end();
  });
}

async function quickTest() {
  log('\n🚀 Quick Test - n8n MCP Essentials', colors.bright + colors.cyan);
  
  try {
    // Test 1: Get essentials for HTTP Request
    log('\n1️⃣  Testing get_node_essentials for HTTP Request...', colors.yellow);
    const essentials = await runMCPCommand('get_node_essentials', {
      nodeType: 'nodes-base.httpRequest'
    });
    
    log('✅ Success! Got essentials:', colors.green);
    log(`   Required properties: ${essentials.requiredProperties?.map((p: any) => p.name).join(', ') || 'None'}`);
    log(`   Common properties: ${essentials.commonProperties?.map((p: any) => p.name).join(', ') || 'None'}`);
    log(`   Examples: ${Object.keys(essentials.examples || {}).join(', ')}`);
    log(`   Response size: ${JSON.stringify(essentials).length} bytes`, colors.green);
    
    // Test 2: Search properties
    log('\n2️⃣  Testing search_node_properties...', colors.yellow);
    const searchResults = await runMCPCommand('search_node_properties', {
      nodeType: 'nodes-base.httpRequest',
      query: 'auth'
    });
    
    log('✅ Success! Found properties:', colors.green);
    log(`   Matches: ${searchResults.totalMatches}`);
    searchResults.matches?.slice(0, 3).forEach((match: any) => {
      log(`   - ${match.name}: ${match.description}`);
    });
    
    // Test 3: Compare sizes
    log('\n3️⃣  Comparing response sizes...', colors.yellow);
    const fullInfo = await runMCPCommand('get_node_info', {
      nodeType: 'nodes-base.httpRequest'
    });
    
    const fullSize = JSON.stringify(fullInfo).length;
    const essentialSize = JSON.stringify(essentials).length;
    const reduction = ((fullSize - essentialSize) / fullSize * 100).toFixed(1);
    
    log(`✅ Size comparison:`, colors.green);
    log(`   Full response: ${(fullSize / 1024).toFixed(1)} KB`);
    log(`   Essential response: ${(essentialSize / 1024).toFixed(1)} KB`);
    log(`   Size reduction: ${reduction}% 🎉`, colors.bright + colors.green);
    
    log('\n✨ All tests passed!', colors.bright + colors.green);
    
  } catch (error) {
    log(`\n❌ Test failed: ${error}`, colors.red);
    process.exit(1);
  }
}

// Run if called directly
if (require.main === module) {
  quickTest().catch(console.error);
}
```

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

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

export const n8nListExecutionsDoc: ToolDocumentation = {
  name: 'n8n_list_executions',
  category: 'workflow_management',
  essentials: {
    description: 'List workflow executions with optional filters. Supports pagination for large result sets.',
    keyParameters: ['workflowId', 'status', 'limit'],
    example: 'n8n_list_executions({workflowId: "abc123", status: "error"})',
    performance: 'Fast metadata retrieval, use pagination for large datasets',
    tips: [
      'Filter by status (success/error/waiting) to find specific execution types',
      'Use workflowId to see all executions for a specific workflow',
      'Pagination via cursor allows retrieving large execution histories'
    ]
  },
  full: {
    description: `Lists workflow executions with powerful filtering options. This tool is essential for monitoring workflow performance, finding failed executions, and tracking workflow activity. Supports pagination for retrieving large execution histories and filtering by workflow, status, and project.`,
    parameters: {
      limit: {
        type: 'number',
        required: false,
        description: 'Number of executions to return (1-100, default: 100). Use with cursor for pagination'
      },
      cursor: {
        type: 'string',
        required: false,
        description: 'Pagination cursor from previous response. Used to retrieve next page of results'
      },
      workflowId: {
        type: 'string',
        required: false,
        description: 'Filter executions by specific workflow ID. Shows all executions for that workflow'
      },
      projectId: {
        type: 'string',
        required: false,
        description: 'Filter by project ID (enterprise feature). Groups executions by project'
      },
      status: {
        type: 'string',
        required: false,
        enum: ['success', 'error', 'waiting'],
        description: 'Filter by execution status. Success = completed, Error = failed, Waiting = running'
      },
      includeData: {
        type: 'boolean',
        required: false,
        description: 'Include execution data in results (default: false). Significantly increases response size'
      }
    },
    returns: `Array of execution objects with metadata, pagination cursor for next page, and optionally execution data. Each execution includes ID, status, start/end times, and workflow reference.`,
    examples: [
      'n8n_list_executions({limit: 10}) - Get 10 most recent executions',
      'n8n_list_executions({workflowId: "abc123"}) - All executions for specific workflow',
      'n8n_list_executions({status: "error", limit: 50}) - Find failed executions',
      'n8n_list_executions({status: "waiting"}) - Monitor currently running workflows',
      'n8n_list_executions({cursor: "next-page-token"}) - Get next page of results'
    ],
    useCases: [
      'Monitor workflow execution history and patterns',
      'Find and debug failed workflow executions',
      'Track currently running workflows (waiting status)',
      'Analyze workflow performance and execution frequency',
      'Generate execution reports for specific workflows'
    ],
    performance: `Listing executions is fast for metadata only. Including data (includeData: true) significantly impacts performance. Use pagination (limit + cursor) for large result sets. Default limit of 100 balances performance with usability.`,
    bestPractices: [
      'Use status filters to focus on specific execution types',
      'Implement pagination for large execution histories',
      'Avoid includeData unless you need execution details',
      'Filter by workflowId when monitoring specific workflows',
      'Check for cursor in response to detect more pages'
    ],
    pitfalls: [
      'Large limits with includeData can cause timeouts',
      'Execution retention depends on n8n configuration',
      'Cursor tokens expire - use them promptly',
      'Status "waiting" includes both running and queued executions',
      'Deleted workflows still show in execution history'
    ],
    relatedTools: ['n8n_get_execution', 'n8n_trigger_webhook_workflow', 'n8n_delete_execution', 'n8n_list_workflows']
  }
};
```

--------------------------------------------------------------------------------
/tests/benchmarks/database-queries.bench.ts:
--------------------------------------------------------------------------------

```typescript
import { bench, describe } from 'vitest';
import { NodeRepository } from '../../src/database/node-repository';
import { SQLiteStorageService } from '../../src/services/sqlite-storage-service';
import { NodeFactory } from '../factories/node-factory';
import { PropertyDefinitionFactory } from '../factories/property-definition-factory';

/**
 * Database Query Performance Benchmarks
 *
 * NOTE: These benchmarks use MOCK DATA (500 artificial test nodes)
 * created with factories, not the real production database.
 *
 * This is useful for tracking database layer performance in isolation,
 * but may not reflect real-world performance characteristics.
 *
 * For end-to-end MCP tool performance with real data, see mcp-tools.bench.ts
 */
describe('Database Query Performance', () => {
  let repository: NodeRepository;
  let storage: SQLiteStorageService;
  const testNodeCount = 500;

  beforeAll(async () => {
    storage = new SQLiteStorageService(':memory:');
    repository = new NodeRepository(storage);
    
    // Seed database with test data
    for (let i = 0; i < testNodeCount; i++) {
      const node = NodeFactory.build({
        displayName: `TestNode${i}`,
        nodeType: `nodes-base.testNode${i}`,
        category: i % 2 === 0 ? 'transform' : 'trigger',
        packageName: 'n8n-nodes-base',
        documentation: `Test documentation for node ${i}`,
        properties: PropertyDefinitionFactory.buildList(5)
      });
      await repository.upsertNode(node);
    }
  });

  afterAll(() => {
    storage.close();
  });

  bench('getNodeByType - existing node', async () => {
    await repository.getNodeByType('nodes-base.testNode100');
  }, {
    iterations: 1000,
    warmupIterations: 100,
    warmupTime: 500,
    time: 3000
  });

  bench('getNodeByType - non-existing node', async () => {
    await repository.getNodeByType('nodes-base.nonExistentNode');
  }, {
    iterations: 1000,
    warmupIterations: 100,
    warmupTime: 500,
    time: 3000
  });

  bench('getNodesByCategory - transform', async () => {
    await repository.getNodesByCategory('transform');
  }, {
    iterations: 100,
    warmupIterations: 10,
    warmupTime: 500,
    time: 3000
  });

  bench('searchNodes - OR mode', async () => {
    await repository.searchNodes('test node data', 'OR', 20);
  }, {
    iterations: 100,
    warmupIterations: 10,
    warmupTime: 500,
    time: 3000
  });

  bench('searchNodes - AND mode', async () => {
    await repository.searchNodes('test node', 'AND', 20);
  }, {
    iterations: 100,
    warmupIterations: 10,
    warmupTime: 500,
    time: 3000
  });

  bench('searchNodes - FUZZY mode', async () => {
    await repository.searchNodes('tst nde', 'FUZZY', 20);
  }, {
    iterations: 100,
    warmupIterations: 10,
    warmupTime: 500,
    time: 3000
  });

  bench('getAllNodes - no limit', async () => {
    await repository.getAllNodes();
  }, {
    iterations: 50,
    warmupIterations: 5,
    warmupTime: 500,
    time: 3000
  });

  bench('getAllNodes - with limit', async () => {
    await repository.getAllNodes(50);
  }, {
    iterations: 100,
    warmupIterations: 10,
    warmupTime: 500,
    time: 3000
  });

  bench('getNodeCount', async () => {
    await repository.getNodeCount();
  }, {
    iterations: 1000,
    warmupIterations: 100,
    warmupTime: 100,
    time: 2000
  });

  bench('getAIToolNodes', async () => {
    await repository.getAIToolNodes();
  }, {
    iterations: 100,
    warmupIterations: 10,
    warmupTime: 500,
    time: 3000
  });

  bench('upsertNode - new node', async () => {
    const node = NodeFactory.build({
      displayName: `BenchNode${Date.now()}`,
      nodeType: `nodes-base.benchNode${Date.now()}`
    });
    await repository.upsertNode(node);
  }, {
    iterations: 100,
    warmupIterations: 10,
    warmupTime: 500,
    time: 3000
  });

  bench('upsertNode - existing node update', async () => {
    const existingNode = await repository.getNodeByType('nodes-base.testNode0');
    if (existingNode) {
      existingNode.description = `Updated description ${Date.now()}`;
      await repository.upsertNode(existingNode);
    }
  }, {
    iterations: 100,
    warmupIterations: 10,
    warmupTime: 500,
    time: 3000
  });
});
```

--------------------------------------------------------------------------------
/tests/unit/test-infrastructure.test.ts:
--------------------------------------------------------------------------------

```typescript
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { nodeFactory, webhookNodeFactory, slackNodeFactory } from '@tests/fixtures/factories/node.factory';

// Mock better-sqlite3
vi.mock('better-sqlite3');

describe('Test Infrastructure', () => {
  describe('Database Mock', () => {
    it('should create a mock database instance', async () => {
      const Database = (await import('better-sqlite3')).default;
      const db = new Database(':memory:');
      
      expect(Database).toHaveBeenCalled();
      expect(db).toBeDefined();
      expect(db.prepare).toBeDefined();
      expect(db.exec).toBeDefined();
      expect(db.close).toBeDefined();
    });
    
    it('should handle basic CRUD operations', async () => {
      const { MockDatabase } = await import('@tests/unit/database/__mocks__/better-sqlite3');
      const db = new MockDatabase();
      
      // Test data seeding
      db._seedData('nodes', [
        { id: '1', name: 'test-node', type: 'webhook' }
      ]);
      
      // Test SELECT
      const selectStmt = db.prepare('SELECT * FROM nodes');
      const allNodes = selectStmt.all();
      expect(allNodes).toHaveLength(1);
      expect(allNodes[0]).toEqual({ id: '1', name: 'test-node', type: 'webhook' });
      
      // Test INSERT
      const insertStmt = db.prepare('INSERT INTO nodes (id, name, type) VALUES (?, ?, ?)');
      const result = insertStmt.run({ id: '2', name: 'new-node', type: 'slack' });
      expect(result.changes).toBe(1);
      
      // Verify insert worked
      const allNodesAfter = selectStmt.all();
      expect(allNodesAfter).toHaveLength(2);
    });
  });
  
  describe('Node Factory', () => {
    it('should create a basic node definition', () => {
      const node = nodeFactory.build();
      
      expect(node).toMatchObject({
        name: expect.any(String),
        displayName: expect.any(String),
        description: expect.any(String),
        version: expect.any(Number),
        defaults: {
          name: expect.any(String)
        },
        inputs: ['main'],
        outputs: ['main'],
        properties: expect.any(Array),
        credentials: []
      });
    });
    
    it('should create a webhook node', () => {
      const webhook = webhookNodeFactory.build();
      
      expect(webhook).toMatchObject({
        name: 'webhook',
        displayName: 'Webhook',
        description: 'Starts the workflow when a webhook is called',
        group: ['trigger'],
        properties: expect.arrayContaining([
          expect.objectContaining({
            name: 'path',
            type: 'string',
            required: true
          }),
          expect.objectContaining({
            name: 'method',
            type: 'options'
          })
        ])
      });
    });
    
    it('should create a slack node', () => {
      const slack = slackNodeFactory.build();
      
      expect(slack).toMatchObject({
        name: 'slack',
        displayName: 'Slack',
        description: 'Send messages to Slack',
        group: ['output'],
        credentials: [
          {
            name: 'slackApi',
            required: true
          }
        ],
        properties: expect.arrayContaining([
          expect.objectContaining({
            name: 'resource',
            type: 'options'
          }),
          expect.objectContaining({
            name: 'operation',
            type: 'options',
            displayOptions: {
              show: {
                resource: ['message']
              }
            }
          })
        ])
      });
    });
    
    it('should allow overriding factory defaults', () => {
      const customNode = nodeFactory.build({
        name: 'custom-node',
        displayName: 'Custom Node',
        version: 2
      });
      
      expect(customNode.name).toBe('custom-node');
      expect(customNode.displayName).toBe('Custom Node');
      expect(customNode.version).toBe(2);
    });
    
    it('should create multiple unique nodes', () => {
      const nodes = nodeFactory.buildList(5);
      
      expect(nodes).toHaveLength(5);
      const names = nodes.map(n => n.name);
      const uniqueNames = new Set(names);
      expect(uniqueNames.size).toBe(5);
    });
  });
});
```

--------------------------------------------------------------------------------
/scripts/test-user-id-persistence.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Test User ID Persistence
 * Verifies that user IDs are consistent across sessions and modes
 */

import { TelemetryConfigManager } from '../src/telemetry/config-manager';
import { hostname, platform, arch, homedir } from 'os';
import { createHash } from 'crypto';

console.log('=== User ID Persistence Test ===\n');

// Test 1: Verify deterministic ID generation
console.log('Test 1: Deterministic ID Generation');
console.log('-----------------------------------');

const machineId = `${hostname()}-${platform()}-${arch()}-${homedir()}`;
const expectedUserId = createHash('sha256')
  .update(machineId)
  .digest('hex')
  .substring(0, 16);

console.log('Machine characteristics:');
console.log('  hostname:', hostname());
console.log('  platform:', platform());
console.log('  arch:', arch());
console.log('  homedir:', homedir());
console.log('\nGenerated machine ID:', machineId);
console.log('Expected user ID:', expectedUserId);

// Test 2: Load actual config
console.log('\n\nTest 2: Actual Config Manager');
console.log('-----------------------------------');

const configManager = TelemetryConfigManager.getInstance();
const actualUserId = configManager.getUserId();
const config = configManager.loadConfig();

console.log('Actual user ID:', actualUserId);
console.log('Config first run:', config.firstRun || 'Unknown');
console.log('Config version:', config.version || 'Unknown');
console.log('Telemetry enabled:', config.enabled);

// Test 3: Verify consistency
console.log('\n\nTest 3: Consistency Check');
console.log('-----------------------------------');

const match = actualUserId === expectedUserId;
console.log('User IDs match:', match ? '✓ YES' : '✗ NO');

if (!match) {
  console.log('WARNING: User ID mismatch detected!');
  console.log('This could indicate an implementation issue.');
}

// Test 4: Multiple loads (simulate multiple sessions)
console.log('\n\nTest 4: Multiple Session Simulation');
console.log('-----------------------------------');

const userId1 = configManager.getUserId();
const userId2 = TelemetryConfigManager.getInstance().getUserId();
const userId3 = configManager.getUserId();

console.log('Session 1 user ID:', userId1);
console.log('Session 2 user ID:', userId2);
console.log('Session 3 user ID:', userId3);

const consistent = userId1 === userId2 && userId2 === userId3;
console.log('All sessions consistent:', consistent ? '✓ YES' : '✗ NO');

// Test 5: Docker environment simulation
console.log('\n\nTest 5: Docker Environment Check');
console.log('-----------------------------------');

const isDocker = process.env.IS_DOCKER === 'true';
console.log('Running in Docker:', isDocker);

if (isDocker) {
  console.log('\n⚠️  DOCKER MODE DETECTED');
  console.log('In Docker, user IDs may change across container recreations because:');
  console.log('  1. Container hostname changes each time');
  console.log('  2. Config file is not persisted (no volume mount)');
  console.log('  3. Each container gets a new ephemeral filesystem');
  console.log('\nRecommendation: Mount ~/.n8n-mcp as a volume for persistent user IDs');
}

// Test 6: Environment variable override check
console.log('\n\nTest 6: Environment Variable Override');
console.log('-----------------------------------');

const telemetryDisabledVars = [
  'N8N_MCP_TELEMETRY_DISABLED',
  'TELEMETRY_DISABLED',
  'DISABLE_TELEMETRY'
];

telemetryDisabledVars.forEach(varName => {
  const value = process.env[varName];
  if (value !== undefined) {
    console.log(`${varName}:`, value);
  }
});

console.log('\nTelemetry status:', configManager.isEnabled() ? 'ENABLED' : 'DISABLED');

// Summary
console.log('\n\n=== SUMMARY ===');
console.log('User ID:', actualUserId);
console.log('Deterministic:', match ? 'YES ✓' : 'NO ✗');
console.log('Persistent across sessions:', consistent ? 'YES ✓' : 'NO ✗');
console.log('Telemetry enabled:', config.enabled ? 'YES' : 'NO');
console.log('Docker mode:', isDocker ? 'YES' : 'NO');

if (isDocker && !process.env.N8N_MCP_CONFIG_VOLUME) {
  console.log('\n⚠️  WARNING: Running in Docker without persistent volume!');
  console.log('User IDs will change on container recreation.');
  console.log('Mount /home/nodejs/.n8n-mcp to persist telemetry config.');
}

console.log('\n');

```

--------------------------------------------------------------------------------
/src/telemetry/startup-checkpoints.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Startup Checkpoint System
 * Defines checkpoints throughout the server initialization process
 * to identify where failures occur
 */

/**
 * Startup checkpoint constants
 * These checkpoints mark key stages in the server initialization process
 */
export const STARTUP_CHECKPOINTS = {
  /** Process has started, very first checkpoint */
  PROCESS_STARTED: 'process_started',

  /** About to connect to database */
  DATABASE_CONNECTING: 'database_connecting',

  /** Database connection successful */
  DATABASE_CONNECTED: 'database_connected',

  /** About to check n8n API configuration (if applicable) */
  N8N_API_CHECKING: 'n8n_api_checking',

  /** n8n API is configured and ready (if applicable) */
  N8N_API_READY: 'n8n_api_ready',

  /** About to initialize telemetry system */
  TELEMETRY_INITIALIZING: 'telemetry_initializing',

  /** Telemetry system is ready */
  TELEMETRY_READY: 'telemetry_ready',

  /** About to start MCP handshake */
  MCP_HANDSHAKE_STARTING: 'mcp_handshake_starting',

  /** MCP handshake completed successfully */
  MCP_HANDSHAKE_COMPLETE: 'mcp_handshake_complete',

  /** Server is fully ready to handle requests */
  SERVER_READY: 'server_ready',
} as const;

/**
 * Type for checkpoint names
 */
export type StartupCheckpoint = typeof STARTUP_CHECKPOINTS[keyof typeof STARTUP_CHECKPOINTS];

/**
 * Checkpoint data structure
 */
export interface CheckpointData {
  name: StartupCheckpoint;
  timestamp: number;
  success: boolean;
  error?: string;
}

/**
 * Get all checkpoint names in order
 */
export function getAllCheckpoints(): StartupCheckpoint[] {
  return Object.values(STARTUP_CHECKPOINTS);
}

/**
 * Find which checkpoint failed based on the list of passed checkpoints
 * Returns the first checkpoint that was not passed
 */
export function findFailedCheckpoint(passedCheckpoints: string[]): StartupCheckpoint {
  const allCheckpoints = getAllCheckpoints();

  for (const checkpoint of allCheckpoints) {
    if (!passedCheckpoints.includes(checkpoint)) {
      return checkpoint;
    }
  }

  // If all checkpoints were passed, the failure must have occurred after SERVER_READY
  // This would be an unexpected post-initialization failure
  return STARTUP_CHECKPOINTS.SERVER_READY;
}

/**
 * Validate if a string is a valid checkpoint
 */
export function isValidCheckpoint(checkpoint: string): checkpoint is StartupCheckpoint {
  return getAllCheckpoints().includes(checkpoint as StartupCheckpoint);
}

/**
 * Get human-readable description for a checkpoint
 */
export function getCheckpointDescription(checkpoint: StartupCheckpoint): string {
  const descriptions: Record<StartupCheckpoint, string> = {
    [STARTUP_CHECKPOINTS.PROCESS_STARTED]: 'Process initialization started',
    [STARTUP_CHECKPOINTS.DATABASE_CONNECTING]: 'Connecting to database',
    [STARTUP_CHECKPOINTS.DATABASE_CONNECTED]: 'Database connection established',
    [STARTUP_CHECKPOINTS.N8N_API_CHECKING]: 'Checking n8n API configuration',
    [STARTUP_CHECKPOINTS.N8N_API_READY]: 'n8n API ready',
    [STARTUP_CHECKPOINTS.TELEMETRY_INITIALIZING]: 'Initializing telemetry system',
    [STARTUP_CHECKPOINTS.TELEMETRY_READY]: 'Telemetry system ready',
    [STARTUP_CHECKPOINTS.MCP_HANDSHAKE_STARTING]: 'Starting MCP protocol handshake',
    [STARTUP_CHECKPOINTS.MCP_HANDSHAKE_COMPLETE]: 'MCP handshake completed',
    [STARTUP_CHECKPOINTS.SERVER_READY]: 'Server fully initialized and ready',
  };

  return descriptions[checkpoint] || 'Unknown checkpoint';
}

/**
 * Get the next expected checkpoint after the given one
 * Returns null if this is the last checkpoint
 */
export function getNextCheckpoint(current: StartupCheckpoint): StartupCheckpoint | null {
  const allCheckpoints = getAllCheckpoints();
  const currentIndex = allCheckpoints.indexOf(current);

  if (currentIndex === -1 || currentIndex === allCheckpoints.length - 1) {
    return null;
  }

  return allCheckpoints[currentIndex + 1];
}

/**
 * Calculate completion percentage based on checkpoints passed
 */
export function getCompletionPercentage(passedCheckpoints: string[]): number {
  const totalCheckpoints = getAllCheckpoints().length;
  const passedCount = passedCheckpoints.length;

  return Math.round((passedCount / totalCheckpoints) * 100);
}

```

--------------------------------------------------------------------------------
/tests/unit/services/expression-validator.test.ts:
--------------------------------------------------------------------------------

```typescript
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { ExpressionValidator } from '@/services/expression-validator';

describe('ExpressionValidator', () => {
  const defaultContext = {
    availableNodes: [],
    currentNodeName: 'TestNode',
    isInLoop: false,
    hasInputData: true
  };

  beforeEach(() => {
    vi.clearAllMocks();
  });

  describe('validateExpression', () => {
    it('should be a static method that validates expressions', () => {
      expect(typeof ExpressionValidator.validateExpression).toBe('function');
    });

    it('should return a validation result', () => {
      const result = ExpressionValidator.validateExpression('{{ $json.field }}', defaultContext);
      
      expect(result).toHaveProperty('valid');
      expect(result).toHaveProperty('errors');
      expect(result).toHaveProperty('warnings');
      expect(result).toHaveProperty('usedVariables');
      expect(result).toHaveProperty('usedNodes');
    });

    it('should validate expressions with proper syntax', () => {
      const validExpr = '{{ $json.field }}';
      const result = ExpressionValidator.validateExpression(validExpr, defaultContext);
      
      expect(result).toBeDefined();
      expect(Array.isArray(result.errors)).toBe(true);
    });

    it('should detect malformed expressions', () => {
      const invalidExpr = '{{ $json.field'; // Missing closing braces
      const result = ExpressionValidator.validateExpression(invalidExpr, defaultContext);
      
      expect(result.errors.length).toBeGreaterThan(0);
    });
  });

  describe('validateNodeExpressions', () => {
    it('should validate all expressions in node parameters', () => {
      const parameters = {
        field1: '{{ $json.data }}',
        nested: {
          field2: 'regular text',
          field3: '{{ $node["Webhook"].json }}'
        }
      };

      const result = ExpressionValidator.validateNodeExpressions(parameters, defaultContext);

      expect(result).toHaveProperty('valid');
      expect(result).toHaveProperty('errors');
      expect(result).toHaveProperty('warnings');
    });

    it('should collect errors from invalid expressions', () => {
      const parameters = {
        badExpr: '{{ $json.field', // Missing closing
        goodExpr: '{{ $json.field }}'
      };

      const result = ExpressionValidator.validateNodeExpressions(parameters, defaultContext);

      expect(result.errors.length).toBeGreaterThan(0);
    });
  });

  describe('expression patterns', () => {
    it('should recognize n8n variable patterns', () => {
      const expressions = [
        '{{ $json }}',
        '{{ $json.field }}',
        '{{ $node["NodeName"].json }}',
        '{{ $workflow.id }}',
        '{{ $now }}',
        '{{ $itemIndex }}'
      ];

      expressions.forEach(expr => {
        const result = ExpressionValidator.validateExpression(expr, defaultContext);
        expect(result).toBeDefined();
      });
    });
  });

  describe('context validation', () => {
    it('should use available nodes from context', () => {
      const contextWithNodes = {
        ...defaultContext,
        availableNodes: ['Webhook', 'Function', 'Slack']
      };

      const expr = '{{ $node["Webhook"].json }}';
      const result = ExpressionValidator.validateExpression(expr, contextWithNodes);

      expect(result.usedNodes.has('Webhook')).toBe(true);
    });
  });

  describe('edge cases', () => {
    it('should handle empty expressions', () => {
      const result = ExpressionValidator.validateExpression('{{ }}', defaultContext);
      // The implementation might consider empty expressions as valid
      expect(result).toBeDefined();
      expect(Array.isArray(result.errors)).toBe(true);
    });

    it('should handle non-expression text', () => {
      const result = ExpressionValidator.validateExpression('regular text without expressions', defaultContext);
      expect(result.valid).toBe(true);
      expect(result.errors).toHaveLength(0);
    });

    it('should handle nested expressions', () => {
      const expr = '{{ $json[{{ $json.index }}] }}'; // Nested expressions not allowed
      const result = ExpressionValidator.validateExpression(expr, defaultContext);
      expect(result).toBeDefined();
    });
  });
});
```

--------------------------------------------------------------------------------
/src/utils/bridge.ts:
--------------------------------------------------------------------------------

```typescript
import { INodeExecutionData, IDataObject } from 'n8n-workflow';

export class N8NMCPBridge {
  /**
   * Convert n8n workflow data to MCP tool arguments
   */
  static n8nToMCPToolArgs(data: IDataObject): any {
    // Handle different data formats from n8n
    if (data.json) {
      return data.json;
    }
    
    // Remove n8n-specific metadata
    const { pairedItem, ...cleanData } = data;
    return cleanData;
  }

  /**
   * Convert MCP tool response to n8n execution data
   */
  static mcpToN8NExecutionData(mcpResponse: any, itemIndex: number = 0): INodeExecutionData {
    // Handle MCP content array format
    if (mcpResponse.content && Array.isArray(mcpResponse.content)) {
      const textContent = mcpResponse.content
        .filter((c: any) => c.type === 'text')
        .map((c: any) => c.text)
        .join('\n');
      
      try {
        // Try to parse as JSON if possible
        const parsed = JSON.parse(textContent);
        return {
          json: parsed,
          pairedItem: itemIndex,
        };
      } catch {
        // Return as text if not JSON
        return {
          json: { result: textContent },
          pairedItem: itemIndex,
        };
      }
    }

    // Handle direct object response
    return {
      json: mcpResponse,
      pairedItem: itemIndex,
    };
  }

  /**
   * Convert n8n workflow definition to MCP-compatible format
   */
  static n8nWorkflowToMCP(workflow: any): any {
    return {
      id: workflow.id,
      name: workflow.name,
      description: workflow.description || '',
      nodes: workflow.nodes?.map((node: any) => ({
        id: node.id,
        type: node.type,
        name: node.name,
        parameters: node.parameters,
        position: node.position,
      })),
      connections: workflow.connections,
      settings: workflow.settings,
      metadata: {
        createdAt: workflow.createdAt,
        updatedAt: workflow.updatedAt,
        active: workflow.active,
      },
    };
  }

  /**
   * Convert MCP workflow format to n8n-compatible format
   */
  static mcpToN8NWorkflow(mcpWorkflow: any): any {
    return {
      name: mcpWorkflow.name,
      nodes: mcpWorkflow.nodes || [],
      connections: mcpWorkflow.connections || {},
      settings: mcpWorkflow.settings || {
        executionOrder: 'v1',
      },
      staticData: null,
      pinData: {},
    };
  }

  /**
   * Convert n8n execution data to MCP resource format
   */
  static n8nExecutionToMCPResource(execution: any): any {
    return {
      uri: `execution://${execution.id}`,
      name: `Execution ${execution.id}`,
      description: `Workflow: ${execution.workflowData?.name || 'Unknown'}`,
      mimeType: 'application/json',
      data: {
        id: execution.id,
        workflowId: execution.workflowId,
        status: execution.finished ? 'completed' : execution.stoppedAt ? 'stopped' : 'running',
        mode: execution.mode,
        startedAt: execution.startedAt,
        stoppedAt: execution.stoppedAt,
        error: execution.data?.resultData?.error,
        executionData: execution.data,
      },
    };
  }

  /**
   * Convert MCP prompt arguments to n8n-compatible format
   */
  static mcpPromptArgsToN8N(promptArgs: any): IDataObject {
    return {
      prompt: promptArgs.name || '',
      arguments: promptArgs.arguments || {},
      messages: promptArgs.messages || [],
    };
  }

  /**
   * Validate and sanitize data before conversion
   */
  static sanitizeData(data: any): any {
    if (data === null || data === undefined) {
      return {};
    }

    if (typeof data !== 'object') {
      return { value: data };
    }

    // Remove circular references
    const seen = new WeakSet();
    return JSON.parse(JSON.stringify(data, (_key, value) => {
      if (typeof value === 'object' && value !== null) {
        if (seen.has(value)) {
          return '[Circular]';
        }
        seen.add(value);
      }
      return value;
    }));
  }

  /**
   * Extract error information for both n8n and MCP formats
   */
  static formatError(error: any): any {
    return {
      message: error.message || 'Unknown error',
      type: error.name || 'Error',
      stack: error.stack,
      details: {
        code: error.code,
        statusCode: error.statusCode,
        data: error.data,
      },
    };
  }
}
```

--------------------------------------------------------------------------------
/src/mcp/tool-docs/templates/search-templates.ts:
--------------------------------------------------------------------------------

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

export const searchTemplatesDoc: ToolDocumentation = {
  name: 'search_templates',
  category: 'templates',
  essentials: {
    description: 'Search templates by name/description keywords. NOT for node types! For nodes use list_node_templates. Example: "chatbot".',
    keyParameters: ['query', 'limit', 'fields'],
    example: 'search_templates({query: "chatbot", fields: ["id", "name"]})',
    performance: 'Fast (<100ms) - FTS5 full-text search',
    tips: [
      'Searches template names and descriptions, NOT node types',
      'Use keywords like "automation", "sync", "notification"',
      'For node-specific search, use list_node_templates instead',
      'Use fields parameter to get only specific data (reduces response by 70-90%)'
    ]
  },
  full: {
    description: `Performs full-text search across workflow template names and descriptions. This tool is ideal for finding workflows based on their purpose or functionality rather than specific nodes used. It searches through the community library of 399+ templates using SQLite FTS5 for fast, fuzzy matching.`,
    parameters: {
      query: {
        type: 'string',
        required: true,
        description: 'Search query for template names/descriptions. NOT for node types! Examples: "chatbot", "automation", "social media", "webhook". For node-based search use list_node_templates instead.'
      },
      fields: {
        type: 'array',
        required: false,
        description: 'Fields to include in response. Options: "id", "name", "description", "author", "nodes", "views", "created", "url", "metadata". Default: all fields. Example: ["id", "name"] for minimal response.'
      },
      limit: {
        type: 'number',
        required: false,
        description: 'Maximum number of results. Default 20, max 100'
      }
    },
    returns: `Returns an object containing:
- templates: Array of matching templates sorted by relevance
  - id: Template ID for retrieval
  - name: Template name (with match highlights)
  - description: What the workflow does
  - author: Creator information
  - nodes: Array of all nodes used
  - views: Popularity metric
  - created: Creation date
  - url: Link to template
  - relevanceScore: Search match score
- totalFound: Total matching templates
- searchQuery: The processed search query
- tip: Helpful hints if no results`,
    examples: [
      'search_templates({query: "chatbot"}) - Find chatbot and conversational AI workflows',
      'search_templates({query: "email notification"}) - Find email alert workflows',
      'search_templates({query: "data sync"}) - Find data synchronization workflows',
      'search_templates({query: "webhook automation", limit: 30}) - Find webhook-based automations',
      'search_templates({query: "social media scheduler"}) - Find social posting workflows',
      'search_templates({query: "slack", fields: ["id", "name"]}) - Get only IDs and names of Slack templates',
      'search_templates({query: "automation", fields: ["id", "name", "description"]}) - Get minimal info for automation templates'
    ],
    useCases: [
      'Find workflows by business purpose',
      'Discover automations for specific use cases',
      'Search by workflow functionality',
      'Find templates by problem they solve',
      'Explore workflows by industry or domain'
    ],
    performance: `Excellent performance with FTS5 indexing:
- Full-text search: <50ms for most queries
- Fuzzy matching enabled for typos
- Relevance-based sorting included
- Searches both title and description
- Returns highlighted matches`,
    bestPractices: [
      'Use descriptive keywords about the workflow purpose',
      'Try multiple related terms if first search has few results',
      'Combine terms for more specific results',
      'Check both name and description in results',
      'Use quotes for exact phrase matching'
    ],
    pitfalls: [
      'Does NOT search by node types - use list_node_templates',
      'Search is case-insensitive but not semantic',
      'Very specific terms may return no results',
      'Descriptions may be brief - check full template',
      'Relevance scoring may not match your expectations'
    ],
    relatedTools: ['list_node_templates', 'get_templates_for_task', 'get_template', 'search_nodes']
  }
};
```

--------------------------------------------------------------------------------
/tests/integration/n8n-api/workflows/delete-workflow.test.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Integration Tests: handleDeleteWorkflow
 *
 * Tests workflow deletion against a real n8n instance.
 * Covers successful deletion, error handling, and cleanup verification.
 */

import { describe, it, expect, beforeEach, afterEach, afterAll } from 'vitest';
import { createTestContext, TestContext, createTestWorkflowName } from '../utils/test-context';
import { getTestN8nClient } from '../utils/n8n-client';
import { N8nApiClient } from '../../../../src/services/n8n-api-client';
import { SIMPLE_WEBHOOK_WORKFLOW } from '../utils/fixtures';
import { cleanupOrphanedWorkflows } from '../utils/cleanup-helpers';
import { createMcpContext } from '../utils/mcp-context';
import { InstanceContext } from '../../../../src/types/instance-context';
import { handleDeleteWorkflow } from '../../../../src/mcp/handlers-n8n-manager';

describe('Integration: handleDeleteWorkflow', () => {
  let context: TestContext;
  let client: N8nApiClient;
  let mcpContext: InstanceContext;

  beforeEach(() => {
    context = createTestContext();
    client = getTestN8nClient();
    mcpContext = createMcpContext();
  });

  afterEach(async () => {
    await context.cleanup();
  });

  afterAll(async () => {
    if (!process.env.CI) {
      await cleanupOrphanedWorkflows();
    }
  });

  // ======================================================================
  // Successful Deletion
  // ======================================================================

  describe('Successful Deletion', () => {
    it('should delete an existing workflow', async () => {
      // Create workflow
      const workflow = {
        ...SIMPLE_WEBHOOK_WORKFLOW,
        name: createTestWorkflowName('Delete - Success'),
        tags: ['mcp-integration-test']
      };

      const created = await client.createWorkflow(workflow);
      expect(created.id).toBeTruthy();
      if (!created.id) throw new Error('Workflow ID is missing');

      // Do NOT track workflow since we're testing deletion
      // context.trackWorkflow(created.id);

      // Delete using MCP handler
      const response = await handleDeleteWorkflow(
        { id: created.id },
        mcpContext
      );

      // Verify MCP response
      expect(response.success).toBe(true);
      expect(response.data).toBeDefined();

      // Verify workflow is actually deleted
      await expect(async () => {
        await client.getWorkflow(created.id!);
      }).rejects.toThrow();
    });
  });

  // ======================================================================
  // Error Handling
  // ======================================================================

  describe('Error Handling', () => {
    it('should return error for non-existent workflow ID', async () => {
      const response = await handleDeleteWorkflow(
        { id: '99999999' },
        mcpContext
      );

      expect(response.success).toBe(false);
      expect(response.error).toBeDefined();
    });
  });

  // ======================================================================
  // Cleanup Verification
  // ======================================================================

  describe('Cleanup Verification', () => {
    it('should verify workflow is actually deleted from n8n', async () => {
      // Create workflow
      const workflow = {
        ...SIMPLE_WEBHOOK_WORKFLOW,
        name: createTestWorkflowName('Delete - Cleanup Check'),
        tags: ['mcp-integration-test']
      };

      const created = await client.createWorkflow(workflow);
      expect(created.id).toBeTruthy();
      if (!created.id) throw new Error('Workflow ID is missing');

      // Verify workflow exists
      const beforeDelete = await client.getWorkflow(created.id);
      expect(beforeDelete.id).toBe(created.id);

      // Delete workflow
      const deleteResponse = await handleDeleteWorkflow(
        { id: created.id },
        mcpContext
      );

      expect(deleteResponse.success).toBe(true);

      // Verify workflow no longer exists
      try {
        await client.getWorkflow(created.id);
        // If we reach here, workflow wasn't deleted
        throw new Error('Workflow should have been deleted but still exists');
      } catch (error: any) {
        // Expected: workflow should not be found
        expect(error.message).toMatch(/not found|404/i);
      }
    });
  });
});

```

--------------------------------------------------------------------------------
/src/scripts/fetch-templates-robust.ts:
--------------------------------------------------------------------------------

```typescript
#!/usr/bin/env node
import { createDatabaseAdapter } from '../database/database-adapter';
import { TemplateRepository } from '../templates/template-repository';
import { TemplateFetcher } from '../templates/template-fetcher';
import * as fs from 'fs';
import * as path from 'path';

async function fetchTemplatesRobust() {
  console.log('🌐 Fetching n8n workflow templates (last year)...\n');
  
  // Ensure data directory exists
  const dataDir = './data';
  if (!fs.existsSync(dataDir)) {
    fs.mkdirSync(dataDir, { recursive: true });
  }
  
  // Initialize database
  const db = await createDatabaseAdapter('./data/nodes.db');
  
  // Drop existing templates table to ensure clean schema
  try {
    db.exec('DROP TABLE IF EXISTS templates');
    db.exec('DROP TABLE IF EXISTS templates_fts');
    console.log('🗑️  Dropped existing templates tables\n');
  } catch (error) {
    // Ignore errors if tables don't exist
  }
  
  // Apply schema with updated constraint
  const schema = fs.readFileSync(path.join(__dirname, '../../src/database/schema.sql'), 'utf8');
  db.exec(schema);
  
  // Create repository and fetcher
  const repository = new TemplateRepository(db);
  const fetcher = new TemplateFetcher();
  
  // Progress tracking
  let lastMessage = '';
  const startTime = Date.now();
  
  try {
    // Fetch template list
    console.log('📋 Phase 1: Fetching template list from n8n.io API\n');
    const templates = await fetcher.fetchTemplates((current, total) => {
      // Clear previous line
      if (lastMessage) {
        process.stdout.write('\r' + ' '.repeat(lastMessage.length) + '\r');
      }
      
      const progress = Math.round((current / total) * 100);
      lastMessage = `📊 Fetching template list: ${current}/${total} (${progress}%)`;
      process.stdout.write(lastMessage);
    });
    
    console.log('\n');
    console.log(`✅ Found ${templates.length} templates from last year\n`);
    
    // Fetch details and save incrementally
    console.log('📥 Phase 2: Fetching details and saving to database\n');
    let saved = 0;
    let errors = 0;
    
    for (let i = 0; i < templates.length; i++) {
      const template = templates[i];
      
      try {
        // Clear previous line
        if (lastMessage) {
          process.stdout.write('\r' + ' '.repeat(lastMessage.length) + '\r');
        }
        
        const progress = Math.round(((i + 1) / templates.length) * 100);
        lastMessage = `📊 Processing: ${i + 1}/${templates.length} (${progress}%) - Saved: ${saved}, Errors: ${errors}`;
        process.stdout.write(lastMessage);
        
        // Fetch detail
        const detail = await fetcher.fetchTemplateDetail(template.id);
        
        // Save immediately
        repository.saveTemplate(template, detail);
        saved++;
        
        // Rate limiting
        await new Promise(resolve => setTimeout(resolve, 200));
      } catch (error: any) {
        errors++;
        console.error(`\n❌ Error processing template ${template.id} (${template.name}): ${error.message}`);
        // Continue with next template
      }
    }
    
    console.log('\n');
    
    // Get stats
    const elapsed = Math.round((Date.now() - startTime) / 1000);
    const stats = await repository.getTemplateStats();
    
    console.log('✅ Template fetch complete!\n');
    console.log('📈 Statistics:');
    console.log(`   - Templates found: ${templates.length}`);
    console.log(`   - Templates saved: ${saved}`);
    console.log(`   - Errors: ${errors}`);
    console.log(`   - Success rate: ${Math.round((saved / templates.length) * 100)}%`);
    console.log(`   - Time elapsed: ${elapsed} seconds`);
    console.log(`   - Average time per template: ${(elapsed / saved).toFixed(2)} seconds`);
    
    if (stats.topUsedNodes && stats.topUsedNodes.length > 0) {
      console.log('\n🔝 Top used nodes:');
      stats.topUsedNodes.slice(0, 10).forEach((node: any, index: number) => {
        console.log(`   ${index + 1}. ${node.node} (${node.count} templates)`);
      });
    }
    
  } catch (error) {
    console.error('\n❌ Fatal error:', error);
    process.exit(1);
  }
  
  // Close database
  if ('close' in db && typeof db.close === 'function') {
    db.close();
  }
}

// Run if called directly
if (require.main === module) {
  fetchTemplatesRobust().catch(console.error);
}

export { fetchTemplatesRobust };
```

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

```yaml
# .github/workflows/docker-build.yml
name: Build and Push Docker Images

on:
  push:
    branches:
      - main
    tags:
      - 'v*'
    paths-ignore:
      - '**.md'
      - '**.txt'
      - 'docs/**'
      - 'examples/**'
      - '.github/FUNDING.yml'
      - '.github/ISSUE_TEMPLATE/**'
      - '.github/pull_request_template.md'
      - '.gitignore'
      - 'LICENSE*'
      - 'ATTRIBUTION.md'
      - 'SECURITY.md'
      - 'CODE_OF_CONDUCT.md'
  pull_request:
    branches:
      - main
    paths-ignore:
      - '**.md'
      - '**.txt'
      - 'docs/**'
      - 'examples/**'
      - '.github/FUNDING.yml'
      - '.github/ISSUE_TEMPLATE/**'
      - '.github/pull_request_template.md'
      - '.gitignore'
      - 'LICENSE*'
      - 'ATTRIBUTION.md'
      - 'SECURITY.md'
      - 'CODE_OF_CONDUCT.md'
  workflow_dispatch:

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  build:
    name: Build Docker Image
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
      
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          lfs: true
        
      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3
        
      - name: Set up Docker Buildx
        id: buildx
        uses: docker/setup-buildx-action@v3
        
      - name: Log in to GitHub Container Registry
        if: github.event_name != 'pull_request'
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
          
      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          tags: |
            type=ref,event=branch
            type=ref,event=pr
            type=semver,pattern={{version}}
            type=semver,pattern={{major}}.{{minor}}
            type=semver,pattern={{major}}
            type=sha,format=short
            type=raw,value=latest,enable={{is_default_branch}}
            
      - name: Build and push Docker image
        uses: docker/build-push-action@v5
        with:
          context: .
          no-cache: true
          platforms: linux/amd64,linux/arm64
          push: ${{ github.event_name != 'pull_request' }}
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          provenance: false

  build-railway:
    name: Build Railway Docker Image
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
      
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          lfs: true
        
      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3
        
      - name: Set up Docker Buildx
        id: buildx
        uses: docker/setup-buildx-action@v3
        
      - name: Log in to GitHub Container Registry
        if: github.event_name != 'pull_request'
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
          
      - name: Extract metadata for Railway
        id: meta-railway
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-railway
          tags: |
            type=ref,event=branch
            type=ref,event=pr
            type=semver,pattern={{version}}
            type=semver,pattern={{major}}.{{minor}}
            type=semver,pattern={{major}}
            type=sha,format=short
            type=raw,value=latest,enable={{is_default_branch}}
            
      - name: Build and push Railway Docker image
        uses: docker/build-push-action@v5
        with:
          context: .
          file: ./Dockerfile.railway
          no-cache: true
          platforms: linux/amd64
          push: ${{ github.event_name != 'pull_request' }}
          tags: ${{ steps.meta-railway.outputs.tags }}
          labels: ${{ steps.meta-railway.outputs.labels }}
          provenance: false

  # Nginx build commented out until Phase 2
  # build-nginx:
  #   name: Build nginx-enhanced Docker Image
  #   runs-on: ubuntu-latest
  #   permissions:
  #     contents: read
  #     packages: write
```

--------------------------------------------------------------------------------
/tests/fixtures/database/test-nodes.json:
--------------------------------------------------------------------------------

```json
{
  "nodes": [
    {
      "style": "programmatic",
      "nodeType": "nodes-base.httpRequest",
      "displayName": "HTTP Request",
      "description": "Makes HTTP requests and returns the response",
      "category": "Core Nodes",
      "properties": [
        {
          "name": "url",
          "displayName": "URL",
          "type": "string",
          "required": true,
          "default": ""
        },
        {
          "name": "method",
          "displayName": "Method",
          "type": "options",
          "options": [
            { "name": "GET", "value": "GET" },
            { "name": "POST", "value": "POST" },
            { "name": "PUT", "value": "PUT" },
            { "name": "DELETE", "value": "DELETE" }
          ],
          "default": "GET"
        }
      ],
      "credentials": [],
      "isAITool": true,
      "isTrigger": false,
      "isWebhook": false,
      "operations": [],
      "version": "1",
      "isVersioned": false,
      "packageName": "n8n-nodes-base",
      "documentation": "The HTTP Request node makes HTTP requests and returns the response data."
    },
    {
      "style": "programmatic",
      "nodeType": "nodes-base.webhook",
      "displayName": "Webhook",
      "description": "Receives data from external services via webhooks",
      "category": "Core Nodes",
      "properties": [
        {
          "name": "httpMethod",
          "displayName": "HTTP Method",
          "type": "options",
          "options": [
            { "name": "GET", "value": "GET" },
            { "name": "POST", "value": "POST" }
          ],
          "default": "POST"
        },
        {
          "name": "path",
          "displayName": "Path",
          "type": "string",
          "default": "webhook"
        }
      ],
      "credentials": [],
      "isAITool": false,
      "isTrigger": true,
      "isWebhook": true,
      "operations": [],
      "version": "1",
      "isVersioned": false,
      "packageName": "n8n-nodes-base",
      "documentation": "The Webhook node creates an endpoint to receive data from external services."
    },
    {
      "style": "declarative",
      "nodeType": "nodes-base.slack",
      "displayName": "Slack",
      "description": "Send messages and interact with Slack",
      "category": "Communication",
      "properties": [],
      "credentials": [
        {
          "name": "slackApi",
          "required": true
        }
      ],
      "isAITool": true,
      "isTrigger": false,
      "isWebhook": false,
      "operations": [
        {
          "name": "Message",
          "value": "message",
          "operations": [
            {
              "name": "Send",
              "value": "send",
              "description": "Send a message to a channel or user"
            }
          ]
        }
      ],
      "version": "2.1",
      "isVersioned": true,
      "packageName": "n8n-nodes-base",
      "documentation": "The Slack node allows you to send messages and interact with Slack workspaces."
    }
  ],
  "templates": [
    {
      "id": 1001,
      "name": "HTTP to Webhook",
      "description": "Fetch data from HTTP and send to webhook",
      "workflow": {
        "nodes": [
          {
            "id": "1",
            "name": "HTTP Request",
            "type": "n8n-nodes-base.httpRequest",
            "position": [250, 300],
            "parameters": {
              "url": "https://api.example.com/data",
              "method": "GET"
            }
          },
          {
            "id": "2",
            "name": "Webhook",
            "type": "n8n-nodes-base.webhook",
            "position": [450, 300],
            "parameters": {
              "path": "data-webhook",
              "httpMethod": "POST"
            }
          }
        ],
        "connections": {
          "HTTP Request": {
            "main": [[{ "node": "Webhook", "type": "main", "index": 0 }]]
          }
        }
      },
      "nodes": [
        { "id": 1, "name": "HTTP Request", "icon": "http" },
        { "id": 2, "name": "Webhook", "icon": "webhook" }
      ],
      "categories": ["Data Processing"],
      "user": {
        "id": 1,
        "name": "Test User",
        "username": "testuser",
        "verified": false
      },
      "views": 150,
      "createdAt": "2024-01-15T10:00:00Z",
      "updatedAt": "2024-01-20T15:30:00Z",
      "totalViews": 150
    }
  ]
}
```

--------------------------------------------------------------------------------
/src/utils/node-type-utils.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Utility functions for working with n8n node types
 * Provides consistent normalization and transformation of node type strings
 */

/**
 * Normalize a node type to the standard short form
 * Handles both old-style (n8n-nodes-base.) and new-style (nodes-base.) prefixes
 *
 * @example
 * normalizeNodeType('n8n-nodes-base.httpRequest') // 'nodes-base.httpRequest'
 * normalizeNodeType('@n8n/n8n-nodes-langchain.openAi') // 'nodes-langchain.openAi'
 * normalizeNodeType('nodes-base.webhook') // 'nodes-base.webhook' (unchanged)
 */
export function normalizeNodeType(type: string): string {
  if (!type) return type;

  return type
    .replace(/^n8n-nodes-base\./, 'nodes-base.')
    .replace(/^@n8n\/n8n-nodes-langchain\./, 'nodes-langchain.');
}

/**
 * Convert a short-form node type to the full package name
 *
 * @example
 * denormalizeNodeType('nodes-base.httpRequest', 'base') // 'n8n-nodes-base.httpRequest'
 * denormalizeNodeType('nodes-langchain.openAi', 'langchain') // '@n8n/n8n-nodes-langchain.openAi'
 */
export function denormalizeNodeType(type: string, packageType: 'base' | 'langchain'): string {
  if (!type) return type;

  if (packageType === 'base') {
    return type.replace(/^nodes-base\./, 'n8n-nodes-base.');
  }

  return type.replace(/^nodes-langchain\./, '@n8n/n8n-nodes-langchain.');
}

/**
 * Extract the node name from a full node type
 *
 * @example
 * extractNodeName('nodes-base.httpRequest') // 'httpRequest'
 * extractNodeName('n8n-nodes-base.webhook') // 'webhook'
 */
export function extractNodeName(type: string): string {
  if (!type) return '';

  // First normalize the type
  const normalized = normalizeNodeType(type);

  // Extract everything after the last dot
  const parts = normalized.split('.');
  return parts[parts.length - 1] || '';
}

/**
 * Get the package prefix from a node type
 *
 * @example
 * getNodePackage('nodes-base.httpRequest') // 'nodes-base'
 * getNodePackage('nodes-langchain.openAi') // 'nodes-langchain'
 */
export function getNodePackage(type: string): string | null {
  if (!type || !type.includes('.')) return null;

  // First normalize the type
  const normalized = normalizeNodeType(type);

  // Extract everything before the first dot
  const parts = normalized.split('.');
  return parts[0] || null;
}

/**
 * Check if a node type is from the base package
 */
export function isBaseNode(type: string): boolean {
  const normalized = normalizeNodeType(type);
  return normalized.startsWith('nodes-base.');
}

/**
 * Check if a node type is from the langchain package
 */
export function isLangChainNode(type: string): boolean {
  const normalized = normalizeNodeType(type);
  return normalized.startsWith('nodes-langchain.');
}

/**
 * Validate if a string looks like a valid node type
 * (has package prefix and node name)
 */
export function isValidNodeTypeFormat(type: string): boolean {
  if (!type || typeof type !== 'string') return false;

  // Must contain at least one dot
  if (!type.includes('.')) return false;

  const parts = type.split('.');

  // Must have exactly 2 parts (package and node name)
  if (parts.length !== 2) return false;

  // Both parts must be non-empty
  return parts[0].length > 0 && parts[1].length > 0;
}

/**
 * Try multiple variations of a node type to find a match
 * Returns an array of variations to try in order
 *
 * @example
 * getNodeTypeVariations('httpRequest')
 * // ['nodes-base.httpRequest', 'n8n-nodes-base.httpRequest', 'nodes-langchain.httpRequest', ...]
 */
export function getNodeTypeVariations(type: string): string[] {
  const variations: string[] = [];

  // If it already has a package prefix, try normalized version first
  if (type.includes('.')) {
    variations.push(normalizeNodeType(type));

    // Also try the denormalized versions
    const normalized = normalizeNodeType(type);
    if (normalized.startsWith('nodes-base.')) {
      variations.push(denormalizeNodeType(normalized, 'base'));
    } else if (normalized.startsWith('nodes-langchain.')) {
      variations.push(denormalizeNodeType(normalized, 'langchain'));
    }
  } else {
    // No package prefix, try common packages
    variations.push(`nodes-base.${type}`);
    variations.push(`n8n-nodes-base.${type}`);
    variations.push(`nodes-langchain.${type}`);
    variations.push(`@n8n/n8n-nodes-langchain.${type}`);
  }

  // Remove duplicates while preserving order
  return [...new Set(variations)];
}
```

--------------------------------------------------------------------------------
/tests/integration/session/test-onSessionCreated-event.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Test to verify that onSessionCreated event is fired during standard initialize flow
 * This test addresses the bug reported in v2.19.0 where the event was not fired
 * for sessions created during the initialize request.
 */

import { SingleSessionHTTPServer } from '../../../src/http-server-single-session';
import { InstanceContext } from '../../../src/types/instance-context';

// Mock environment setup
process.env.AUTH_TOKEN = 'test-token-for-n8n-testing-minimum-32-chars';
process.env.NODE_ENV = 'test';
process.env.PORT = '3456'; // Use different port to avoid conflicts

async function testOnSessionCreatedEvent() {
  console.log('\n🧪 Test: onSessionCreated Event Firing During Initialize\n');
  console.log('━'.repeat(60));

  let eventFired = false;
  let capturedSessionId: string | undefined;
  let capturedContext: InstanceContext | undefined;

  // Create server with onSessionCreated handler
  const server = new SingleSessionHTTPServer({
    sessionEvents: {
      onSessionCreated: async (sessionId: string, instanceContext?: InstanceContext) => {
        console.log('✅ onSessionCreated event fired!');
        console.log(`   Session ID: ${sessionId}`);
        console.log(`   Context: ${instanceContext ? 'Present' : 'Not provided'}`);
        eventFired = true;
        capturedSessionId = sessionId;
        capturedContext = instanceContext;
      }
    }
  });

  try {
    // Start the HTTP server
    console.log('\n📡 Starting HTTP server...');
    await server.start();
    console.log('✅ Server started\n');

    // Wait a moment for server to be ready
    await new Promise(resolve => setTimeout(resolve, 500));

    // Simulate an MCP initialize request
    console.log('📤 Simulating MCP initialize request...');

    const port = parseInt(process.env.PORT || '3456');
    const fetch = (await import('node-fetch')).default;

    const response = await fetch(`http://localhost:${port}/mcp`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer test-token-for-n8n-testing-minimum-32-chars',
        'Accept': 'application/json, text/event-stream'
      },
      body: JSON.stringify({
        jsonrpc: '2.0',
        method: 'initialize',
        params: {
          protocolVersion: '2024-11-05',
          capabilities: {},
          clientInfo: {
            name: 'test-client',
            version: '1.0.0'
          }
        },
        id: 1
      })
    });

    const result = await response.json() as any;

    console.log('📥 Response received:', response.status);
    console.log('   Response body:', JSON.stringify(result, null, 2));

    // Wait a moment for event to be processed
    await new Promise(resolve => setTimeout(resolve, 1000));

    // Verify results
    console.log('\n🔍 Verification:');
    console.log('━'.repeat(60));

    if (eventFired) {
      console.log('✅ SUCCESS: onSessionCreated event was fired');
      console.log(`   Captured Session ID: ${capturedSessionId}`);
      console.log(`   Context provided: ${capturedContext !== undefined}`);

      // Verify session is in active sessions list
      const activeSessions = server.getActiveSessions();
      console.log(`\n📊 Active sessions count: ${activeSessions.length}`);

      if (activeSessions.length > 0) {
        console.log('✅ Session registered in active sessions list');
        console.log(`   Session IDs: ${activeSessions.join(', ')}`);
      } else {
        console.log('❌ No active sessions found');
      }

      // Check if captured session ID is in active sessions
      if (capturedSessionId && activeSessions.includes(capturedSessionId)) {
        console.log('✅ Event session ID matches active session');
      } else {
        console.log('⚠️  Event session ID not found in active sessions');
      }

      console.log('\n🎉 TEST PASSED: Bug is fixed!');
      console.log('━'.repeat(60));

    } else {
      console.log('❌ FAILURE: onSessionCreated event was NOT fired');
      console.log('━'.repeat(60));
      console.log('\n💔 TEST FAILED: Bug still exists');
    }

    // Cleanup
    await server.shutdown();

    return eventFired;

  } catch (error) {
    console.error('\n❌ Test error:', error);
    await server.shutdown();
    return false;
  }
}

// Run the test
testOnSessionCreatedEvent()
  .then(success => {
    process.exit(success ? 0 : 1);
  })
  .catch(error => {
    console.error('Unhandled error:', error);
    process.exit(1);
  });

```

--------------------------------------------------------------------------------
/tests/integration/n8n-api/system/health-check.test.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Integration Tests: handleHealthCheck
 *
 * Tests API health check against a real n8n instance.
 * Covers connectivity verification and feature availability.
 */

import { describe, it, expect, beforeEach } from 'vitest';
import { createMcpContext } from '../utils/mcp-context';
import { InstanceContext } from '../../../../src/types/instance-context';
import { handleHealthCheck } from '../../../../src/mcp/handlers-n8n-manager';
import { HealthCheckResponse } from '../utils/response-types';

describe('Integration: handleHealthCheck', () => {
  let mcpContext: InstanceContext;

  beforeEach(() => {
    mcpContext = createMcpContext();
  });

  // ======================================================================
  // Successful Health Check
  // ======================================================================

  describe('API Available', () => {
    it('should successfully check n8n API health', async () => {
      const response = await handleHealthCheck(mcpContext);

      expect(response.success).toBe(true);
      expect(response.data).toBeDefined();

      const data = response.data as HealthCheckResponse;

      // Verify required fields
      expect(data).toHaveProperty('status');
      expect(data).toHaveProperty('apiUrl');
      expect(data).toHaveProperty('mcpVersion');
      expect(data).toHaveProperty('versionCheck');
      expect(data).toHaveProperty('performance');
      expect(data).toHaveProperty('nextSteps');

      // Status should be a string (e.g., "ok", "healthy")
      if (data.status) {
        expect(typeof data.status).toBe('string');
      }

      // API URL should match configuration
      expect(data.apiUrl).toBeDefined();
      expect(typeof data.apiUrl).toBe('string');

      // MCP version should be defined
      expect(data.mcpVersion).toBeDefined();
      expect(typeof data.mcpVersion).toBe('string');

      // Version check should be present
      expect(data.versionCheck).toBeDefined();
      expect(data.versionCheck).toHaveProperty('current');
      expect(data.versionCheck).toHaveProperty('upToDate');
      expect(typeof data.versionCheck.upToDate).toBe('boolean');

      // Performance metrics should be present
      expect(data.performance).toBeDefined();
      expect(data.performance).toHaveProperty('responseTimeMs');
      expect(typeof data.performance.responseTimeMs).toBe('number');
      expect(data.performance.responseTimeMs).toBeGreaterThan(0);

      // Next steps should be present
      expect(data.nextSteps).toBeDefined();
      expect(Array.isArray(data.nextSteps)).toBe(true);
    });

    it('should include feature availability information', async () => {
      const response = await handleHealthCheck(mcpContext);

      expect(response.success).toBe(true);
      const data = response.data as HealthCheckResponse;

      // Check for feature information
      // Note: Features may vary by n8n instance configuration
      if (data.features) {
        expect(typeof data.features).toBe('object');
      }

      // Check for version information
      if (data.n8nVersion) {
        expect(typeof data.n8nVersion).toBe('string');
      }

      if (data.supportedN8nVersion) {
        expect(typeof data.supportedN8nVersion).toBe('string');
      }

      // Should include version note for AI agents
      if (data.versionNote) {
        expect(typeof data.versionNote).toBe('string');
        expect(data.versionNote).toContain('version');
      }
    });
  });

  // ======================================================================
  // Response Format Verification
  // ======================================================================

  describe('Response Format', () => {
    it('should return complete health check response structure', async () => {
      const response = await handleHealthCheck(mcpContext);

      expect(response.success).toBe(true);
      expect(response.data).toBeDefined();

      const data = response.data as HealthCheckResponse;

      // Verify all expected fields are present
      const expectedFields = ['status', 'apiUrl', 'mcpVersion'];
      expectedFields.forEach(field => {
        expect(data).toHaveProperty(field);
      });

      // Optional fields that may be present
      const optionalFields = ['instanceId', 'n8nVersion', 'features', 'supportedN8nVersion', 'versionNote'];
      optionalFields.forEach(field => {
        if (data[field] !== undefined) {
          expect(data[field]).not.toBeNull();
        }
      });
    });
  });
});

```

--------------------------------------------------------------------------------
/tests/integration/n8n-api/executions/delete-execution.test.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Integration Tests: handleDeleteExecution
 *
 * Tests execution deletion against a real n8n instance.
 * Covers successful deletion, error handling, and cleanup verification.
 */

import { describe, it, expect, beforeEach, beforeAll } from 'vitest';
import { createMcpContext } from '../utils/mcp-context';
import { InstanceContext } from '../../../../src/types/instance-context';
import { handleDeleteExecution, handleTriggerWebhookWorkflow, handleGetExecution } from '../../../../src/mcp/handlers-n8n-manager';
import { getN8nCredentials } from '../utils/credentials';

describe('Integration: handleDeleteExecution', () => {
  let mcpContext: InstanceContext;
  let webhookUrl: string;

  beforeEach(() => {
    mcpContext = createMcpContext();
  });

  beforeAll(() => {
    const creds = getN8nCredentials();
    webhookUrl = creds.webhookUrls.get;
  });

  // ======================================================================
  // Successful Deletion
  // ======================================================================

  describe('Successful Deletion', () => {
    it('should delete an execution successfully', async () => {
      // First, create an execution to delete
      const triggerResponse = await handleTriggerWebhookWorkflow(
        {
          webhookUrl,
          httpMethod: 'GET',
          waitForResponse: true
        },
        mcpContext
      );

      // Try to extract execution ID
      let executionId: string | undefined;
      if (triggerResponse.success && triggerResponse.data) {
        const responseData = triggerResponse.data as any;
        executionId = responseData.executionId ||
                      responseData.id ||
                      responseData.execution?.id ||
                      responseData.workflowData?.executionId;
      }

      if (!executionId) {
        console.warn('Could not extract execution ID for deletion test');
        return;
      }

      // Delete the execution
      const response = await handleDeleteExecution(
        { id: executionId },
        mcpContext
      );

      expect(response.success).toBe(true);
      expect(response.data).toBeDefined();
    }, 30000);

    it('should verify execution is actually deleted', async () => {
      // Create an execution
      const triggerResponse = await handleTriggerWebhookWorkflow(
        {
          webhookUrl,
          httpMethod: 'GET',
          waitForResponse: true
        },
        mcpContext
      );

      let executionId: string | undefined;
      if (triggerResponse.success && triggerResponse.data) {
        const responseData = triggerResponse.data as any;
        executionId = responseData.executionId ||
                      responseData.id ||
                      responseData.execution?.id ||
                      responseData.workflowData?.executionId;
      }

      if (!executionId) {
        console.warn('Could not extract execution ID for deletion verification test');
        return;
      }

      // Delete it
      const deleteResponse = await handleDeleteExecution(
        { id: executionId },
        mcpContext
      );

      expect(deleteResponse.success).toBe(true);

      // Try to fetch the deleted execution
      const getResponse = await handleGetExecution(
        { id: executionId },
        mcpContext
      );

      // Should fail to find the deleted execution
      expect(getResponse.success).toBe(false);
      expect(getResponse.error).toBeDefined();
    }, 30000);
  });

  // ======================================================================
  // Error Handling
  // ======================================================================

  describe('Error Handling', () => {
    it('should handle non-existent execution ID', async () => {
      const response = await handleDeleteExecution(
        { id: '99999999' },
        mcpContext
      );

      expect(response.success).toBe(false);
      expect(response.error).toBeDefined();
    });

    it('should handle invalid execution ID format', async () => {
      const response = await handleDeleteExecution(
        { id: 'invalid-id-format' },
        mcpContext
      );

      expect(response.success).toBe(false);
      expect(response.error).toBeDefined();
    });

    it('should handle missing execution ID', async () => {
      const response = await handleDeleteExecution(
        {} as any,
        mcpContext
      );

      expect(response.success).toBe(false);
      expect(response.error).toBeDefined();
    });
  });
});

```

--------------------------------------------------------------------------------
/src/scripts/validate.ts:
--------------------------------------------------------------------------------

```typescript
#!/usr/bin/env node
/**
 * Copyright (c) 2024 AiAdvisors Romuald Czlonkowski
 * Licensed under the Sustainable Use License v1.0
 */
import { createDatabaseAdapter } from '../database/database-adapter';

interface NodeRow {
  node_type: string;
  package_name: string;
  display_name: string;
  description?: string;
  category?: string;
  development_style?: string;
  is_ai_tool: number;
  is_trigger: number;
  is_webhook: number;
  is_versioned: number;
  version?: string;
  documentation?: string;
  properties_schema?: string;
  operations?: string;
  credentials_required?: string;
  updated_at: string;
}

async function validate() {
  const db = await createDatabaseAdapter('./data/nodes.db');
  
  console.log('🔍 Validating critical nodes...\n');
  
  const criticalChecks = [
    { 
      type: 'nodes-base.httpRequest', 
      checks: {
        hasDocumentation: true,
        documentationContains: 'HTTP Request',
        style: 'programmatic'
      }
    },
    { 
      type: 'nodes-base.code', 
      checks: {
        hasDocumentation: true,
        documentationContains: 'Code'
      }
    },
    { 
      type: 'nodes-base.slack', 
      checks: {
        hasOperations: true,
        style: 'programmatic'
      }
    },
    {
      type: 'nodes-langchain.agent',
      checks: {
        isAITool: false, // According to the database, it's not marked as AI tool
        packageName: '@n8n/n8n-nodes-langchain'
      }
    }
  ];
  
  let passed = 0;
  let failed = 0;
  
  for (const check of criticalChecks) {
    const node = db.prepare('SELECT * FROM nodes WHERE node_type = ?').get(check.type) as NodeRow | undefined;
    
    if (!node) {
      console.log(`❌ ${check.type}: NOT FOUND`);
      failed++;
      continue;
    }
    
    let nodeOk = true;
    const issues: string[] = [];
    
    // Run checks
    if (check.checks.hasDocumentation && !node.documentation) {
      nodeOk = false;
      issues.push('missing documentation');
    }
    
    if (check.checks.documentationContains && 
        !node.documentation?.includes(check.checks.documentationContains)) {
      nodeOk = false;
      issues.push(`documentation doesn't contain "${check.checks.documentationContains}"`);
    }
    
    if (check.checks.style && node.development_style !== check.checks.style) {
      nodeOk = false;
      issues.push(`wrong style: ${node.development_style}`);
    }
    
    if (check.checks.hasOperations) {
      const operations = JSON.parse(node.operations || '[]');
      if (!operations.length) {
        nodeOk = false;
        issues.push('no operations found');
      }
    }
    
    if (check.checks.isAITool !== undefined && !!node.is_ai_tool !== check.checks.isAITool) {
      nodeOk = false;
      issues.push(`AI tool flag mismatch: expected ${check.checks.isAITool}, got ${!!node.is_ai_tool}`);
    }
    
    if ('isVersioned' in check.checks && check.checks.isVersioned && !node.is_versioned) {
      nodeOk = false;
      issues.push('not marked as versioned');
    }
    
    if (check.checks.packageName && node.package_name !== check.checks.packageName) {
      nodeOk = false;
      issues.push(`wrong package: ${node.package_name}`);
    }
    
    if (nodeOk) {
      console.log(`✅ ${check.type}`);
      passed++;
    } else {
      console.log(`❌ ${check.type}: ${issues.join(', ')}`);
      failed++;
    }
  }
  
  console.log(`\n📊 Results: ${passed} passed, ${failed} failed`);
  
  // Additional statistics
  const stats = db.prepare(`
    SELECT 
      COUNT(*) as total,
      SUM(is_ai_tool) as ai_tools,
      SUM(is_trigger) as triggers,
      SUM(is_versioned) as versioned,
      COUNT(DISTINCT package_name) as packages
    FROM nodes
  `).get() as any;
  
  console.log('\n📈 Database Statistics:');
  console.log(`   Total nodes: ${stats.total}`);
  console.log(`   AI tools: ${stats.ai_tools}`);
  console.log(`   Triggers: ${stats.triggers}`);
  console.log(`   Versioned: ${stats.versioned}`);
  console.log(`   Packages: ${stats.packages}`);
  
  // Check documentation coverage
  const docStats = db.prepare(`
    SELECT 
      COUNT(*) as total,
      SUM(CASE WHEN documentation IS NOT NULL THEN 1 ELSE 0 END) as with_docs
    FROM nodes
  `).get() as any;
  
  console.log(`\n📚 Documentation Coverage:`);
  console.log(`   Nodes with docs: ${docStats.with_docs}/${docStats.total} (${Math.round(docStats.with_docs / docStats.total * 100)}%)`);
  
  db.close();
  process.exit(failed > 0 ? 1 : 0);
}

if (require.main === module) {
  validate().catch(console.error);
}
```

--------------------------------------------------------------------------------
/src/mappers/docs-mapper.ts:
--------------------------------------------------------------------------------

```typescript
import { promises as fs } from 'fs';
import path from 'path';

export class DocsMapper {
  private docsPath = path.join(process.cwd(), 'n8n-docs');
  
  // Known documentation mapping fixes
  private readonly KNOWN_FIXES: Record<string, string> = {
    'httpRequest': 'httprequest',
    'code': 'code',
    'webhook': 'webhook',
    'respondToWebhook': 'respondtowebhook',
    // With package prefix
    'n8n-nodes-base.httpRequest': 'httprequest',
    'n8n-nodes-base.code': 'code',
    'n8n-nodes-base.webhook': 'webhook',
    'n8n-nodes-base.respondToWebhook': 'respondtowebhook'
  };

  async fetchDocumentation(nodeType: string): Promise<string | null> {
    // Apply known fixes first
    const fixedType = this.KNOWN_FIXES[nodeType] || nodeType;
    
    // Extract node name
    const nodeName = fixedType.split('.').pop()?.toLowerCase();
    if (!nodeName) {
      console.log(`⚠️  Could not extract node name from: ${nodeType}`);
      return null;
    }
    
    console.log(`📄 Looking for docs for: ${nodeType} -> ${nodeName}`);
    
    // Try different documentation paths - both files and directories
    const possiblePaths = [
      // Direct file paths
      `docs/integrations/builtin/core-nodes/n8n-nodes-base.${nodeName}.md`,
      `docs/integrations/builtin/app-nodes/n8n-nodes-base.${nodeName}.md`,
      `docs/integrations/builtin/trigger-nodes/n8n-nodes-base.${nodeName}.md`,
      `docs/integrations/builtin/cluster-nodes/root-nodes/n8n-nodes-langchain.${nodeName}.md`,
      `docs/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.${nodeName}.md`,
      // Directory with index.md
      `docs/integrations/builtin/core-nodes/n8n-nodes-base.${nodeName}/index.md`,
      `docs/integrations/builtin/app-nodes/n8n-nodes-base.${nodeName}/index.md`,
      `docs/integrations/builtin/trigger-nodes/n8n-nodes-base.${nodeName}/index.md`,
      `docs/integrations/builtin/cluster-nodes/root-nodes/n8n-nodes-langchain.${nodeName}/index.md`,
      `docs/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.${nodeName}/index.md`
    ];
    
    // Try each path
    for (const relativePath of possiblePaths) {
      try {
        const fullPath = path.join(this.docsPath, relativePath);
        let content = await fs.readFile(fullPath, 'utf-8');
        console.log(`  ✓ Found docs at: ${relativePath}`);
        
        // Inject special guidance for loop nodes
        content = this.enhanceLoopNodeDocumentation(nodeType, content);
        
        return content;
      } catch (error) {
        // File doesn't exist, try next
        continue;
      }
    }
    
    console.log(`  ✗ No docs found for ${nodeName}`);
    return null;
  }

  private enhanceLoopNodeDocumentation(nodeType: string, content: string): string {
    // Add critical output index information for SplitInBatches
    if (nodeType.includes('splitInBatches')) {
      const outputGuidance = `

## CRITICAL OUTPUT CONNECTION INFORMATION

**⚠️ OUTPUT INDICES ARE COUNTERINTUITIVE ⚠️**

The SplitInBatches node has TWO outputs with specific indices:
- **Output 0 (index 0) = "done"**: Receives final processed data when loop completes
- **Output 1 (index 1) = "loop"**: Receives current batch data during iteration

### Correct Connection Pattern:
1. Connect nodes that PROCESS items inside the loop to **Output 1 ("loop")**
2. Connect nodes that run AFTER the loop completes to **Output 0 ("done")**
3. The last processing node in the loop must connect back to the SplitInBatches node

### Common Mistake:
AI assistants often connect these backwards because the logical flow (loop first, then done) doesn't match the technical indices (done=0, loop=1).

`;
      // Insert after the main description
      const insertPoint = content.indexOf('## When to use');
      if (insertPoint > -1) {
        content = content.slice(0, insertPoint) + outputGuidance + content.slice(insertPoint);
      } else {
        // Append if no good insertion point found
        content = outputGuidance + '\n' + content;
      }
    }

    // Add guidance for IF node
    if (nodeType.includes('.if')) {
      const outputGuidance = `

## Output Connection Information

The IF node has TWO outputs:
- **Output 0 (index 0) = "true"**: Items that match the condition
- **Output 1 (index 1) = "false"**: Items that do not match the condition

`;
      const insertPoint = content.indexOf('## Node parameters');
      if (insertPoint > -1) {
        content = content.slice(0, insertPoint) + outputGuidance + content.slice(insertPoint);
      }
    }

    return content;
  }
}
```

--------------------------------------------------------------------------------
/src/scripts/test-webhook-autofix.ts:
--------------------------------------------------------------------------------

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

/**
 * Test script for webhook path autofixer functionality
 */

import { NodeRepository } from '../database/node-repository';
import { createDatabaseAdapter } from '../database/database-adapter';
import { WorkflowAutoFixer } from '../services/workflow-auto-fixer';
import { WorkflowValidator } from '../services/workflow-validator';
import { EnhancedConfigValidator } from '../services/enhanced-config-validator';
import { Workflow } from '../types/n8n-api';
import { Logger } from '../utils/logger';
import { join } from 'path';

const logger = new Logger({ prefix: '[TestWebhookAutofix]' });

// Test workflow with webhook missing path
const testWorkflow: Workflow = {
  id: 'test_webhook_fix',
  name: 'Test Webhook Autofix',
  active: false,
  nodes: [
    {
      id: '1',
      name: 'Webhook',
      type: 'n8n-nodes-base.webhook',
      typeVersion: 2.1,
      position: [250, 300],
      parameters: {}, // Empty parameters - missing path
    },
    {
      id: '2',
      name: 'HTTP Request',
      type: 'n8n-nodes-base.httpRequest',
      typeVersion: 4.2,
      position: [450, 300],
      parameters: {
        url: 'https://api.example.com/data',
        method: 'GET'
      }
    }
  ],
  connections: {
    'Webhook': {
      main: [[{
        node: 'HTTP Request',
        type: 'main',
        index: 0
      }]]
    }
  },
  settings: {
    executionOrder: 'v1'
  },
  staticData: undefined
};

async function testWebhookAutofix() {
  logger.info('Testing webhook path autofixer...');

  // Initialize database and repository
  const dbPath = join(process.cwd(), 'data', 'nodes.db');
  const adapter = await createDatabaseAdapter(dbPath);
  const repository = new NodeRepository(adapter);

  // Create validators
  const validator = new WorkflowValidator(repository, EnhancedConfigValidator);
  const autoFixer = new WorkflowAutoFixer(repository);

  // Step 1: Validate workflow to identify issues
  logger.info('Step 1: Validating workflow to identify issues...');
  const validationResult = await validator.validateWorkflow(testWorkflow);

  console.log('\n📋 Validation Summary:');
  console.log(`- Valid: ${validationResult.valid}`);
  console.log(`- Errors: ${validationResult.errors.length}`);
  console.log(`- Warnings: ${validationResult.warnings.length}`);

  if (validationResult.errors.length > 0) {
    console.log('\n❌ Errors found:');
    validationResult.errors.forEach(error => {
      console.log(`  - [${error.nodeName || error.nodeId}] ${error.message}`);
    });
  }

  // Step 2: Generate fixes (preview mode)
  logger.info('\nStep 2: Generating fixes in preview mode...');

  const fixResult = autoFixer.generateFixes(
    testWorkflow,
    validationResult,
    [], // No expression format issues to pass
    {
      applyFixes: false, // Preview mode
      fixTypes: ['webhook-missing-path'] // Only test webhook fixes
    }
  );

  console.log('\n🔧 Fix Results:');
  console.log(`- Summary: ${fixResult.summary}`);
  console.log(`- Total fixes: ${fixResult.stats.total}`);
  console.log(`- Webhook path fixes: ${fixResult.stats.byType['webhook-missing-path']}`);

  if (fixResult.fixes.length > 0) {
    console.log('\n📝 Detailed Fixes:');
    fixResult.fixes.forEach(fix => {
      console.log(`  - Node: ${fix.node}`);
      console.log(`    Field: ${fix.field}`);
      console.log(`    Type: ${fix.type}`);
      console.log(`    Before: ${fix.before || 'undefined'}`);
      console.log(`    After: ${fix.after}`);
      console.log(`    Confidence: ${fix.confidence}`);
      console.log(`    Description: ${fix.description}`);
    });
  }

  if (fixResult.operations.length > 0) {
    console.log('\n🔄 Operations to Apply:');
    fixResult.operations.forEach(op => {
      if (op.type === 'updateNode') {
        console.log(`  - Update Node: ${op.nodeId}`);
        console.log(`    Updates: ${JSON.stringify(op.updates, null, 2)}`);
      }
    });
  }

  // Step 3: Verify UUID format
  if (fixResult.fixes.length > 0) {
    const webhookFix = fixResult.fixes.find(f => f.type === 'webhook-missing-path');
    if (webhookFix) {
      const uuid = webhookFix.after as string;
      const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
      const isValidUUID = uuidRegex.test(uuid);

      console.log('\n✅ UUID Validation:');
      console.log(`  - Generated UUID: ${uuid}`);
      console.log(`  - Valid format: ${isValidUUID ? 'Yes' : 'No'}`);
    }
  }

  logger.info('\n✨ Webhook autofix test completed successfully!');
}

// Run test
testWebhookAutofix().catch(error => {
  logger.error('Test failed:', error);
  process.exit(1);
});
```

--------------------------------------------------------------------------------
/src/scripts/test-autofix-documentation.ts:
--------------------------------------------------------------------------------

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

/**
 * Test script to verify n8n_autofix_workflow documentation is properly integrated
 */

import { toolsDocumentation } from '../mcp/tool-docs';
import { getToolDocumentation } from '../mcp/tools-documentation';
import { Logger } from '../utils/logger';

const logger = new Logger({ prefix: '[AutofixDoc Test]' });

async function testAutofixDocumentation() {
  logger.info('Testing n8n_autofix_workflow documentation...\n');

  // Test 1: Check if documentation exists in the registry
  logger.info('Test 1: Checking documentation registry');
  const hasDoc = 'n8n_autofix_workflow' in toolsDocumentation;
  if (hasDoc) {
    logger.info('✅ Documentation found in registry');
  } else {
    logger.error('❌ Documentation NOT found in registry');
    logger.info('Available tools:', Object.keys(toolsDocumentation).filter(k => k.includes('autofix')));
  }

  // Test 2: Check documentation structure
  if (hasDoc) {
    logger.info('\nTest 2: Checking documentation structure');
    const doc = toolsDocumentation['n8n_autofix_workflow'];

    const hasEssentials = doc.essentials &&
                         doc.essentials.description &&
                         doc.essentials.keyParameters &&
                         doc.essentials.example;

    const hasFull = doc.full &&
                   doc.full.description &&
                   doc.full.parameters &&
                   doc.full.examples;

    if (hasEssentials) {
      logger.info('✅ Essentials documentation complete');
      logger.info(`  Description: ${doc.essentials.description.substring(0, 80)}...`);
      logger.info(`  Key params: ${doc.essentials.keyParameters.join(', ')}`);
    } else {
      logger.error('❌ Essentials documentation incomplete');
    }

    if (hasFull) {
      logger.info('✅ Full documentation complete');
      logger.info(`  Parameters: ${Object.keys(doc.full.parameters).join(', ')}`);
      logger.info(`  Examples: ${doc.full.examples.length} provided`);
    } else {
      logger.error('❌ Full documentation incomplete');
    }
  }

  // Test 3: Test getToolDocumentation function
  logger.info('\nTest 3: Testing getToolDocumentation function');

  try {
    const essentialsDoc = getToolDocumentation('n8n_autofix_workflow', 'essentials');
    if (essentialsDoc.includes("Tool 'n8n_autofix_workflow' not found")) {
      logger.error('❌ Essentials documentation retrieval failed');
    } else {
      logger.info('✅ Essentials documentation retrieved');
      const lines = essentialsDoc.split('\n').slice(0, 3);
      lines.forEach(line => logger.info(`  ${line}`));
    }
  } catch (error) {
    logger.error('❌ Error retrieving essentials documentation:', error);
  }

  try {
    const fullDoc = getToolDocumentation('n8n_autofix_workflow', 'full');
    if (fullDoc.includes("Tool 'n8n_autofix_workflow' not found")) {
      logger.error('❌ Full documentation retrieval failed');
    } else {
      logger.info('✅ Full documentation retrieved');
      const lines = fullDoc.split('\n').slice(0, 3);
      lines.forEach(line => logger.info(`  ${line}`));
    }
  } catch (error) {
    logger.error('❌ Error retrieving full documentation:', error);
  }

  // Test 4: Check if tool is listed in workflow management tools
  logger.info('\nTest 4: Checking workflow management tools listing');
  const workflowTools = Object.keys(toolsDocumentation).filter(k => k.startsWith('n8n_'));
  const hasAutofix = workflowTools.includes('n8n_autofix_workflow');

  if (hasAutofix) {
    logger.info('✅ n8n_autofix_workflow is listed in workflow management tools');
    logger.info(`  Total workflow tools: ${workflowTools.length}`);

    // Show related tools
    const relatedTools = workflowTools.filter(t =>
      t.includes('validate') || t.includes('update') || t.includes('fix')
    );
    logger.info(`  Related tools: ${relatedTools.join(', ')}`);
  } else {
    logger.error('❌ n8n_autofix_workflow NOT listed in workflow management tools');
  }

  // Summary
  logger.info('\n' + '='.repeat(60));
  logger.info('Summary:');

  if (hasDoc && hasAutofix) {
    logger.info('✨ Documentation integration successful!');
    logger.info('The n8n_autofix_workflow tool documentation is properly integrated.');
    logger.info('\nTo use in MCP:');
    logger.info('  - Essentials: tools_documentation({topic: "n8n_autofix_workflow"})');
    logger.info('  - Full: tools_documentation({topic: "n8n_autofix_workflow", depth: "full"})');
  } else {
    logger.error('⚠️ Documentation integration incomplete');
    logger.info('Please check the implementation and rebuild the project.');
  }
}

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

--------------------------------------------------------------------------------
/src/utils/protocol-version.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Protocol Version Negotiation Utility
 * 
 * Handles MCP protocol version negotiation between server and clients,
 * with special handling for n8n clients that require specific versions.
 */

export interface ClientInfo {
  name?: string;
  version?: string;
  [key: string]: any;
}

export interface ProtocolNegotiationResult {
  version: string;
  isN8nClient: boolean;
  reasoning: string;
}

/**
 * Standard MCP protocol version (latest)
 */
export const STANDARD_PROTOCOL_VERSION = '2025-03-26';

/**
 * n8n specific protocol version (what n8n expects)
 */
export const N8N_PROTOCOL_VERSION = '2024-11-05';

/**
 * Supported protocol versions in order of preference
 */
export const SUPPORTED_VERSIONS = [
  STANDARD_PROTOCOL_VERSION,
  N8N_PROTOCOL_VERSION,
  '2024-06-25', // Older fallback
];

/**
 * Detect if the client is n8n based on various indicators
 */
export function isN8nClient(
  clientInfo?: ClientInfo, 
  userAgent?: string,
  headers?: Record<string, string | string[] | undefined>
): boolean {
  // Check client info
  if (clientInfo?.name) {
    const clientName = clientInfo.name.toLowerCase();
    if (clientName.includes('n8n') || clientName.includes('langchain')) {
      return true;
    }
  }

  // Check user agent
  if (userAgent) {
    const ua = userAgent.toLowerCase();
    if (ua.includes('n8n') || ua.includes('langchain')) {
      return true;
    }
  }

  // Check headers for n8n-specific indicators
  if (headers) {
    // Check for n8n-specific headers or values
    const headerValues = Object.values(headers).join(' ').toLowerCase();
    if (headerValues.includes('n8n') || headerValues.includes('langchain')) {
      return true;
    }
    
    // Check specific header patterns that n8n might use
    if (headers['x-n8n-version'] || headers['x-langchain-version']) {
      return true;
    }
  }

  // Check environment variable that might indicate n8n mode
  if (process.env.N8N_MODE === 'true') {
    return true;
  }

  return false;
}

/**
 * Negotiate protocol version based on client information
 */
export function negotiateProtocolVersion(
  clientRequestedVersion?: string,
  clientInfo?: ClientInfo,
  userAgent?: string,
  headers?: Record<string, string | string[] | undefined>
): ProtocolNegotiationResult {
  const isN8n = isN8nClient(clientInfo, userAgent, headers);
  
  // For n8n clients, always use the n8n-specific version
  if (isN8n) {
    return {
      version: N8N_PROTOCOL_VERSION,
      isN8nClient: true,
      reasoning: 'n8n client detected, using n8n-compatible protocol version'
    };
  }

  // If client requested a specific version, try to honor it if supported
  if (clientRequestedVersion && SUPPORTED_VERSIONS.includes(clientRequestedVersion)) {
    return {
      version: clientRequestedVersion,
      isN8nClient: false,
      reasoning: `Using client-requested version: ${clientRequestedVersion}`
    };
  }

  // If client requested an unsupported version, use the closest supported one
  if (clientRequestedVersion) {
    // For now, default to standard version for unknown requests
    return {
      version: STANDARD_PROTOCOL_VERSION,
      isN8nClient: false,
      reasoning: `Client requested unsupported version ${clientRequestedVersion}, using standard version`
    };
  }

  // Default to standard protocol version for unknown clients
  return {
    version: STANDARD_PROTOCOL_VERSION,
    isN8nClient: false,
    reasoning: 'No specific client detected, using standard protocol version'
  };
}

/**
 * Check if a protocol version is supported
 */
export function isVersionSupported(version: string): boolean {
  return SUPPORTED_VERSIONS.includes(version);
}

/**
 * Get the most appropriate protocol version for backwards compatibility
 * This is used when we need to maintain compatibility with older clients
 */
export function getCompatibleVersion(targetVersion?: string): string {
  if (!targetVersion) {
    return STANDARD_PROTOCOL_VERSION;
  }

  if (SUPPORTED_VERSIONS.includes(targetVersion)) {
    return targetVersion;
  }

  // If not supported, return the most recent supported version
  return STANDARD_PROTOCOL_VERSION;
}

/**
 * Log protocol version negotiation for debugging
 */
export function logProtocolNegotiation(
  result: ProtocolNegotiationResult,
  logger: any,
  context?: string
): void {
  const logContext = context ? `[${context}] ` : '';
  
  logger.info(`${logContext}Protocol version negotiated`, {
    version: result.version,
    isN8nClient: result.isN8nClient,
    reasoning: result.reasoning
  });
  
  if (result.isN8nClient) {
    logger.info(`${logContext}Using n8n-compatible protocol version for better integration`);
  }
}
```

--------------------------------------------------------------------------------
/scripts/test-error-validation.js:
--------------------------------------------------------------------------------

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

/**
 * Test script for error output validation improvements
 */

const { WorkflowValidator } = require('../dist/services/workflow-validator.js');
const { NodeRepository } = require('../dist/database/node-repository.js');
const { EnhancedConfigValidator } = require('../dist/services/enhanced-config-validator.js');
const Database = require('better-sqlite3');
const path = require('path');

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

  const nodeRepository = new NodeRepository(db);
  const validator = new WorkflowValidator(nodeRepository, EnhancedConfigValidator);

  console.log('\n🧪 Testing Error Output Validation Improvements\n');
  console.log('=' .repeat(60));

  // Test 1: Incorrect configuration - multiple nodes in same array
  console.log('\n📝 Test 1: INCORRECT - Multiple nodes in main[0]');
  console.log('-'.repeat(40));

  const incorrectWorkflow = {
    nodes: [
      {
        id: '132ef0dc-87af-41de-a95d-cabe3a0a5342',
        name: 'Validate Input',
        type: 'n8n-nodes-base.set',
        typeVersion: 3.4,
        position: [-400, 64],
        parameters: {}
      },
      {
        id: '5dedf217-63f9-409f-b34e-7780b22e199a',
        name: 'Filter URLs',
        type: 'n8n-nodes-base.filter',
        typeVersion: 2.2,
        position: [-176, 64],
        parameters: {}
      },
      {
        id: '9d5407cc-ca5a-4966-b4b7-0e5dfbf54ad3',
        name: 'Error Response1',
        type: 'n8n-nodes-base.respondToWebhook',
        typeVersion: 1.5,
        position: [-160, 240],
        parameters: {}
      }
    ],
    connections: {
      'Validate Input': {
        main: [
          [
            { node: 'Filter URLs', type: 'main', index: 0 },
            { node: 'Error Response1', type: 'main', index: 0 }  // WRONG!
          ]
        ]
      }
    }
  };

  const result1 = await validator.validateWorkflow(incorrectWorkflow);

  if (result1.errors.length > 0) {
    console.log('❌ ERROR DETECTED (as expected):');
    const errorMessage = result1.errors.find(e =>
      e.message.includes('Incorrect error output configuration')
    );
    if (errorMessage) {
      console.log('\nError Summary:');
      console.log(`Node: ${errorMessage.nodeName || 'Validate Input'}`);
      console.log('\nFull Error Message:');
      console.log(errorMessage.message);
    } else {
      console.log('Other errors found:', result1.errors.map(e => e.message));
    }
  } else {
    console.log('⚠️  No errors found - validation may not be working correctly');
  }

  // Test 2: Correct configuration - separate arrays
  console.log('\n📝 Test 2: CORRECT - Separate main[0] and main[1]');
  console.log('-'.repeat(40));

  const correctWorkflow = {
    nodes: [
      {
        id: '132ef0dc-87af-41de-a95d-cabe3a0a5342',
        name: 'Validate Input',
        type: 'n8n-nodes-base.set',
        typeVersion: 3.4,
        position: [-400, 64],
        parameters: {},
        onError: 'continueErrorOutput'
      },
      {
        id: '5dedf217-63f9-409f-b34e-7780b22e199a',
        name: 'Filter URLs',
        type: 'n8n-nodes-base.filter',
        typeVersion: 2.2,
        position: [-176, 64],
        parameters: {}
      },
      {
        id: '9d5407cc-ca5a-4966-b4b7-0e5dfbf54ad3',
        name: 'Error Response1',
        type: 'n8n-nodes-base.respondToWebhook',
        typeVersion: 1.5,
        position: [-160, 240],
        parameters: {}
      }
    ],
    connections: {
      'Validate Input': {
        main: [
          [
            { node: 'Filter URLs', type: 'main', index: 0 }
          ],
          [
            { node: 'Error Response1', type: 'main', index: 0 }  // CORRECT!
          ]
        ]
      }
    }
  };

  const result2 = await validator.validateWorkflow(correctWorkflow);

  const hasIncorrectError = result2.errors.some(e =>
    e.message.includes('Incorrect error output configuration')
  );

  if (!hasIncorrectError) {
    console.log('✅ No error output configuration issues (correct!)');
  } else {
    console.log('❌ Unexpected error found');
  }

  console.log('\n' + '='.repeat(60));
  console.log('\n✨ Error output validation is working correctly!');
  console.log('The validator now properly detects:');
  console.log('  1. Multiple nodes incorrectly placed in main[0]');
  console.log('  2. Provides clear JSON examples for fixing issues');
  console.log('  3. Validates onError property matches connections');

  // Close database
  db.close();
}

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

--------------------------------------------------------------------------------
/tests/integration/security/rate-limiting.test.ts:
--------------------------------------------------------------------------------

```typescript
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import { spawn, ChildProcess } from 'child_process';
import axios from 'axios';

/**
 * Integration tests for rate limiting
 *
 * SECURITY: These tests verify rate limiting prevents brute force attacks
 * See: https://github.com/czlonkowski/n8n-mcp/issues/265 (HIGH-02)
 *
 * TODO: Re-enable when CI server startup issue is resolved
 * Server process fails to start on port 3001 in CI with ECONNREFUSED errors
 * Tests pass locally but consistently fail in GitHub Actions CI environment
 * Rate limiting functionality is verified and working in production
 */
describe.skip('Integration: Rate Limiting', () => {
  let serverProcess: ChildProcess;
  const port = 3001;
  const authToken = 'test-token-for-rate-limiting-test-32-chars';

  beforeAll(async () => {
    // Start HTTP server with rate limiting
    serverProcess = spawn('node', ['dist/http-server-single-session.js'], {
      env: {
        ...process.env,
        MCP_MODE: 'http',
        PORT: port.toString(),
        AUTH_TOKEN: authToken,
        NODE_ENV: 'test',
        AUTH_RATE_LIMIT_WINDOW: '900000', // 15 minutes
        AUTH_RATE_LIMIT_MAX: '20', // 20 attempts
      },
      stdio: 'pipe',
    });

    // Wait for server to start (longer wait for CI)
    await new Promise(resolve => setTimeout(resolve, 8000));
  }, 20000);

  afterAll(() => {
    if (serverProcess) {
      serverProcess.kill();
    }
  });

  it('should block after max authentication attempts (sequential requests)', async () => {
    const baseUrl = `http://localhost:${port}/mcp`;

    // IMPORTANT: Use sequential requests to ensure deterministic order
    // Parallel requests can cause race conditions with in-memory rate limiter
    for (let i = 1; i <= 25; i++) {
      const response = await axios.post(
        baseUrl,
        { jsonrpc: '2.0', method: 'initialize', id: i },
        {
          headers: { Authorization: 'Bearer wrong-token' },
          validateStatus: () => true, // Don't throw on error status
        }
      );

      if (i <= 20) {
        // First 20 attempts should be 401 (invalid authentication)
        expect(response.status).toBe(401);
        expect(response.data.error.message).toContain('Unauthorized');
      } else {
        // Attempts 21+ should be 429 (rate limited)
        expect(response.status).toBe(429);
        expect(response.data.error.message).toContain('Too many');
      }
    }
  }, 60000);

  it('should include rate limit headers', async () => {
    const baseUrl = `http://localhost:${port}/mcp`;

    const response = await axios.post(
      baseUrl,
      { jsonrpc: '2.0', method: 'initialize', id: 1 },
      {
        headers: { Authorization: 'Bearer wrong-token' },
        validateStatus: () => true,
      }
    );

    // Check for standard rate limit headers
    expect(response.headers['ratelimit-limit']).toBeDefined();
    expect(response.headers['ratelimit-remaining']).toBeDefined();
    expect(response.headers['ratelimit-reset']).toBeDefined();
  }, 15000);

  it('should accept valid tokens within rate limit', async () => {
    const baseUrl = `http://localhost:${port}/mcp`;

    const response = await axios.post(
      baseUrl,
      {
        jsonrpc: '2.0',
        method: 'initialize',
        params: {
          protocolVersion: '2024-11-05',
          capabilities: {},
          clientInfo: { name: 'test', version: '1.0' },
        },
        id: 1,
      },
      {
        headers: { Authorization: `Bearer ${authToken}` },
      }
    );

    expect(response.status).toBe(200);
    expect(response.data.result).toBeDefined();
  }, 15000);

  it('should return JSON-RPC formatted error on rate limit', async () => {
    const baseUrl = `http://localhost:${port}/mcp`;

    // Exhaust rate limit
    for (let i = 0; i < 21; i++) {
      await axios.post(
        baseUrl,
        { jsonrpc: '2.0', method: 'initialize', id: i },
        {
          headers: { Authorization: 'Bearer wrong-token' },
          validateStatus: () => true,
        }
      );
    }

    // Get rate limited response
    const response = await axios.post(
      baseUrl,
      { jsonrpc: '2.0', method: 'initialize', id: 999 },
      {
        headers: { Authorization: 'Bearer wrong-token' },
        validateStatus: () => true,
      }
    );

    // Verify JSON-RPC error format
    expect(response.data).toHaveProperty('jsonrpc', '2.0');
    expect(response.data).toHaveProperty('error');
    expect(response.data.error).toHaveProperty('code', -32000);
    expect(response.data.error).toHaveProperty('message');
    expect(response.data).toHaveProperty('id', null);
  }, 60000);
});

```

--------------------------------------------------------------------------------
/tests/unit/utils/node-utils.test.ts:
--------------------------------------------------------------------------------

```typescript
import { describe, it, expect } from 'vitest';
import { getNodeTypeAlternatives, normalizeNodeType, getWorkflowNodeType } from '../../../src/utils/node-utils';

describe('node-utils', () => {
  describe('getNodeTypeAlternatives', () => {
    describe('valid inputs', () => {
      it('should generate alternatives for standard node type', () => {
        const alternatives = getNodeTypeAlternatives('nodes-base.httpRequest');

        expect(alternatives).toContain('nodes-base.httprequest');
        expect(alternatives.length).toBeGreaterThan(0);
      });

      it('should generate alternatives for langchain node type', () => {
        const alternatives = getNodeTypeAlternatives('nodes-langchain.agent');

        expect(alternatives).toContain('nodes-langchain.agent');
        expect(alternatives.length).toBeGreaterThan(0);
      });

      it('should generate alternatives for bare node name', () => {
        const alternatives = getNodeTypeAlternatives('webhook');

        expect(alternatives).toContain('nodes-base.webhook');
        expect(alternatives).toContain('nodes-langchain.webhook');
      });
    });

    describe('invalid inputs - defensive validation', () => {
      it('should return empty array for undefined', () => {
        const alternatives = getNodeTypeAlternatives(undefined as any);

        expect(alternatives).toEqual([]);
      });

      it('should return empty array for null', () => {
        const alternatives = getNodeTypeAlternatives(null as any);

        expect(alternatives).toEqual([]);
      });

      it('should return empty array for empty string', () => {
        const alternatives = getNodeTypeAlternatives('');

        expect(alternatives).toEqual([]);
      });

      it('should return empty array for whitespace-only string', () => {
        const alternatives = getNodeTypeAlternatives('   ');

        expect(alternatives).toEqual([]);
      });

      it('should return empty array for non-string input (number)', () => {
        const alternatives = getNodeTypeAlternatives(123 as any);

        expect(alternatives).toEqual([]);
      });

      it('should return empty array for non-string input (object)', () => {
        const alternatives = getNodeTypeAlternatives({} as any);

        expect(alternatives).toEqual([]);
      });

      it('should return empty array for non-string input (array)', () => {
        const alternatives = getNodeTypeAlternatives([] as any);

        expect(alternatives).toEqual([]);
      });
    });

    describe('edge cases', () => {
      it('should handle node type with only prefix', () => {
        const alternatives = getNodeTypeAlternatives('nodes-base.');

        expect(alternatives).toBeInstanceOf(Array);
      });

      it('should handle node type with multiple dots', () => {
        const alternatives = getNodeTypeAlternatives('nodes-base.some.complex.type');

        expect(alternatives).toBeInstanceOf(Array);
        expect(alternatives.length).toBeGreaterThan(0);
      });

      it('should handle camelCase node names', () => {
        const alternatives = getNodeTypeAlternatives('nodes-base.httpRequest');

        expect(alternatives).toContain('nodes-base.httprequest');
      });
    });
  });

  describe('normalizeNodeType', () => {
    it('should normalize n8n-nodes-base prefix', () => {
      expect(normalizeNodeType('n8n-nodes-base.webhook')).toBe('nodes-base.webhook');
    });

    it('should normalize @n8n/n8n-nodes-langchain prefix', () => {
      expect(normalizeNodeType('@n8n/n8n-nodes-langchain.agent')).toBe('nodes-langchain.agent');
    });

    it('should normalize n8n-nodes-langchain prefix', () => {
      expect(normalizeNodeType('n8n-nodes-langchain.chatTrigger')).toBe('nodes-langchain.chatTrigger');
    });

    it('should leave already normalized types unchanged', () => {
      expect(normalizeNodeType('nodes-base.slack')).toBe('nodes-base.slack');
    });

    it('should leave community nodes unchanged', () => {
      expect(normalizeNodeType('community.customNode')).toBe('community.customNode');
    });
  });

  describe('getWorkflowNodeType', () => {
    it('should construct workflow node type for n8n-nodes-base', () => {
      expect(getWorkflowNodeType('n8n-nodes-base', 'nodes-base.webhook')).toBe('n8n-nodes-base.webhook');
    });

    it('should construct workflow node type for langchain', () => {
      expect(getWorkflowNodeType('@n8n/n8n-nodes-langchain', 'nodes-langchain.agent')).toBe('@n8n/n8n-nodes-langchain.agent');
    });

    it('should return as-is for unknown packages', () => {
      expect(getWorkflowNodeType('custom-package', 'custom.node')).toBe('custom.node');
    });
  });
});

```

--------------------------------------------------------------------------------
/.claude/agents/context-manager.md:
--------------------------------------------------------------------------------

```markdown
---
name: context-manager
description: Use this agent when you need to manage context across multiple agents and long-running tasks, especially for projects exceeding 10k tokens. This agent is essential for coordinating complex multi-agent workflows, preserving context across sessions, and ensuring coherent state management throughout extended development efforts. Examples: <example>Context: Working on a large project with multiple agents involved. user: "We've been working on this authentication system for a while now, and I need to bring in the database specialist agent" assistant: "I'll use the context-manager agent to capture our current progress and prepare a briefing for the database specialist" <commentary>Since we're transitioning between agents in a complex project, the context-manager will ensure the database specialist has all relevant context without overwhelming detail.</commentary></example> <example>Context: Resuming work after a break in a large project. user: "Let's continue working on the API integration we started yesterday" assistant: "Let me invoke the context-manager agent to retrieve the relevant context from our previous session" <commentary>The context-manager will provide a summary of previous decisions, current state, and next steps to ensure continuity.</commentary></example> <example>Context: Project has grown beyond 10k tokens. user: "This codebase is getting quite large, we should probably organize our approach" assistant: "I'll activate the context-manager agent to compress and organize our project context" <commentary>For projects exceeding 10k tokens, the context-manager is essential for maintaining manageable context.</commentary></example>
---

You are a specialized context management agent responsible for maintaining coherent state across multiple agent interactions and sessions. Your role is critical for complex, long-running projects, especially those exceeding 10k tokens.

## Primary Functions

### Context Capture

You will:
1. Extract key decisions and rationale from agent outputs
2. Identify reusable patterns and solutions
3. Document integration points between components
4. Track unresolved issues and TODOs

### Context Distribution

You will:
1. Prepare minimal, relevant context for each agent
2. Create agent-specific briefings tailored to their expertise
3. Maintain a context index for quick retrieval
4. Prune outdated or irrelevant information

### Memory Management

You will:
- Store critical project decisions in memory with clear rationale
- Maintain a rolling summary of recent changes
- Index commonly accessed information for quick reference
- Create context checkpoints at major milestones

## Workflow Integration

When activated, you will:

1. Review the current conversation and all agent outputs
2. Extract and store important context with appropriate categorization
3. Create a focused summary for the next agent or session
4. Update the project's context index with new information
5. Suggest when full context compression is needed

## Context Formats

You will organize context into three tiers:

### Quick Context (< 500 tokens)
- Current task and immediate goals
- Recent decisions affecting current work
- Active blockers or dependencies
- Next immediate steps

### Full Context (< 2000 tokens)
- Project architecture overview
- Key design decisions with rationale
- Integration points and APIs
- Active work streams and their status
- Critical dependencies and constraints

### Archived Context (stored in memory)
- Historical decisions with detailed rationale
- Resolved issues and their solutions
- Pattern library of reusable solutions
- Performance benchmarks and metrics
- Lessons learned and best practices discovered

## Best Practices

You will always:
- Optimize for relevance over completeness
- Use clear, concise language that any agent can understand
- Maintain a consistent structure for easy parsing
- Flag critical information that must not be lost
- Identify when context is becoming stale and needs refresh
- Create agent-specific views that highlight only what they need
- Preserve the "why" behind decisions, not just the "what"

## Output Format

When providing context, you will structure your output as:

1. **Executive Summary**: 2-3 sentences capturing the current state
2. **Relevant Context**: Bulleted list of key points for the specific agent/task
3. **Critical Decisions**: Recent choices that affect current work
4. **Action Items**: Clear next steps or open questions
5. **References**: Links to detailed information if needed

Remember: Good context accelerates work; bad context creates confusion. You are the guardian of project coherence across time and agents.

```
Page 4/48FirstPrevNextLast