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

# Directory Structure

```
├── _config.yml
├── .claude
│   └── agents
│       ├── code-reviewer.md
│       ├── context-manager.md
│       ├── debugger.md
│       ├── deployment-engineer.md
│       ├── mcp-backend-engineer.md
│       ├── n8n-mcp-tester.md
│       ├── technical-researcher.md
│       └── test-automator.md
├── .dockerignore
├── .env.docker
├── .env.example
├── .env.n8n.example
├── .env.test
├── .env.test.example
├── .github
│   ├── ABOUT.md
│   ├── BENCHMARK_THRESHOLDS.md
│   ├── FUNDING.yml
│   ├── gh-pages.yml
│   ├── secret_scanning.yml
│   └── workflows
│       ├── benchmark-pr.yml
│       ├── benchmark.yml
│       ├── docker-build-fast.yml
│       ├── docker-build-n8n.yml
│       ├── docker-build.yml
│       ├── release.yml
│       ├── test.yml
│       └── update-n8n-deps.yml
├── .gitignore
├── .npmignore
├── ANALYSIS_QUICK_REFERENCE.md
├── 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
│   ├── CHANGELOG.md
│   ├── CI_TEST_INFRASTRUCTURE.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
│   │   ├── skills.png
│   │   ├── vsc_ghcp_chat_agent_mode.png
│   │   ├── vsc_ghcp_chat_instruction_files.png
│   │   ├── vsc_ghcp_chat_thinking_tool.png
│   │   └── windsurf_tut.png
│   ├── INSTALLATION.md
│   ├── LIBRARY_USAGE.md
│   ├── local
│   │   ├── DEEP_DIVE_ANALYSIS_2025-10-02.md
│   │   ├── DEEP_DIVE_ANALYSIS_README.md
│   │   ├── Deep_dive_p1_p2.md
│   │   ├── integration-testing-plan.md
│   │   ├── integration-tests-phase1-summary.md
│   │   ├── N8N_AI_WORKFLOW_BUILDER_ANALYSIS.md
│   │   ├── P0_IMPLEMENTATION_PLAN.md
│   │   └── TEMPLATE_MINING_ANALYSIS.md
│   ├── MCP_ESSENTIALS_README.md
│   ├── MCP_QUICK_START_GUIDE.md
│   ├── N8N_DEPLOYMENT.md
│   ├── RAILWAY_DEPLOYMENT.md
│   ├── README_CLAUDE_SETUP.md
│   ├── README.md
│   ├── SESSION_PERSISTENCE.md
│   ├── tools-documentation-usage.md
│   ├── TYPE_STRUCTURE_VALIDATION.md
│   ├── VS_CODE_PROJECT_SETUP.md
│   ├── WINDSURF_SETUP.md
│   └── workflow-diff-examples.md
├── examples
│   └── enhanced-documentation-demo.js
├── fetch_log.txt
├── LICENSE
├── MEMORY_N8N_UPDATE.md
├── MEMORY_TEMPLATE_UPDATE.md
├── monitor_fetch.sh
├── 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_ANALYSIS.md
├── README.md
├── renovate.json
├── scripts
│   ├── analyze-optimization.sh
│   ├── audit-schema-coverage.ts
│   ├── backfill-mutation-hashes.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-initial-release-notes.js
│   ├── generate-release-notes.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
│   ├── process-batch-metadata.ts
│   ├── 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-structure-validation.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
│   ├── test-workflow-versioning.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
│   ├── constants
│   │   └── type-structures.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.ts
│   │   │   │   └── index.ts
│   │   │   ├── discovery
│   │   │   │   ├── index.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
│   │   │   │   ├── index.ts
│   │   │   │   └── search-templates.ts
│   │   │   ├── types.ts
│   │   │   ├── validation
│   │   │   │   ├── index.ts
│   │   │   │   ├── validate-node.ts
│   │   │   │   └── validate-workflow.ts
│   │   │   └── workflow_management
│   │   │       ├── index.ts
│   │   │       ├── n8n-autofix-workflow.ts
│   │   │       ├── n8n-create-workflow.ts
│   │   │       ├── n8n-delete-workflow.ts
│   │   │       ├── n8n-executions.ts
│   │   │       ├── n8n-get-workflow.ts
│   │   │       ├── n8n-list-workflows.ts
│   │   │       ├── n8n-trigger-webhook-workflow.ts
│   │   │       ├── n8n-update-full-workflow.ts
│   │   │       ├── n8n-update-partial-workflow.ts
│   │   │       ├── n8n-validate-workflow.ts
│   │   │       └── n8n-workflow-versions.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-telemetry-mutations-verbose.ts
│   │   ├── test-telemetry-mutations.ts
│   │   ├── test-webhook-autofix.ts
│   │   ├── validate.ts
│   │   └── validation-summary.ts
│   ├── services
│   │   ├── ai-node-validator.ts
│   │   ├── ai-tool-validators.ts
│   │   ├── breaking-change-detector.ts
│   │   ├── breaking-changes-registry.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-migration-service.ts
│   │   ├── node-sanitizer.ts
│   │   ├── node-similarity-service.ts
│   │   ├── node-specific-validators.ts
│   │   ├── node-version-service.ts
│   │   ├── operation-similarity-service.ts
│   │   ├── post-update-validator.ts
│   │   ├── property-dependencies.ts
│   │   ├── property-filter.ts
│   │   ├── resource-similarity-service.ts
│   │   ├── sqlite-storage-service.ts
│   │   ├── task-templates.ts
│   │   ├── type-structure-service.ts
│   │   ├── universal-expression-validator.ts
│   │   ├── workflow-auto-fixer.ts
│   │   ├── workflow-diff-engine.ts
│   │   ├── workflow-validator.ts
│   │   └── workflow-versioning-service.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
│   │   ├── intent-classifier.ts
│   │   ├── intent-sanitizer.ts
│   │   ├── mutation-tracker.ts
│   │   ├── mutation-types.ts
│   │   ├── mutation-validator.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-state.ts
│   │   ├── type-structures.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
│       ├── expression-utils.ts
│       ├── fixed-collection-validator.ts
│       ├── logger.ts
│       ├── mcp-client.ts
│       ├── n8n-errors.ts
│       ├── node-classification.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
├── 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
│   │   │   ├── sqljs-memory-leak.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
│   │   │   ├── 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
│   │   ├── setup
│   │   │   ├── integration-setup.ts
│   │   │   └── msw-test-server.ts
│   │   ├── telemetry
│   │   │   ├── docker-user-id-stability.test.ts
│   │   │   └── mcp-telemetry.test.ts
│   │   ├── templates
│   │   │   └── metadata-operations.test.ts
│   │   ├── validation
│   │   │   └── real-world-structure-validation.test.ts
│   │   ├── workflow-creation-node-type-format.test.ts
│   │   └── workflow-diff
│   │       ├── ai-node-connection-validation.test.ts
│   │       └── node-rename-integration.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
│   │   ├── constants
│   │   │   └── type-structures.test.ts
│   │   ├── 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
│   │   │   └── session-persistence.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
│   │   │   ├── disabled-tools-additional.test.ts
│   │   │   ├── disabled-tools.test.ts
│   │   │   ├── get-node-essentials-examples.test.ts
│   │   │   ├── get-node-unified.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
│   │   ├── mcp-engine
│   │   │   └── session-persistence.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
│   │   │   ├── breaking-change-detector.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-type-structures.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-sticky-notes.test.ts
│   │   │   ├── n8n-validation.test.ts
│   │   │   ├── node-migration-service.test.ts
│   │   │   ├── node-sanitizer.test.ts
│   │   │   ├── node-similarity-service.test.ts
│   │   │   ├── node-specific-validators.test.ts
│   │   │   ├── node-version-service.test.ts
│   │   │   ├── operation-similarity-service-comprehensive.test.ts
│   │   │   ├── operation-similarity-service.test.ts
│   │   │   ├── post-update-validator.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
│   │   │   ├── type-structure-service.test.ts
│   │   │   ├── universal-expression-validator.test.ts
│   │   │   ├── validation-fixes.test.ts
│   │   │   ├── workflow-auto-fixer.test.ts
│   │   │   ├── workflow-diff-engine.test.ts
│   │   │   ├── workflow-diff-node-rename.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
│   │   │   └── workflow-versioning-service.test.ts
│   │   ├── telemetry
│   │   │   ├── batch-processor.test.ts
│   │   │   ├── config-manager.test.ts
│   │   │   ├── event-tracker.test.ts
│   │   │   ├── event-validator.test.ts
│   │   │   ├── mutation-tracker.test.ts
│   │   │   ├── mutation-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
│   │   │   └── type-structures.test.ts
│   │   ├── utils
│   │   │   ├── auth-timing-safe.test.ts
│   │   │   ├── cache-utils.test.ts
│   │   │   ├── console-manager.test.ts
│   │   │   ├── database-utils.test.ts
│   │   │   ├── expression-utils.test.ts
│   │   │   ├── fixed-collection-validator.test.ts
│   │   │   ├── n8n-errors.test.ts
│   │   │   ├── node-classification.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
├── versioned-nodes.md
├── vitest.config.benchmark.ts
├── vitest.config.integration.ts
└── vitest.config.ts
```

# Files

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

```typescript
  1 | #!/usr/bin/env tsx
  2 | import * as fs from 'fs';
  3 | import * as path from 'path';
  4 | 
  5 | // This is a helper script to migrate tool documentation to the new structure
  6 | // It creates a template file for each tool that needs to be migrated
  7 | 
  8 | const toolsByCategory = {
  9 |   discovery: [
 10 |     'search_nodes',
 11 |     'list_nodes', 
 12 |     'list_ai_tools',
 13 |     'get_database_statistics'
 14 |   ],
 15 |   configuration: [
 16 |     'get_node_info',
 17 |     'get_node_essentials',
 18 |     'get_node_documentation',
 19 |     'search_node_properties',
 20 |     'get_node_as_tool_info',
 21 |     'get_property_dependencies'
 22 |   ],
 23 |   validation: [
 24 |     'validate_node_minimal',
 25 |     'validate_node_operation',
 26 |     'validate_workflow',
 27 |     'validate_workflow_connections',
 28 |     'validate_workflow_expressions'
 29 |   ],
 30 |   templates: [
 31 |     'get_node_for_task',
 32 |     'list_tasks',
 33 |     'list_node_templates',
 34 |     'get_template',
 35 |     'search_templates',
 36 |     'get_templates_for_task'
 37 |   ],
 38 |   workflow_management: [
 39 |     'n8n_create_workflow',
 40 |     'n8n_get_workflow',
 41 |     'n8n_get_workflow_details',
 42 |     'n8n_get_workflow_structure',
 43 |     'n8n_get_workflow_minimal',
 44 |     'n8n_update_full_workflow',
 45 |     'n8n_update_partial_workflow',
 46 |     'n8n_delete_workflow',
 47 |     'n8n_list_workflows',
 48 |     'n8n_validate_workflow',
 49 |     'n8n_trigger_webhook_workflow',
 50 |     'n8n_get_execution',
 51 |     'n8n_list_executions',
 52 |     'n8n_delete_execution'
 53 |   ],
 54 |   system: [
 55 |     'tools_documentation',
 56 |     'n8n_diagnostic',
 57 |     'n8n_health_check',
 58 |     'n8n_list_available_tools'
 59 |   ],
 60 |   special: [
 61 |     'code_node_guide'
 62 |   ]
 63 | };
 64 | 
 65 | const template = (toolName: string, category: string) => `import { ToolDocumentation } from '../types';
 66 | 
 67 | export const ${toCamelCase(toolName)}Doc: ToolDocumentation = {
 68 |   name: '${toolName}',
 69 |   category: '${category}',
 70 |   essentials: {
 71 |     description: 'TODO: Add description from old file',
 72 |     keyParameters: ['TODO'],
 73 |     example: '${toolName}({TODO})',
 74 |     performance: 'TODO',
 75 |     tips: [
 76 |       'TODO: Add tips'
 77 |     ]
 78 |   },
 79 |   full: {
 80 |     description: 'TODO: Add full description',
 81 |     parameters: {
 82 |       // TODO: Add parameters
 83 |     },
 84 |     returns: 'TODO: Add return description',
 85 |     examples: [
 86 |       '${toolName}({TODO}) - TODO'
 87 |     ],
 88 |     useCases: [
 89 |       'TODO: Add use cases'
 90 |     ],
 91 |     performance: 'TODO: Add performance description',
 92 |     bestPractices: [
 93 |       'TODO: Add best practices'
 94 |     ],
 95 |     pitfalls: [
 96 |       'TODO: Add pitfalls'
 97 |     ],
 98 |     relatedTools: ['TODO']
 99 |   }
100 | };`;
101 | 
102 | function toCamelCase(str: string): string {
103 |   return str.split('_').map((part, index) => 
104 |     index === 0 ? part : part.charAt(0).toUpperCase() + part.slice(1)
105 |   ).join('');
106 | }
107 | 
108 | function toKebabCase(str: string): string {
109 |   return str.replace(/_/g, '-');
110 | }
111 | 
112 | // Create template files for tools that don't exist yet
113 | Object.entries(toolsByCategory).forEach(([category, tools]) => {
114 |   tools.forEach(toolName => {
115 |     const fileName = toKebabCase(toolName) + '.ts';
116 |     const filePath = path.join('src/mcp/tool-docs', category, fileName);
117 |     
118 |     // Skip if file already exists
119 |     if (fs.existsSync(filePath)) {
120 |       console.log(`✓ ${filePath} already exists`);
121 |       return;
122 |     }
123 |     
124 |     // Create the file with template
125 |     fs.writeFileSync(filePath, template(toolName, category));
126 |     console.log(`✨ Created ${filePath}`);
127 |   });
128 |   
129 |   // Create index file for the category
130 |   const indexPath = path.join('src/mcp/tool-docs', category, 'index.ts');
131 |   if (!fs.existsSync(indexPath)) {
132 |     const indexContent = tools.map(toolName => 
133 |       `export { ${toCamelCase(toolName)}Doc } from './${toKebabCase(toolName)}';`
134 |     ).join('\n');
135 |     
136 |     fs.writeFileSync(indexPath, indexContent);
137 |     console.log(`✨ Created ${indexPath}`);
138 |   }
139 | });
140 | 
141 | console.log('\n📝 Migration templates created!');
142 | console.log('Next steps:');
143 | console.log('1. Copy documentation from the old tools-documentation.ts file');
144 | console.log('2. Update each template file with the actual documentation');
145 | console.log('3. Update src/mcp/tool-docs/index.ts to import all tools');
146 | console.log('4. Replace the old tools-documentation.ts with the new one');
```

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

```markdown
  1 | # Docker Config File Support Tests
  2 | 
  3 | This directory contains comprehensive tests for the Docker config file support feature added to n8n-mcp.
  4 | 
  5 | ## Test Structure
  6 | 
  7 | ### Unit Tests (`tests/unit/docker/`)
  8 | 
  9 | 1. **parse-config.test.ts** - Tests for the JSON config parser
 10 |    - Basic JSON parsing functionality
 11 |    - Environment variable precedence
 12 |    - Shell escaping and quoting
 13 |    - Nested object flattening
 14 |    - Error handling for invalid JSON
 15 | 
 16 | 2. **serve-command.test.ts** - Tests for "n8n-mcp serve" command
 17 |    - Command transformation logic
 18 |    - Argument preservation
 19 |    - Integration with config loading
 20 |    - Backwards compatibility
 21 | 
 22 | 3. **config-security.test.ts** - Security-focused tests
 23 |    - Command injection prevention
 24 |    - Shell metacharacter handling
 25 |    - Path traversal protection
 26 |    - Polyglot payload defense
 27 |    - Real-world attack scenarios
 28 | 
 29 | 4. **edge-cases.test.ts** - Edge case and stress tests
 30 |    - JavaScript number edge cases
 31 |    - Unicode handling
 32 |    - Deep nesting performance
 33 |    - Large config files
 34 |    - Invalid data types
 35 | 
 36 | ### Integration Tests (`tests/integration/docker/`)
 37 | 
 38 | 1. **docker-config.test.ts** - Full Docker container tests with config files
 39 |    - Config file loading and parsing
 40 |    - Environment variable precedence
 41 |    - Security in container context
 42 |    - Complex configuration scenarios
 43 | 
 44 | 2. **docker-entrypoint.test.ts** - Docker entrypoint script tests
 45 |    - MCP mode handling
 46 |    - Database initialization
 47 |    - Permission management
 48 |    - Signal handling
 49 |    - Authentication validation
 50 | 
 51 | ## Running the Tests
 52 | 
 53 | ### Prerequisites
 54 | - Node.js and npm installed
 55 | - Docker installed (for integration tests)
 56 | - Build the project first: `npm run build`
 57 | 
 58 | ### Commands
 59 | 
 60 | ```bash
 61 | # Run all Docker config tests
 62 | npm run test:docker
 63 | 
 64 | # Run only unit tests (no Docker required)
 65 | npm run test:docker:unit
 66 | 
 67 | # Run only integration tests (requires Docker)
 68 | npm run test:docker:integration
 69 | 
 70 | # Run security-focused tests
 71 | npm run test:docker:security
 72 | 
 73 | # Run with coverage
 74 | ./scripts/test-docker-config.sh coverage
 75 | ```
 76 | 
 77 | ### Individual test files
 78 | ```bash
 79 | # Run a specific test file
 80 | npm test -- tests/unit/docker/parse-config.test.ts
 81 | 
 82 | # Run with watch mode
 83 | npm run test:watch -- tests/unit/docker/
 84 | 
 85 | # Run with coverage
 86 | npm run test:coverage -- tests/unit/docker/config-security.test.ts
 87 | ```
 88 | 
 89 | ## Test Coverage
 90 | 
 91 | The tests cover:
 92 | 
 93 | 1. **Functionality**
 94 |    - JSON parsing and environment variable conversion
 95 |    - Nested object flattening with underscore separation
 96 |    - Environment variable precedence (env vars override config)
 97 |    - "n8n-mcp serve" command auto-enables HTTP mode
 98 | 
 99 | 2. **Security**
100 |    - Command injection prevention through proper shell escaping
101 |    - Protection against malicious config values
102 |    - Safe handling of special characters and Unicode
103 |    - Prevention of path traversal attacks
104 | 
105 | 3. **Edge Cases**
106 |    - Invalid JSON handling
107 |    - Missing config files
108 |    - Permission errors
109 |    - Very large config files
110 |    - Deep nesting performance
111 | 
112 | 4. **Integration**
113 |    - Full Docker container behavior
114 |    - Database initialization with file locking
115 |    - Permission handling (root vs nodejs user)
116 |    - Signal propagation and process management
117 | 
118 | ## CI/CD Considerations
119 | 
120 | Integration tests are skipped by default unless:
121 | - Running in CI (CI=true environment variable)
122 | - Explicitly enabled (RUN_DOCKER_TESTS=true)
123 | 
124 | This prevents test failures on developer machines without Docker.
125 | 
126 | ## Security Notes
127 | 
128 | The config parser implements defense in depth:
129 | 1. All values are wrapped in single quotes for shell safety
130 | 2. Single quotes within values are escaped as '"'"'
131 | 3. No variable expansion occurs within single quotes
132 | 4. Arrays and null values are ignored (not exported)
133 | 5. The parser exits silently on any error to prevent container startup issues
134 | 
135 | ## Troubleshooting
136 | 
137 | If tests fail:
138 | 1. Ensure Docker is running (for integration tests)
139 | 2. Check that the project is built (`npm run build`)
140 | 3. Verify no containers are left running: `docker ps -a | grep n8n-mcp-test`
141 | 4. Clean up test containers: `docker rm $(docker ps -aq -f name=n8n-mcp-test)`
```

--------------------------------------------------------------------------------
/src/telemetry/mutation-types.ts:
--------------------------------------------------------------------------------

```typescript
  1 | /**
  2 |  * Types and interfaces for workflow mutation tracking
  3 |  * Purpose: Track workflow transformations to improve partial updates tooling
  4 |  */
  5 | 
  6 | import { DiffOperation } from '../types/workflow-diff.js';
  7 | 
  8 | /**
  9 |  * Intent classification for workflow mutations
 10 |  */
 11 | export enum IntentClassification {
 12 |   ADD_FUNCTIONALITY = 'add_functionality',
 13 |   MODIFY_CONFIGURATION = 'modify_configuration',
 14 |   REWIRE_LOGIC = 'rewire_logic',
 15 |   FIX_VALIDATION = 'fix_validation',
 16 |   CLEANUP = 'cleanup',
 17 |   UNKNOWN = 'unknown',
 18 | }
 19 | 
 20 | /**
 21 |  * Tool names that perform workflow mutations
 22 |  */
 23 | export enum MutationToolName {
 24 |   UPDATE_PARTIAL = 'n8n_update_partial_workflow',
 25 |   UPDATE_FULL = 'n8n_update_full_workflow',
 26 | }
 27 | 
 28 | /**
 29 |  * Validation result structure
 30 |  */
 31 | export interface ValidationResult {
 32 |   valid: boolean;
 33 |   errors: Array<{
 34 |     type: string;
 35 |     message: string;
 36 |     severity?: string;
 37 |     location?: string;
 38 |   }>;
 39 |   warnings?: Array<{
 40 |     type: string;
 41 |     message: string;
 42 |   }>;
 43 | }
 44 | 
 45 | /**
 46 |  * Change metrics calculated from workflow mutation
 47 |  */
 48 | export interface MutationChangeMetrics {
 49 |   nodesAdded: number;
 50 |   nodesRemoved: number;
 51 |   nodesModified: number;
 52 |   connectionsAdded: number;
 53 |   connectionsRemoved: number;
 54 |   propertiesChanged: number;
 55 | }
 56 | 
 57 | /**
 58 |  * Validation improvement metrics
 59 |  */
 60 | export interface MutationValidationMetrics {
 61 |   validationImproved: boolean | null;
 62 |   errorsResolved: number;
 63 |   errorsIntroduced: number;
 64 | }
 65 | 
 66 | /**
 67 |  * Input data for tracking a workflow mutation
 68 |  */
 69 | export interface WorkflowMutationData {
 70 |   sessionId: string;
 71 |   toolName: MutationToolName;
 72 |   userIntent: string;
 73 |   operations: DiffOperation[];
 74 |   workflowBefore: any;
 75 |   workflowAfter: any;
 76 |   validationBefore?: ValidationResult;
 77 |   validationAfter?: ValidationResult;
 78 |   mutationSuccess: boolean;
 79 |   mutationError?: string;
 80 |   durationMs: number;
 81 | }
 82 | 
 83 | /**
 84 |  * Complete mutation record for database storage
 85 |  */
 86 | export interface WorkflowMutationRecord {
 87 |   id?: string;
 88 |   userId: string;
 89 |   sessionId: string;
 90 |   workflowBefore: any;
 91 |   workflowAfter: any;
 92 |   workflowHashBefore: string;
 93 |   workflowHashAfter: string;
 94 |   /** Structural hash (nodeTypes + connections) for cross-referencing with telemetry_workflows */
 95 |   workflowStructureHashBefore?: string;
 96 |   /** Structural hash (nodeTypes + connections) for cross-referencing with telemetry_workflows */
 97 |   workflowStructureHashAfter?: string;
 98 |   /** Computed field: true if mutation executed successfully, improved validation, and has known intent */
 99 |   isTrulySuccessful?: boolean;
100 |   userIntent: string;
101 |   intentClassification: IntentClassification;
102 |   toolName: MutationToolName;
103 |   operations: DiffOperation[];
104 |   operationCount: number;
105 |   operationTypes: string[];
106 |   validationBefore?: ValidationResult;
107 |   validationAfter?: ValidationResult;
108 |   validationImproved: boolean | null;
109 |   errorsResolved: number;
110 |   errorsIntroduced: number;
111 |   nodesAdded: number;
112 |   nodesRemoved: number;
113 |   nodesModified: number;
114 |   connectionsAdded: number;
115 |   connectionsRemoved: number;
116 |   propertiesChanged: number;
117 |   mutationSuccess: boolean;
118 |   mutationError?: string;
119 |   durationMs: number;
120 |   createdAt?: Date;
121 | }
122 | 
123 | /**
124 |  * Options for mutation tracking
125 |  */
126 | export interface MutationTrackingOptions {
127 |   /** Whether to track this mutation (default: true) */
128 |   enabled?: boolean;
129 | 
130 |   /** Maximum workflow size in KB to track (default: 500) */
131 |   maxWorkflowSizeKb?: number;
132 | 
133 |   /** Whether to validate data quality before tracking (default: true) */
134 |   validateQuality?: boolean;
135 | 
136 |   /** Whether to sanitize workflows for PII (default: true) */
137 |   sanitize?: boolean;
138 | }
139 | 
140 | /**
141 |  * Mutation tracking statistics for monitoring
142 |  */
143 | export interface MutationTrackingStats {
144 |   totalMutationsTracked: number;
145 |   successfulMutations: number;
146 |   failedMutations: number;
147 |   mutationsWithValidationImprovement: number;
148 |   averageDurationMs: number;
149 |   intentClassificationBreakdown: Record<IntentClassification, number>;
150 |   operationTypeBreakdown: Record<string, number>;
151 | }
152 | 
153 | /**
154 |  * Data quality validation result
155 |  */
156 | export interface MutationDataQualityResult {
157 |   valid: boolean;
158 |   errors: string[];
159 |   warnings: string[];
160 | }
161 | 
```

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

```typescript
  1 | /**
  2 |  * Integration Tests: handleGetWorkflow
  3 |  *
  4 |  * Tests workflow retrieval against a real n8n instance.
  5 |  * Covers successful retrieval and error handling.
  6 |  */
  7 | 
  8 | import { describe, it, expect, beforeEach, afterEach, afterAll } from 'vitest';
  9 | import { createTestContext, TestContext, createTestWorkflowName } from '../utils/test-context';
 10 | import { getTestN8nClient } from '../utils/n8n-client';
 11 | import { N8nApiClient } from '../../../../src/services/n8n-api-client';
 12 | import { Workflow } from '../../../../src/types/n8n-api';
 13 | import { SIMPLE_WEBHOOK_WORKFLOW } from '../utils/fixtures';
 14 | import { cleanupOrphanedWorkflows } from '../utils/cleanup-helpers';
 15 | import { createMcpContext } from '../utils/mcp-context';
 16 | import { InstanceContext } from '../../../../src/types/instance-context';
 17 | import { handleGetWorkflow } from '../../../../src/mcp/handlers-n8n-manager';
 18 | 
 19 | describe('Integration: handleGetWorkflow', () => {
 20 |   let context: TestContext;
 21 |   let client: N8nApiClient;
 22 |   let mcpContext: InstanceContext;
 23 | 
 24 |   beforeEach(() => {
 25 |     context = createTestContext();
 26 |     client = getTestN8nClient();
 27 |     mcpContext = createMcpContext();
 28 |   });
 29 | 
 30 |   afterEach(async () => {
 31 |     await context.cleanup();
 32 |   });
 33 | 
 34 |   afterAll(async () => {
 35 |     if (!process.env.CI) {
 36 |       await cleanupOrphanedWorkflows();
 37 |     }
 38 |   });
 39 | 
 40 |   // ======================================================================
 41 |   // Successful Retrieval
 42 |   // ======================================================================
 43 | 
 44 |   describe('Successful Retrieval', () => {
 45 |     it('should retrieve complete workflow data', async () => {
 46 |       // Create a workflow first
 47 |       const workflow = {
 48 |         ...SIMPLE_WEBHOOK_WORKFLOW,
 49 |         name: createTestWorkflowName('Get Workflow - Complete Data'),
 50 |         tags: ['mcp-integration-test']
 51 |       };
 52 | 
 53 |       const created = await client.createWorkflow(workflow);
 54 |       expect(created).toBeDefined();
 55 |       expect(created.id).toBeTruthy();
 56 | 
 57 |       if (!created.id) throw new Error('Workflow ID is missing');
 58 |       context.trackWorkflow(created.id);
 59 | 
 60 |       // Retrieve the workflow using MCP handler
 61 |       const response = await handleGetWorkflow({ id: created.id }, mcpContext);
 62 | 
 63 |       // Verify MCP response structure
 64 |       expect(response.success).toBe(true);
 65 |       expect(response.data).toBeDefined();
 66 | 
 67 |       const retrieved = response.data as Workflow;
 68 | 
 69 |       // Verify all expected fields are present
 70 |       expect(retrieved).toBeDefined();
 71 |       expect(retrieved.id).toBe(created.id);
 72 |       expect(retrieved.name).toBe(workflow.name);
 73 |       expect(retrieved.nodes).toBeDefined();
 74 |       expect(retrieved.nodes).toHaveLength(workflow.nodes!.length);
 75 |       expect(retrieved.connections).toBeDefined();
 76 |       expect(retrieved.active).toBeDefined();
 77 |       expect(retrieved.createdAt).toBeDefined();
 78 |       expect(retrieved.updatedAt).toBeDefined();
 79 | 
 80 |       // Verify node data integrity
 81 |       const retrievedNode = retrieved.nodes[0];
 82 |       const originalNode = workflow.nodes![0];
 83 |       expect(retrievedNode.name).toBe(originalNode.name);
 84 |       expect(retrievedNode.type).toBe(originalNode.type);
 85 |       expect(retrievedNode.parameters).toBeDefined();
 86 |     });
 87 |   });
 88 | 
 89 |   // ======================================================================
 90 |   // Error Handling
 91 |   // ======================================================================
 92 | 
 93 |   describe('Error Handling', () => {
 94 |     it('should return error for non-existent workflow (invalid ID)', async () => {
 95 |       const invalidId = '99999999';
 96 | 
 97 |       const response = await handleGetWorkflow({ id: invalidId }, mcpContext);
 98 | 
 99 |       // MCP handlers return success: false on error
100 |       expect(response.success).toBe(false);
101 |       expect(response.error).toBeDefined();
102 |     });
103 | 
104 |     it('should return error for malformed workflow ID', async () => {
105 |       const malformedId = 'not-a-valid-id-format';
106 | 
107 |       const response = await handleGetWorkflow({ id: malformedId }, mcpContext);
108 | 
109 |       // MCP handlers return success: false on error
110 |       expect(response.success).toBe(false);
111 |       expect(response.error).toBeDefined();
112 |     });
113 |   });
114 | });
115 | 
```

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

```typescript
  1 | #!/usr/bin/env npx tsx
  2 | 
  3 | /**
  4 |  * Test script for Expression vs Code Node validation
  5 |  * Tests that we properly detect and warn about expression syntax in Code nodes
  6 |  */
  7 | 
  8 | import { EnhancedConfigValidator } from '../src/services/enhanced-config-validator.js';
  9 | 
 10 | console.log('🧪 Testing Expression vs Code Node Validation\n');
 11 | 
 12 | // Test cases with expression syntax that shouldn't work in Code nodes
 13 | const testCases = [
 14 |   {
 15 |     name: 'Expression syntax in Code node',
 16 |     config: {
 17 |       language: 'javaScript',
 18 |       jsCode: `// Using expression syntax
 19 | const value = {{$json.field}};
 20 | return [{json: {value}}];`
 21 |     },
 22 |     expectedError: 'Expression syntax {{...}} is not valid in Code nodes'
 23 |   },
 24 |   {
 25 |     name: 'Wrong $node syntax',
 26 |     config: {
 27 |       language: 'javaScript',
 28 |       jsCode: `// Using expression $node syntax
 29 | const data = $node['Previous Node'].json;
 30 | return [{json: data}];`
 31 |     },
 32 |     expectedWarning: 'Use $(\'Node Name\') instead of $node[\'Node Name\'] in Code nodes'
 33 |   },
 34 |   {
 35 |     name: 'Expression-only functions',
 36 |     config: {
 37 |       language: 'javaScript',
 38 |       jsCode: `// Using expression functions
 39 | const now = $now();
 40 | const unique = items.unique();
 41 | return [{json: {now, unique}}];`
 42 |     },
 43 |     expectedWarning: '$now() is an expression-only function'
 44 |   },
 45 |   {
 46 |     name: 'Wrong JMESPath parameter order',
 47 |     config: {
 48 |       language: 'javaScript',
 49 |       jsCode: `// Wrong parameter order
 50 | const result = $jmespath("users[*].name", data);
 51 | return [{json: {result}}];`
 52 |     },
 53 |     expectedWarning: 'Code node $jmespath has reversed parameter order'
 54 |   },
 55 |   {
 56 |     name: 'Correct Code node syntax',
 57 |     config: {
 58 |       language: 'javaScript',
 59 |       jsCode: `// Correct syntax
 60 | const prevData = $('Previous Node').first();
 61 | const now = DateTime.now();
 62 | const result = $jmespath(data, "users[*].name");
 63 | return [{json: {prevData, now, result}}];`
 64 |     },
 65 |     shouldBeValid: true
 66 |   }
 67 | ];
 68 | 
 69 | // Basic node properties for Code node
 70 | const codeNodeProperties = [
 71 |   { name: 'language', type: 'options', options: ['javaScript', 'python'] },
 72 |   { name: 'jsCode', type: 'string' },
 73 |   { name: 'pythonCode', type: 'string' },
 74 |   { name: 'mode', type: 'options', options: ['runOnceForAllItems', 'runOnceForEachItem'] }
 75 | ];
 76 | 
 77 | console.log('Running validation tests...\n');
 78 | 
 79 | testCases.forEach((test, index) => {
 80 |   console.log(`Test ${index + 1}: ${test.name}`);
 81 |   console.log('─'.repeat(50));
 82 |   
 83 |   const result = EnhancedConfigValidator.validateWithMode(
 84 |     'nodes-base.code',
 85 |     test.config,
 86 |     codeNodeProperties,
 87 |     'operation',
 88 |     'ai-friendly'
 89 |   );
 90 |   
 91 |   console.log(`Valid: ${result.valid}`);
 92 |   console.log(`Errors: ${result.errors.length}`);
 93 |   console.log(`Warnings: ${result.warnings.length}`);
 94 |   
 95 |   if (test.expectedError) {
 96 |     const hasExpectedError = result.errors.some(e => 
 97 |       e.message.includes(test.expectedError)
 98 |     );
 99 |     console.log(`✅ Expected error found: ${hasExpectedError}`);
100 |     if (!hasExpectedError) {
101 |       console.log('❌ Missing expected error:', test.expectedError);
102 |       console.log('Actual errors:', result.errors.map(e => e.message));
103 |     }
104 |   }
105 |   
106 |   if (test.expectedWarning) {
107 |     const hasExpectedWarning = result.warnings.some(w => 
108 |       w.message.includes(test.expectedWarning)
109 |     );
110 |     console.log(`✅ Expected warning found: ${hasExpectedWarning}`);
111 |     if (!hasExpectedWarning) {
112 |       console.log('❌ Missing expected warning:', test.expectedWarning);
113 |       console.log('Actual warnings:', result.warnings.map(w => w.message));
114 |     }
115 |   }
116 |   
117 |   if (test.shouldBeValid) {
118 |     console.log(`✅ Should be valid: ${result.valid && result.errors.length === 0}`);
119 |     if (!result.valid || result.errors.length > 0) {
120 |       console.log('❌ Unexpected errors:', result.errors);
121 |     }
122 |   }
123 |   
124 |   // Show actual messages
125 |   if (result.errors.length > 0) {
126 |     console.log('\nErrors:');
127 |     result.errors.forEach(e => console.log(`  - ${e.message}`));
128 |   }
129 |   
130 |   if (result.warnings.length > 0) {
131 |     console.log('\nWarnings:');
132 |     result.warnings.forEach(w => console.log(`  - ${w.message}`));
133 |   }
134 |   
135 |   console.log('\n');
136 | });
137 | 
138 | console.log('✅ Expression vs Code Node validation tests completed!');
```

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

```javascript
  1 | #!/usr/bin/env node
  2 | 
  3 | /**
  4 |  * Test SQLite database search functionality
  5 |  */
  6 | 
  7 | const { SQLiteStorageService } = require('../dist/services/sqlite-storage-service');
  8 | const { NodeSourceExtractor } = require('../dist/utils/node-source-extractor');
  9 | 
 10 | async function testDatabaseSearch() {
 11 |   console.log('=== SQLite Database Search Test ===\n');
 12 |   
 13 |   const storage = new SQLiteStorageService();
 14 |   const extractor = new NodeSourceExtractor();
 15 |   
 16 |   // First, ensure we have some data
 17 |   console.log('1️⃣ Checking database status...');
 18 |   let stats = await storage.getStatistics();
 19 |   
 20 |   if (stats.totalNodes === 0) {
 21 |     console.log('   Database is empty. Adding some test nodes...\n');
 22 |     
 23 |     const testNodes = [
 24 |       'n8n-nodes-base.Function',
 25 |       'n8n-nodes-base.Webhook',
 26 |       'n8n-nodes-base.HttpRequest',
 27 |       'n8n-nodes-base.If',
 28 |       'n8n-nodes-base.Slack',
 29 |       'n8n-nodes-base.Discord'
 30 |     ];
 31 |     
 32 |     for (const nodeType of testNodes) {
 33 |       try {
 34 |         const nodeInfo = await extractor.extractNodeSource(nodeType);
 35 |         await storage.storeNode(nodeInfo);
 36 |         console.log(`   ✅ Stored ${nodeType}`);
 37 |       } catch (error) {
 38 |         console.log(`   ❌ Failed to store ${nodeType}: ${error.message}`);
 39 |       }
 40 |     }
 41 |     
 42 |     stats = await storage.getStatistics();
 43 |   }
 44 |   
 45 |   console.log(`\n   Total nodes in database: ${stats.totalNodes}`);
 46 |   console.log(`   Total packages: ${stats.totalPackages}`);
 47 |   console.log(`   Database size: ${(stats.totalCodeSize / 1024).toFixed(2)} KB\n`);
 48 |   
 49 |   // Test different search scenarios
 50 |   console.log('2️⃣ Testing search functionality...\n');
 51 |   
 52 |   const searchTests = [
 53 |     {
 54 |       name: 'Search by partial name (func)',
 55 |       query: { query: 'func' }
 56 |     },
 57 |     {
 58 |       name: 'Search by partial name (web)',
 59 |       query: { query: 'web' }
 60 |     },
 61 |     {
 62 |       name: 'Search for HTTP',
 63 |       query: { query: 'http' }
 64 |     },
 65 |     {
 66 |       name: 'Search for multiple terms',
 67 |       query: { query: 'slack discord' }
 68 |     },
 69 |     {
 70 |       name: 'Filter by package',
 71 |       query: { packageName: 'n8n-nodes-base' }
 72 |     },
 73 |     {
 74 |       name: 'Search with package filter',
 75 |       query: { query: 'func', packageName: 'n8n-nodes-base' }
 76 |     },
 77 |     {
 78 |       name: 'Search by node type',
 79 |       query: { nodeType: 'Webhook' }
 80 |     },
 81 |     {
 82 |       name: 'Limit results',
 83 |       query: { query: 'node', limit: 3 }
 84 |     }
 85 |   ];
 86 |   
 87 |   for (const test of searchTests) {
 88 |     console.log(`   📍 ${test.name}:`);
 89 |     console.log(`      Query: ${JSON.stringify(test.query)}`);
 90 |     
 91 |     try {
 92 |       const results = await storage.searchNodes(test.query);
 93 |       console.log(`      Results: ${results.length} nodes found`);
 94 |       
 95 |       if (results.length > 0) {
 96 |         console.log('      Matches:');
 97 |         results.slice(0, 3).forEach(node => {
 98 |           console.log(`        - ${node.nodeType} (${node.displayName || node.name})`);
 99 |         });
100 |         if (results.length > 3) {
101 |           console.log(`        ... and ${results.length - 3} more`);
102 |         }
103 |       }
104 |     } catch (error) {
105 |       console.log(`      ❌ Error: ${error.message}`);
106 |     }
107 |     
108 |     console.log('');
109 |   }
110 |   
111 |   // Test specific node retrieval
112 |   console.log('3️⃣ Testing specific node retrieval...\n');
113 |   
114 |   const specificNode = await storage.getNode('n8n-nodes-base.Function');
115 |   if (specificNode) {
116 |     console.log(`   ✅ Found node: ${specificNode.nodeType}`);
117 |     console.log(`      Display name: ${specificNode.displayName}`);
118 |     console.log(`      Code size: ${specificNode.codeLength} bytes`);
119 |     console.log(`      Has credentials: ${specificNode.hasCredentials}`);
120 |   } else {
121 |     console.log('   ❌ Node not found');
122 |   }
123 |   
124 |   // Test package listing
125 |   console.log('\n4️⃣ Testing package listing...\n');
126 |   
127 |   const packages = await storage.getPackages();
128 |   console.log(`   Found ${packages.length} packages:`);
129 |   packages.forEach(pkg => {
130 |     console.log(`     - ${pkg.name}: ${pkg.nodeCount} nodes`);
131 |   });
132 |   
133 |   // Close database
134 |   storage.close();
135 |   
136 |   console.log('\n✅ Search functionality test completed!');
137 | }
138 | 
139 | // Run the test
140 | testDatabaseSearch().catch(error => {
141 |   console.error('Test failed:', error);
142 |   process.exit(1);
143 | });
```

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

```typescript
  1 | /**
  2 |  * Node Classification Utilities
  3 |  *
  4 |  * Provides shared classification logic for workflow nodes.
  5 |  * Used by validators to consistently identify node types across the codebase.
  6 |  *
  7 |  * This module centralizes node type classification to ensure consistent behavior
  8 |  * between WorkflowValidator and n8n-validation.ts, preventing bugs like sticky
  9 |  * notes being incorrectly flagged as disconnected nodes.
 10 |  */
 11 | 
 12 | import { isTriggerNode as isTriggerNodeImpl } from './node-type-utils';
 13 | 
 14 | /**
 15 |  * Check if a node type is a sticky note (documentation-only node)
 16 |  *
 17 |  * Sticky notes are UI-only annotation nodes that:
 18 |  * - Do not participate in workflow execution
 19 |  * - Never have connections (by design)
 20 |  * - Should be excluded from connection validation
 21 |  * - Serve purely as visual documentation in the workflow canvas
 22 |  *
 23 |  * Example sticky note types:
 24 |  * - 'n8n-nodes-base.stickyNote' (standard format)
 25 |  * - 'nodes-base.stickyNote' (normalized format)
 26 |  * - '@n8n/n8n-nodes-base.stickyNote' (scoped format)
 27 |  *
 28 |  * @param nodeType - The node type to check (e.g., 'n8n-nodes-base.stickyNote')
 29 |  * @returns true if the node is a sticky note, false otherwise
 30 |  */
 31 | export function isStickyNote(nodeType: string): boolean {
 32 |   const stickyNoteTypes = [
 33 |     'n8n-nodes-base.stickyNote',
 34 |     'nodes-base.stickyNote',
 35 |     '@n8n/n8n-nodes-base.stickyNote'
 36 |   ];
 37 |   return stickyNoteTypes.includes(nodeType);
 38 | }
 39 | 
 40 | /**
 41 |  * Check if a node type is a trigger node
 42 |  *
 43 |  * This function delegates to the comprehensive trigger detection implementation
 44 |  * in node-type-utils.ts which supports 200+ trigger types using flexible
 45 |  * pattern matching instead of a hardcoded list.
 46 |  *
 47 |  * Trigger nodes:
 48 |  * - Start workflow execution
 49 |  * - Only need outgoing connections (no incoming connections required)
 50 |  * - Include webhooks, manual triggers, schedule triggers, email triggers, etc.
 51 |  * - Are the entry points for workflow execution
 52 |  *
 53 |  * Examples:
 54 |  * - Webhooks: Listen for HTTP requests
 55 |  * - Manual triggers: Started manually by user
 56 |  * - Schedule/Cron triggers: Run on a schedule
 57 |  * - Execute Workflow Trigger: Invoked by other workflows
 58 |  *
 59 |  * @param nodeType - The node type to check
 60 |  * @returns true if the node is a trigger, false otherwise
 61 |  */
 62 | export function isTriggerNode(nodeType: string): boolean {
 63 |   return isTriggerNodeImpl(nodeType);
 64 | }
 65 | 
 66 | /**
 67 |  * Check if a node type is non-executable (UI-only)
 68 |  *
 69 |  * Non-executable nodes:
 70 |  * - Do not participate in workflow execution
 71 |  * - Serve documentation/annotation purposes only
 72 |  * - Should be excluded from all execution-related validation
 73 |  * - Should be excluded from statistics like "total executable nodes"
 74 |  * - Should be excluded from connection validation
 75 |  *
 76 |  * Currently includes: sticky notes
 77 |  *
 78 |  * Future: May include other annotation/comment nodes if n8n adds them
 79 |  *
 80 |  * @param nodeType - The node type to check
 81 |  * @returns true if the node is non-executable, false otherwise
 82 |  */
 83 | export function isNonExecutableNode(nodeType: string): boolean {
 84 |   return isStickyNote(nodeType);
 85 |   // Future: Add other non-executable node types here
 86 |   // Example: || isCommentNode(nodeType) || isAnnotationNode(nodeType)
 87 | }
 88 | 
 89 | /**
 90 |  * Check if a node type requires incoming connections
 91 |  *
 92 |  * Most nodes require at least one incoming connection to receive data,
 93 |  * but there are two categories of exceptions:
 94 |  *
 95 |  * 1. Trigger nodes: Only need outgoing connections
 96 |  *    - They start workflow execution
 97 |  *    - They generate their own data
 98 |  *    - Examples: webhook, manualTrigger, scheduleTrigger
 99 |  *
100 |  * 2. Non-executable nodes: Don't need any connections
101 |  *    - They are UI-only annotations
102 |  *    - They don't participate in execution
103 |  *    - Examples: stickyNote
104 |  *
105 |  * @param nodeType - The node type to check
106 |  * @returns true if the node requires incoming connections, false otherwise
107 |  */
108 | export function requiresIncomingConnection(nodeType: string): boolean {
109 |   // Non-executable nodes don't need any connections
110 |   if (isNonExecutableNode(nodeType)) {
111 |     return false;
112 |   }
113 | 
114 |   // Trigger nodes only need outgoing connections
115 |   if (isTriggerNode(nodeType)) {
116 |     return false;
117 |   }
118 | 
119 |   // Regular nodes need incoming connections
120 |   return true;
121 | }
122 | 
```

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

```typescript
  1 | #!/usr/bin/env ts-node
  2 | /**
  3 |  * Quick test script to validate the essentials implementation
  4 |  */
  5 | 
  6 | import { spawn } from 'child_process';
  7 | import { join } from 'path';
  8 | 
  9 | const colors = {
 10 |   reset: '\x1b[0m',
 11 |   bright: '\x1b[1m',
 12 |   green: '\x1b[32m',
 13 |   red: '\x1b[31m',
 14 |   yellow: '\x1b[33m',
 15 |   blue: '\x1b[34m',
 16 |   cyan: '\x1b[36m'
 17 | };
 18 | 
 19 | function log(message: string, color: string = colors.reset) {
 20 |   console.log(`${color}${message}${colors.reset}`);
 21 | }
 22 | 
 23 | async function runMCPCommand(toolName: string, args: any): Promise<any> {
 24 |   return new Promise((resolve, reject) => {
 25 |     const request = {
 26 |       jsonrpc: '2.0',
 27 |       method: 'tools/call',
 28 |       params: {
 29 |         name: toolName,
 30 |         arguments: args
 31 |       },
 32 |       id: 1
 33 |     };
 34 |     
 35 |     const mcp = spawn('npm', ['start'], {
 36 |       cwd: join(__dirname, '..'),
 37 |       stdio: ['pipe', 'pipe', 'pipe']
 38 |     });
 39 |     
 40 |     let output = '';
 41 |     let error = '';
 42 |     
 43 |     mcp.stdout.on('data', (data) => {
 44 |       output += data.toString();
 45 |     });
 46 |     
 47 |     mcp.stderr.on('data', (data) => {
 48 |       error += data.toString();
 49 |     });
 50 |     
 51 |     mcp.on('close', (code) => {
 52 |       if (code !== 0) {
 53 |         reject(new Error(`Process exited with code ${code}: ${error}`));
 54 |         return;
 55 |       }
 56 |       
 57 |       try {
 58 |         // Parse JSON-RPC response
 59 |         const lines = output.split('\n');
 60 |         for (const line of lines) {
 61 |           if (line.trim() && line.includes('"jsonrpc"')) {
 62 |             const response = JSON.parse(line);
 63 |             if (response.result) {
 64 |               resolve(JSON.parse(response.result.content[0].text));
 65 |               return;
 66 |             } else if (response.error) {
 67 |               reject(new Error(response.error.message));
 68 |               return;
 69 |             }
 70 |           }
 71 |         }
 72 |         reject(new Error('No valid response found'));
 73 |       } catch (err) {
 74 |         reject(err);
 75 |       }
 76 |     });
 77 |     
 78 |     // Send request
 79 |     mcp.stdin.write(JSON.stringify(request) + '\n');
 80 |     mcp.stdin.end();
 81 |   });
 82 | }
 83 | 
 84 | async function quickTest() {
 85 |   log('\n🚀 Quick Test - n8n MCP Essentials', colors.bright + colors.cyan);
 86 |   
 87 |   try {
 88 |     // Test 1: Get essentials for HTTP Request
 89 |     log('\n1️⃣  Testing get_node_essentials for HTTP Request...', colors.yellow);
 90 |     const essentials = await runMCPCommand('get_node_essentials', {
 91 |       nodeType: 'nodes-base.httpRequest'
 92 |     });
 93 |     
 94 |     log('✅ Success! Got essentials:', colors.green);
 95 |     log(`   Required properties: ${essentials.requiredProperties?.map((p: any) => p.name).join(', ') || 'None'}`);
 96 |     log(`   Common properties: ${essentials.commonProperties?.map((p: any) => p.name).join(', ') || 'None'}`);
 97 |     log(`   Examples: ${Object.keys(essentials.examples || {}).join(', ')}`);
 98 |     log(`   Response size: ${JSON.stringify(essentials).length} bytes`, colors.green);
 99 |     
100 |     // Test 2: Search properties
101 |     log('\n2️⃣  Testing search_node_properties...', colors.yellow);
102 |     const searchResults = await runMCPCommand('search_node_properties', {
103 |       nodeType: 'nodes-base.httpRequest',
104 |       query: 'auth'
105 |     });
106 |     
107 |     log('✅ Success! Found properties:', colors.green);
108 |     log(`   Matches: ${searchResults.totalMatches}`);
109 |     searchResults.matches?.slice(0, 3).forEach((match: any) => {
110 |       log(`   - ${match.name}: ${match.description}`);
111 |     });
112 |     
113 |     // Test 3: Compare sizes
114 |     log('\n3️⃣  Comparing response sizes...', colors.yellow);
115 |     const fullInfo = await runMCPCommand('get_node_info', {
116 |       nodeType: 'nodes-base.httpRequest'
117 |     });
118 |     
119 |     const fullSize = JSON.stringify(fullInfo).length;
120 |     const essentialSize = JSON.stringify(essentials).length;
121 |     const reduction = ((fullSize - essentialSize) / fullSize * 100).toFixed(1);
122 |     
123 |     log(`✅ Size comparison:`, colors.green);
124 |     log(`   Full response: ${(fullSize / 1024).toFixed(1)} KB`);
125 |     log(`   Essential response: ${(essentialSize / 1024).toFixed(1)} KB`);
126 |     log(`   Size reduction: ${reduction}% 🎉`, colors.bright + colors.green);
127 |     
128 |     log('\n✨ All tests passed!', colors.bright + colors.green);
129 |     
130 |   } catch (error) {
131 |     log(`\n❌ Test failed: ${error}`, colors.red);
132 |     process.exit(1);
133 |   }
134 | }
135 | 
136 | // Run if called directly
137 | if (require.main === module) {
138 |   quickTest().catch(console.error);
139 | }
```

--------------------------------------------------------------------------------
/scripts/generate-release-notes.js:
--------------------------------------------------------------------------------

```javascript
  1 | #!/usr/bin/env node
  2 | 
  3 | /**
  4 |  * Generate release notes from commit messages between two tags
  5 |  * Used by GitHub Actions to create automated release notes
  6 |  */
  7 | 
  8 | const { execSync } = require('child_process');
  9 | const fs = require('fs');
 10 | const path = require('path');
 11 | 
 12 | function generateReleaseNotes(previousTag, currentTag) {
 13 |   try {
 14 |     console.log(`Generating release notes from ${previousTag} to ${currentTag}`);
 15 | 
 16 |     // Get commits between tags
 17 |     const gitLogCommand = `git log --pretty=format:"%H|%s|%an|%ae|%ad" --date=short --no-merges ${previousTag}..${currentTag}`;
 18 |     const commitsOutput = execSync(gitLogCommand, { encoding: 'utf8' });
 19 | 
 20 |     if (!commitsOutput.trim()) {
 21 |       console.log('No commits found between tags');
 22 |       return 'No changes in this release.';
 23 |     }
 24 | 
 25 |     const commits = commitsOutput.trim().split('\n').map(line => {
 26 |       const [hash, subject, author, email, date] = line.split('|');
 27 |       return { hash, subject, author, email, date };
 28 |     });
 29 | 
 30 |     // Categorize commits
 31 |     const categories = {
 32 |       'feat': { title: '✨ Features', commits: [] },
 33 |       'fix': { title: '🐛 Bug Fixes', commits: [] },
 34 |       'docs': { title: '📚 Documentation', commits: [] },
 35 |       'refactor': { title: '♻️ Refactoring', commits: [] },
 36 |       'test': { title: '🧪 Testing', commits: [] },
 37 |       'perf': { title: '⚡ Performance', commits: [] },
 38 |       'style': { title: '💅 Styling', commits: [] },
 39 |       'ci': { title: '🔧 CI/CD', commits: [] },
 40 |       'build': { title: '📦 Build', commits: [] },
 41 |       'chore': { title: '🔧 Maintenance', commits: [] },
 42 |       'other': { title: '📝 Other Changes', commits: [] }
 43 |     };
 44 | 
 45 |     commits.forEach(commit => {
 46 |       const subject = commit.subject.toLowerCase();
 47 |       let categorized = false;
 48 | 
 49 |       // Check for conventional commit prefixes
 50 |       for (const [prefix, category] of Object.entries(categories)) {
 51 |         if (prefix !== 'other' && subject.startsWith(`${prefix}:`)) {
 52 |           category.commits.push(commit);
 53 |           categorized = true;
 54 |           break;
 55 |         }
 56 |       }
 57 | 
 58 |       // If not categorized, put in other
 59 |       if (!categorized) {
 60 |         categories.other.commits.push(commit);
 61 |       }
 62 |     });
 63 | 
 64 |     // Generate release notes
 65 |     const releaseNotes = [];
 66 | 
 67 |     for (const [key, category] of Object.entries(categories)) {
 68 |       if (category.commits.length > 0) {
 69 |         releaseNotes.push(`### ${category.title}`);
 70 |         releaseNotes.push('');
 71 | 
 72 |         category.commits.forEach(commit => {
 73 |           // Clean up the subject by removing the prefix if it exists
 74 |           let cleanSubject = commit.subject;
 75 |           const colonIndex = cleanSubject.indexOf(':');
 76 |           if (colonIndex !== -1 && cleanSubject.substring(0, colonIndex).match(/^(feat|fix|docs|refactor|test|perf|style|ci|build|chore)$/)) {
 77 |             cleanSubject = cleanSubject.substring(colonIndex + 1).trim();
 78 |             // Capitalize first letter
 79 |             cleanSubject = cleanSubject.charAt(0).toUpperCase() + cleanSubject.slice(1);
 80 |           }
 81 | 
 82 |           releaseNotes.push(`- ${cleanSubject} (${commit.hash.substring(0, 7)})`);
 83 |         });
 84 | 
 85 |         releaseNotes.push('');
 86 |       }
 87 |     }
 88 | 
 89 |     // Add commit statistics
 90 |     const totalCommits = commits.length;
 91 |     const contributors = [...new Set(commits.map(c => c.author))];
 92 | 
 93 |     releaseNotes.push('---');
 94 |     releaseNotes.push('');
 95 |     releaseNotes.push(`**Release Statistics:**`);
 96 |     releaseNotes.push(`- ${totalCommits} commit${totalCommits !== 1 ? 's' : ''}`);
 97 |     releaseNotes.push(`- ${contributors.length} contributor${contributors.length !== 1 ? 's' : ''}`);
 98 | 
 99 |     if (contributors.length <= 5) {
100 |       releaseNotes.push(`- Contributors: ${contributors.join(', ')}`);
101 |     }
102 | 
103 |     return releaseNotes.join('\n');
104 | 
105 |   } catch (error) {
106 |     console.error(`Error generating release notes: ${error.message}`);
107 |     return `Failed to generate release notes: ${error.message}`;
108 |   }
109 | }
110 | 
111 | // Parse command line arguments
112 | const previousTag = process.argv[2];
113 | const currentTag = process.argv[3];
114 | 
115 | if (!previousTag || !currentTag) {
116 |   console.error('Usage: generate-release-notes.js <previous-tag> <current-tag>');
117 |   process.exit(1);
118 | }
119 | 
120 | const releaseNotes = generateReleaseNotes(previousTag, currentTag);
121 | console.log(releaseNotes);
122 | 
```

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

```typescript
  1 | import { bench, describe } from 'vitest';
  2 | import { NodeRepository } from '../../src/database/node-repository';
  3 | import { SQLiteStorageService } from '../../src/services/sqlite-storage-service';
  4 | import { NodeFactory } from '../factories/node-factory';
  5 | import { PropertyDefinitionFactory } from '../factories/property-definition-factory';
  6 | 
  7 | /**
  8 |  * Database Query Performance Benchmarks
  9 |  *
 10 |  * NOTE: These benchmarks use MOCK DATA (500 artificial test nodes)
 11 |  * created with factories, not the real production database.
 12 |  *
 13 |  * This is useful for tracking database layer performance in isolation,
 14 |  * but may not reflect real-world performance characteristics.
 15 |  *
 16 |  * For end-to-end MCP tool performance with real data, see mcp-tools.bench.ts
 17 |  */
 18 | describe('Database Query Performance', () => {
 19 |   let repository: NodeRepository;
 20 |   let storage: SQLiteStorageService;
 21 |   const testNodeCount = 500;
 22 | 
 23 |   beforeAll(async () => {
 24 |     storage = new SQLiteStorageService(':memory:');
 25 |     repository = new NodeRepository(storage);
 26 |     
 27 |     // Seed database with test data
 28 |     for (let i = 0; i < testNodeCount; i++) {
 29 |       const node = NodeFactory.build({
 30 |         displayName: `TestNode${i}`,
 31 |         nodeType: `nodes-base.testNode${i}`,
 32 |         category: i % 2 === 0 ? 'transform' : 'trigger',
 33 |         packageName: 'n8n-nodes-base',
 34 |         documentation: `Test documentation for node ${i}`,
 35 |         properties: PropertyDefinitionFactory.buildList(5)
 36 |       });
 37 |       await repository.upsertNode(node);
 38 |     }
 39 |   });
 40 | 
 41 |   afterAll(() => {
 42 |     storage.close();
 43 |   });
 44 | 
 45 |   bench('getNodeByType - existing node', async () => {
 46 |     await repository.getNodeByType('nodes-base.testNode100');
 47 |   }, {
 48 |     iterations: 1000,
 49 |     warmupIterations: 100,
 50 |     warmupTime: 500,
 51 |     time: 3000
 52 |   });
 53 | 
 54 |   bench('getNodeByType - non-existing node', async () => {
 55 |     await repository.getNodeByType('nodes-base.nonExistentNode');
 56 |   }, {
 57 |     iterations: 1000,
 58 |     warmupIterations: 100,
 59 |     warmupTime: 500,
 60 |     time: 3000
 61 |   });
 62 | 
 63 |   bench('getNodesByCategory - transform', async () => {
 64 |     await repository.getNodesByCategory('transform');
 65 |   }, {
 66 |     iterations: 100,
 67 |     warmupIterations: 10,
 68 |     warmupTime: 500,
 69 |     time: 3000
 70 |   });
 71 | 
 72 |   bench('searchNodes - OR mode', async () => {
 73 |     await repository.searchNodes('test node data', 'OR', 20);
 74 |   }, {
 75 |     iterations: 100,
 76 |     warmupIterations: 10,
 77 |     warmupTime: 500,
 78 |     time: 3000
 79 |   });
 80 | 
 81 |   bench('searchNodes - AND mode', async () => {
 82 |     await repository.searchNodes('test node', 'AND', 20);
 83 |   }, {
 84 |     iterations: 100,
 85 |     warmupIterations: 10,
 86 |     warmupTime: 500,
 87 |     time: 3000
 88 |   });
 89 | 
 90 |   bench('searchNodes - FUZZY mode', async () => {
 91 |     await repository.searchNodes('tst nde', 'FUZZY', 20);
 92 |   }, {
 93 |     iterations: 100,
 94 |     warmupIterations: 10,
 95 |     warmupTime: 500,
 96 |     time: 3000
 97 |   });
 98 | 
 99 |   bench('getAllNodes - no limit', async () => {
100 |     await repository.getAllNodes();
101 |   }, {
102 |     iterations: 50,
103 |     warmupIterations: 5,
104 |     warmupTime: 500,
105 |     time: 3000
106 |   });
107 | 
108 |   bench('getAllNodes - with limit', async () => {
109 |     await repository.getAllNodes(50);
110 |   }, {
111 |     iterations: 100,
112 |     warmupIterations: 10,
113 |     warmupTime: 500,
114 |     time: 3000
115 |   });
116 | 
117 |   bench('getNodeCount', async () => {
118 |     await repository.getNodeCount();
119 |   }, {
120 |     iterations: 1000,
121 |     warmupIterations: 100,
122 |     warmupTime: 100,
123 |     time: 2000
124 |   });
125 | 
126 |   bench('getAIToolNodes', async () => {
127 |     await repository.getAIToolNodes();
128 |   }, {
129 |     iterations: 100,
130 |     warmupIterations: 10,
131 |     warmupTime: 500,
132 |     time: 3000
133 |   });
134 | 
135 |   bench('upsertNode - new node', async () => {
136 |     const node = NodeFactory.build({
137 |       displayName: `BenchNode${Date.now()}`,
138 |       nodeType: `nodes-base.benchNode${Date.now()}`
139 |     });
140 |     await repository.upsertNode(node);
141 |   }, {
142 |     iterations: 100,
143 |     warmupIterations: 10,
144 |     warmupTime: 500,
145 |     time: 3000
146 |   });
147 | 
148 |   bench('upsertNode - existing node update', async () => {
149 |     const existingNode = await repository.getNodeByType('nodes-base.testNode0');
150 |     if (existingNode) {
151 |       existingNode.description = `Updated description ${Date.now()}`;
152 |       await repository.upsertNode(existingNode);
153 |     }
154 |   }, {
155 |     iterations: 100,
156 |     warmupIterations: 10,
157 |     warmupTime: 500,
158 |     time: 3000
159 |   });
160 | });
```

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

```typescript
  1 | import { describe, it, expect, vi, beforeEach } from 'vitest';
  2 | import { nodeFactory, webhookNodeFactory, slackNodeFactory } from '@tests/fixtures/factories/node.factory';
  3 | 
  4 | // Mock better-sqlite3
  5 | vi.mock('better-sqlite3');
  6 | 
  7 | describe('Test Infrastructure', () => {
  8 |   describe('Database Mock', () => {
  9 |     it('should create a mock database instance', async () => {
 10 |       const Database = (await import('better-sqlite3')).default;
 11 |       const db = new Database(':memory:');
 12 |       
 13 |       expect(Database).toHaveBeenCalled();
 14 |       expect(db).toBeDefined();
 15 |       expect(db.prepare).toBeDefined();
 16 |       expect(db.exec).toBeDefined();
 17 |       expect(db.close).toBeDefined();
 18 |     });
 19 |     
 20 |     it('should handle basic CRUD operations', async () => {
 21 |       const { MockDatabase } = await import('@tests/unit/database/__mocks__/better-sqlite3');
 22 |       const db = new MockDatabase();
 23 |       
 24 |       // Test data seeding
 25 |       db._seedData('nodes', [
 26 |         { id: '1', name: 'test-node', type: 'webhook' }
 27 |       ]);
 28 |       
 29 |       // Test SELECT
 30 |       const selectStmt = db.prepare('SELECT * FROM nodes');
 31 |       const allNodes = selectStmt.all();
 32 |       expect(allNodes).toHaveLength(1);
 33 |       expect(allNodes[0]).toEqual({ id: '1', name: 'test-node', type: 'webhook' });
 34 |       
 35 |       // Test INSERT
 36 |       const insertStmt = db.prepare('INSERT INTO nodes (id, name, type) VALUES (?, ?, ?)');
 37 |       const result = insertStmt.run({ id: '2', name: 'new-node', type: 'slack' });
 38 |       expect(result.changes).toBe(1);
 39 |       
 40 |       // Verify insert worked
 41 |       const allNodesAfter = selectStmt.all();
 42 |       expect(allNodesAfter).toHaveLength(2);
 43 |     });
 44 |   });
 45 |   
 46 |   describe('Node Factory', () => {
 47 |     it('should create a basic node definition', () => {
 48 |       const node = nodeFactory.build();
 49 |       
 50 |       expect(node).toMatchObject({
 51 |         name: expect.any(String),
 52 |         displayName: expect.any(String),
 53 |         description: expect.any(String),
 54 |         version: expect.any(Number),
 55 |         defaults: {
 56 |           name: expect.any(String)
 57 |         },
 58 |         inputs: ['main'],
 59 |         outputs: ['main'],
 60 |         properties: expect.any(Array),
 61 |         credentials: []
 62 |       });
 63 |     });
 64 |     
 65 |     it('should create a webhook node', () => {
 66 |       const webhook = webhookNodeFactory.build();
 67 |       
 68 |       expect(webhook).toMatchObject({
 69 |         name: 'webhook',
 70 |         displayName: 'Webhook',
 71 |         description: 'Starts the workflow when a webhook is called',
 72 |         group: ['trigger'],
 73 |         properties: expect.arrayContaining([
 74 |           expect.objectContaining({
 75 |             name: 'path',
 76 |             type: 'string',
 77 |             required: true
 78 |           }),
 79 |           expect.objectContaining({
 80 |             name: 'method',
 81 |             type: 'options'
 82 |           })
 83 |         ])
 84 |       });
 85 |     });
 86 |     
 87 |     it('should create a slack node', () => {
 88 |       const slack = slackNodeFactory.build();
 89 |       
 90 |       expect(slack).toMatchObject({
 91 |         name: 'slack',
 92 |         displayName: 'Slack',
 93 |         description: 'Send messages to Slack',
 94 |         group: ['output'],
 95 |         credentials: [
 96 |           {
 97 |             name: 'slackApi',
 98 |             required: true
 99 |           }
100 |         ],
101 |         properties: expect.arrayContaining([
102 |           expect.objectContaining({
103 |             name: 'resource',
104 |             type: 'options'
105 |           }),
106 |           expect.objectContaining({
107 |             name: 'operation',
108 |             type: 'options',
109 |             displayOptions: {
110 |               show: {
111 |                 resource: ['message']
112 |               }
113 |             }
114 |           })
115 |         ])
116 |       });
117 |     });
118 |     
119 |     it('should allow overriding factory defaults', () => {
120 |       const customNode = nodeFactory.build({
121 |         name: 'custom-node',
122 |         displayName: 'Custom Node',
123 |         version: 2
124 |       });
125 |       
126 |       expect(customNode.name).toBe('custom-node');
127 |       expect(customNode.displayName).toBe('Custom Node');
128 |       expect(customNode.version).toBe(2);
129 |     });
130 |     
131 |     it('should create multiple unique nodes', () => {
132 |       const nodes = nodeFactory.buildList(5);
133 |       
134 |       expect(nodes).toHaveLength(5);
135 |       const names = nodes.map(n => n.name);
136 |       const uniqueNames = new Set(names);
137 |       expect(uniqueNames.size).toBe(5);
138 |     });
139 |   });
140 | });
```

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

```typescript
  1 | /**
  2 |  * Test User ID Persistence
  3 |  * Verifies that user IDs are consistent across sessions and modes
  4 |  */
  5 | 
  6 | import { TelemetryConfigManager } from '../src/telemetry/config-manager';
  7 | import { hostname, platform, arch, homedir } from 'os';
  8 | import { createHash } from 'crypto';
  9 | 
 10 | console.log('=== User ID Persistence Test ===\n');
 11 | 
 12 | // Test 1: Verify deterministic ID generation
 13 | console.log('Test 1: Deterministic ID Generation');
 14 | console.log('-----------------------------------');
 15 | 
 16 | const machineId = `${hostname()}-${platform()}-${arch()}-${homedir()}`;
 17 | const expectedUserId = createHash('sha256')
 18 |   .update(machineId)
 19 |   .digest('hex')
 20 |   .substring(0, 16);
 21 | 
 22 | console.log('Machine characteristics:');
 23 | console.log('  hostname:', hostname());
 24 | console.log('  platform:', platform());
 25 | console.log('  arch:', arch());
 26 | console.log('  homedir:', homedir());
 27 | console.log('\nGenerated machine ID:', machineId);
 28 | console.log('Expected user ID:', expectedUserId);
 29 | 
 30 | // Test 2: Load actual config
 31 | console.log('\n\nTest 2: Actual Config Manager');
 32 | console.log('-----------------------------------');
 33 | 
 34 | const configManager = TelemetryConfigManager.getInstance();
 35 | const actualUserId = configManager.getUserId();
 36 | const config = configManager.loadConfig();
 37 | 
 38 | console.log('Actual user ID:', actualUserId);
 39 | console.log('Config first run:', config.firstRun || 'Unknown');
 40 | console.log('Config version:', config.version || 'Unknown');
 41 | console.log('Telemetry enabled:', config.enabled);
 42 | 
 43 | // Test 3: Verify consistency
 44 | console.log('\n\nTest 3: Consistency Check');
 45 | console.log('-----------------------------------');
 46 | 
 47 | const match = actualUserId === expectedUserId;
 48 | console.log('User IDs match:', match ? '✓ YES' : '✗ NO');
 49 | 
 50 | if (!match) {
 51 |   console.log('WARNING: User ID mismatch detected!');
 52 |   console.log('This could indicate an implementation issue.');
 53 | }
 54 | 
 55 | // Test 4: Multiple loads (simulate multiple sessions)
 56 | console.log('\n\nTest 4: Multiple Session Simulation');
 57 | console.log('-----------------------------------');
 58 | 
 59 | const userId1 = configManager.getUserId();
 60 | const userId2 = TelemetryConfigManager.getInstance().getUserId();
 61 | const userId3 = configManager.getUserId();
 62 | 
 63 | console.log('Session 1 user ID:', userId1);
 64 | console.log('Session 2 user ID:', userId2);
 65 | console.log('Session 3 user ID:', userId3);
 66 | 
 67 | const consistent = userId1 === userId2 && userId2 === userId3;
 68 | console.log('All sessions consistent:', consistent ? '✓ YES' : '✗ NO');
 69 | 
 70 | // Test 5: Docker environment simulation
 71 | console.log('\n\nTest 5: Docker Environment Check');
 72 | console.log('-----------------------------------');
 73 | 
 74 | const isDocker = process.env.IS_DOCKER === 'true';
 75 | console.log('Running in Docker:', isDocker);
 76 | 
 77 | if (isDocker) {
 78 |   console.log('\n⚠️  DOCKER MODE DETECTED');
 79 |   console.log('In Docker, user IDs may change across container recreations because:');
 80 |   console.log('  1. Container hostname changes each time');
 81 |   console.log('  2. Config file is not persisted (no volume mount)');
 82 |   console.log('  3. Each container gets a new ephemeral filesystem');
 83 |   console.log('\nRecommendation: Mount ~/.n8n-mcp as a volume for persistent user IDs');
 84 | }
 85 | 
 86 | // Test 6: Environment variable override check
 87 | console.log('\n\nTest 6: Environment Variable Override');
 88 | console.log('-----------------------------------');
 89 | 
 90 | const telemetryDisabledVars = [
 91 |   'N8N_MCP_TELEMETRY_DISABLED',
 92 |   'TELEMETRY_DISABLED',
 93 |   'DISABLE_TELEMETRY'
 94 | ];
 95 | 
 96 | telemetryDisabledVars.forEach(varName => {
 97 |   const value = process.env[varName];
 98 |   if (value !== undefined) {
 99 |     console.log(`${varName}:`, value);
100 |   }
101 | });
102 | 
103 | console.log('\nTelemetry status:', configManager.isEnabled() ? 'ENABLED' : 'DISABLED');
104 | 
105 | // Summary
106 | console.log('\n\n=== SUMMARY ===');
107 | console.log('User ID:', actualUserId);
108 | console.log('Deterministic:', match ? 'YES ✓' : 'NO ✗');
109 | console.log('Persistent across sessions:', consistent ? 'YES ✓' : 'NO ✗');
110 | console.log('Telemetry enabled:', config.enabled ? 'YES' : 'NO');
111 | console.log('Docker mode:', isDocker ? 'YES' : 'NO');
112 | 
113 | if (isDocker && !process.env.N8N_MCP_CONFIG_VOLUME) {
114 |   console.log('\n⚠️  WARNING: Running in Docker without persistent volume!');
115 |   console.log('User IDs will change on container recreation.');
116 |   console.log('Mount /home/nodejs/.n8n-mcp to persist telemetry config.');
117 | }
118 | 
119 | console.log('\n');
120 | 
```

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

```typescript
  1 | /**
  2 |  * Startup Checkpoint System
  3 |  * Defines checkpoints throughout the server initialization process
  4 |  * to identify where failures occur
  5 |  */
  6 | 
  7 | /**
  8 |  * Startup checkpoint constants
  9 |  * These checkpoints mark key stages in the server initialization process
 10 |  */
 11 | export const STARTUP_CHECKPOINTS = {
 12 |   /** Process has started, very first checkpoint */
 13 |   PROCESS_STARTED: 'process_started',
 14 | 
 15 |   /** About to connect to database */
 16 |   DATABASE_CONNECTING: 'database_connecting',
 17 | 
 18 |   /** Database connection successful */
 19 |   DATABASE_CONNECTED: 'database_connected',
 20 | 
 21 |   /** About to check n8n API configuration (if applicable) */
 22 |   N8N_API_CHECKING: 'n8n_api_checking',
 23 | 
 24 |   /** n8n API is configured and ready (if applicable) */
 25 |   N8N_API_READY: 'n8n_api_ready',
 26 | 
 27 |   /** About to initialize telemetry system */
 28 |   TELEMETRY_INITIALIZING: 'telemetry_initializing',
 29 | 
 30 |   /** Telemetry system is ready */
 31 |   TELEMETRY_READY: 'telemetry_ready',
 32 | 
 33 |   /** About to start MCP handshake */
 34 |   MCP_HANDSHAKE_STARTING: 'mcp_handshake_starting',
 35 | 
 36 |   /** MCP handshake completed successfully */
 37 |   MCP_HANDSHAKE_COMPLETE: 'mcp_handshake_complete',
 38 | 
 39 |   /** Server is fully ready to handle requests */
 40 |   SERVER_READY: 'server_ready',
 41 | } as const;
 42 | 
 43 | /**
 44 |  * Type for checkpoint names
 45 |  */
 46 | export type StartupCheckpoint = typeof STARTUP_CHECKPOINTS[keyof typeof STARTUP_CHECKPOINTS];
 47 | 
 48 | /**
 49 |  * Checkpoint data structure
 50 |  */
 51 | export interface CheckpointData {
 52 |   name: StartupCheckpoint;
 53 |   timestamp: number;
 54 |   success: boolean;
 55 |   error?: string;
 56 | }
 57 | 
 58 | /**
 59 |  * Get all checkpoint names in order
 60 |  */
 61 | export function getAllCheckpoints(): StartupCheckpoint[] {
 62 |   return Object.values(STARTUP_CHECKPOINTS);
 63 | }
 64 | 
 65 | /**
 66 |  * Find which checkpoint failed based on the list of passed checkpoints
 67 |  * Returns the first checkpoint that was not passed
 68 |  */
 69 | export function findFailedCheckpoint(passedCheckpoints: string[]): StartupCheckpoint {
 70 |   const allCheckpoints = getAllCheckpoints();
 71 | 
 72 |   for (const checkpoint of allCheckpoints) {
 73 |     if (!passedCheckpoints.includes(checkpoint)) {
 74 |       return checkpoint;
 75 |     }
 76 |   }
 77 | 
 78 |   // If all checkpoints were passed, the failure must have occurred after SERVER_READY
 79 |   // This would be an unexpected post-initialization failure
 80 |   return STARTUP_CHECKPOINTS.SERVER_READY;
 81 | }
 82 | 
 83 | /**
 84 |  * Validate if a string is a valid checkpoint
 85 |  */
 86 | export function isValidCheckpoint(checkpoint: string): checkpoint is StartupCheckpoint {
 87 |   return getAllCheckpoints().includes(checkpoint as StartupCheckpoint);
 88 | }
 89 | 
 90 | /**
 91 |  * Get human-readable description for a checkpoint
 92 |  */
 93 | export function getCheckpointDescription(checkpoint: StartupCheckpoint): string {
 94 |   const descriptions: Record<StartupCheckpoint, string> = {
 95 |     [STARTUP_CHECKPOINTS.PROCESS_STARTED]: 'Process initialization started',
 96 |     [STARTUP_CHECKPOINTS.DATABASE_CONNECTING]: 'Connecting to database',
 97 |     [STARTUP_CHECKPOINTS.DATABASE_CONNECTED]: 'Database connection established',
 98 |     [STARTUP_CHECKPOINTS.N8N_API_CHECKING]: 'Checking n8n API configuration',
 99 |     [STARTUP_CHECKPOINTS.N8N_API_READY]: 'n8n API ready',
100 |     [STARTUP_CHECKPOINTS.TELEMETRY_INITIALIZING]: 'Initializing telemetry system',
101 |     [STARTUP_CHECKPOINTS.TELEMETRY_READY]: 'Telemetry system ready',
102 |     [STARTUP_CHECKPOINTS.MCP_HANDSHAKE_STARTING]: 'Starting MCP protocol handshake',
103 |     [STARTUP_CHECKPOINTS.MCP_HANDSHAKE_COMPLETE]: 'MCP handshake completed',
104 |     [STARTUP_CHECKPOINTS.SERVER_READY]: 'Server fully initialized and ready',
105 |   };
106 | 
107 |   return descriptions[checkpoint] || 'Unknown checkpoint';
108 | }
109 | 
110 | /**
111 |  * Get the next expected checkpoint after the given one
112 |  * Returns null if this is the last checkpoint
113 |  */
114 | export function getNextCheckpoint(current: StartupCheckpoint): StartupCheckpoint | null {
115 |   const allCheckpoints = getAllCheckpoints();
116 |   const currentIndex = allCheckpoints.indexOf(current);
117 | 
118 |   if (currentIndex === -1 || currentIndex === allCheckpoints.length - 1) {
119 |     return null;
120 |   }
121 | 
122 |   return allCheckpoints[currentIndex + 1];
123 | }
124 | 
125 | /**
126 |  * Calculate completion percentage based on checkpoints passed
127 |  */
128 | export function getCompletionPercentage(passedCheckpoints: string[]): number {
129 |   const totalCheckpoints = getAllCheckpoints().length;
130 |   const passedCount = passedCheckpoints.length;
131 | 
132 |   return Math.round((passedCount / totalCheckpoints) * 100);
133 | }
134 | 
```

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

```typescript
  1 | import { describe, it, expect, vi, beforeEach } from 'vitest';
  2 | import { ExpressionValidator } from '@/services/expression-validator';
  3 | 
  4 | describe('ExpressionValidator', () => {
  5 |   const defaultContext = {
  6 |     availableNodes: [],
  7 |     currentNodeName: 'TestNode',
  8 |     isInLoop: false,
  9 |     hasInputData: true
 10 |   };
 11 | 
 12 |   beforeEach(() => {
 13 |     vi.clearAllMocks();
 14 |   });
 15 | 
 16 |   describe('validateExpression', () => {
 17 |     it('should be a static method that validates expressions', () => {
 18 |       expect(typeof ExpressionValidator.validateExpression).toBe('function');
 19 |     });
 20 | 
 21 |     it('should return a validation result', () => {
 22 |       const result = ExpressionValidator.validateExpression('{{ $json.field }}', defaultContext);
 23 |       
 24 |       expect(result).toHaveProperty('valid');
 25 |       expect(result).toHaveProperty('errors');
 26 |       expect(result).toHaveProperty('warnings');
 27 |       expect(result).toHaveProperty('usedVariables');
 28 |       expect(result).toHaveProperty('usedNodes');
 29 |     });
 30 | 
 31 |     it('should validate expressions with proper syntax', () => {
 32 |       const validExpr = '{{ $json.field }}';
 33 |       const result = ExpressionValidator.validateExpression(validExpr, defaultContext);
 34 |       
 35 |       expect(result).toBeDefined();
 36 |       expect(Array.isArray(result.errors)).toBe(true);
 37 |     });
 38 | 
 39 |     it('should detect malformed expressions', () => {
 40 |       const invalidExpr = '{{ $json.field'; // Missing closing braces
 41 |       const result = ExpressionValidator.validateExpression(invalidExpr, defaultContext);
 42 |       
 43 |       expect(result.errors.length).toBeGreaterThan(0);
 44 |     });
 45 |   });
 46 | 
 47 |   describe('validateNodeExpressions', () => {
 48 |     it('should validate all expressions in node parameters', () => {
 49 |       const parameters = {
 50 |         field1: '{{ $json.data }}',
 51 |         nested: {
 52 |           field2: 'regular text',
 53 |           field3: '{{ $node["Webhook"].json }}'
 54 |         }
 55 |       };
 56 | 
 57 |       const result = ExpressionValidator.validateNodeExpressions(parameters, defaultContext);
 58 | 
 59 |       expect(result).toHaveProperty('valid');
 60 |       expect(result).toHaveProperty('errors');
 61 |       expect(result).toHaveProperty('warnings');
 62 |     });
 63 | 
 64 |     it('should collect errors from invalid expressions', () => {
 65 |       const parameters = {
 66 |         badExpr: '{{ $json.field', // Missing closing
 67 |         goodExpr: '{{ $json.field }}'
 68 |       };
 69 | 
 70 |       const result = ExpressionValidator.validateNodeExpressions(parameters, defaultContext);
 71 | 
 72 |       expect(result.errors.length).toBeGreaterThan(0);
 73 |     });
 74 |   });
 75 | 
 76 |   describe('expression patterns', () => {
 77 |     it('should recognize n8n variable patterns', () => {
 78 |       const expressions = [
 79 |         '{{ $json }}',
 80 |         '{{ $json.field }}',
 81 |         '{{ $node["NodeName"].json }}',
 82 |         '{{ $workflow.id }}',
 83 |         '{{ $now }}',
 84 |         '{{ $itemIndex }}'
 85 |       ];
 86 | 
 87 |       expressions.forEach(expr => {
 88 |         const result = ExpressionValidator.validateExpression(expr, defaultContext);
 89 |         expect(result).toBeDefined();
 90 |       });
 91 |     });
 92 |   });
 93 | 
 94 |   describe('context validation', () => {
 95 |     it('should use available nodes from context', () => {
 96 |       const contextWithNodes = {
 97 |         ...defaultContext,
 98 |         availableNodes: ['Webhook', 'Function', 'Slack']
 99 |       };
100 | 
101 |       const expr = '{{ $node["Webhook"].json }}';
102 |       const result = ExpressionValidator.validateExpression(expr, contextWithNodes);
103 | 
104 |       expect(result.usedNodes.has('Webhook')).toBe(true);
105 |     });
106 |   });
107 | 
108 |   describe('edge cases', () => {
109 |     it('should handle empty expressions', () => {
110 |       const result = ExpressionValidator.validateExpression('{{ }}', defaultContext);
111 |       // The implementation might consider empty expressions as valid
112 |       expect(result).toBeDefined();
113 |       expect(Array.isArray(result.errors)).toBe(true);
114 |     });
115 | 
116 |     it('should handle non-expression text', () => {
117 |       const result = ExpressionValidator.validateExpression('regular text without expressions', defaultContext);
118 |       expect(result.valid).toBe(true);
119 |       expect(result.errors).toHaveLength(0);
120 |     });
121 | 
122 |     it('should handle nested expressions', () => {
123 |       const expr = '{{ $json[{{ $json.index }}] }}'; // Nested expressions not allowed
124 |       const result = ExpressionValidator.validateExpression(expr, defaultContext);
125 |       expect(result).toBeDefined();
126 |     });
127 |   });
128 | });
```

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

```typescript
  1 | import { INodeExecutionData, IDataObject } from 'n8n-workflow';
  2 | 
  3 | export class N8NMCPBridge {
  4 |   /**
  5 |    * Convert n8n workflow data to MCP tool arguments
  6 |    */
  7 |   static n8nToMCPToolArgs(data: IDataObject): any {
  8 |     // Handle different data formats from n8n
  9 |     if (data.json) {
 10 |       return data.json;
 11 |     }
 12 |     
 13 |     // Remove n8n-specific metadata
 14 |     const { pairedItem, ...cleanData } = data;
 15 |     return cleanData;
 16 |   }
 17 | 
 18 |   /**
 19 |    * Convert MCP tool response to n8n execution data
 20 |    */
 21 |   static mcpToN8NExecutionData(mcpResponse: any, itemIndex: number = 0): INodeExecutionData {
 22 |     // Handle MCP content array format
 23 |     if (mcpResponse.content && Array.isArray(mcpResponse.content)) {
 24 |       const textContent = mcpResponse.content
 25 |         .filter((c: any) => c.type === 'text')
 26 |         .map((c: any) => c.text)
 27 |         .join('\n');
 28 |       
 29 |       try {
 30 |         // Try to parse as JSON if possible
 31 |         const parsed = JSON.parse(textContent);
 32 |         return {
 33 |           json: parsed,
 34 |           pairedItem: itemIndex,
 35 |         };
 36 |       } catch {
 37 |         // Return as text if not JSON
 38 |         return {
 39 |           json: { result: textContent },
 40 |           pairedItem: itemIndex,
 41 |         };
 42 |       }
 43 |     }
 44 | 
 45 |     // Handle direct object response
 46 |     return {
 47 |       json: mcpResponse,
 48 |       pairedItem: itemIndex,
 49 |     };
 50 |   }
 51 | 
 52 |   /**
 53 |    * Convert n8n workflow definition to MCP-compatible format
 54 |    */
 55 |   static n8nWorkflowToMCP(workflow: any): any {
 56 |     return {
 57 |       id: workflow.id,
 58 |       name: workflow.name,
 59 |       description: workflow.description || '',
 60 |       nodes: workflow.nodes?.map((node: any) => ({
 61 |         id: node.id,
 62 |         type: node.type,
 63 |         name: node.name,
 64 |         parameters: node.parameters,
 65 |         position: node.position,
 66 |       })),
 67 |       connections: workflow.connections,
 68 |       settings: workflow.settings,
 69 |       metadata: {
 70 |         createdAt: workflow.createdAt,
 71 |         updatedAt: workflow.updatedAt,
 72 |         active: workflow.active,
 73 |       },
 74 |     };
 75 |   }
 76 | 
 77 |   /**
 78 |    * Convert MCP workflow format to n8n-compatible format
 79 |    */
 80 |   static mcpToN8NWorkflow(mcpWorkflow: any): any {
 81 |     return {
 82 |       name: mcpWorkflow.name,
 83 |       nodes: mcpWorkflow.nodes || [],
 84 |       connections: mcpWorkflow.connections || {},
 85 |       settings: mcpWorkflow.settings || {
 86 |         executionOrder: 'v1',
 87 |       },
 88 |       staticData: null,
 89 |       pinData: {},
 90 |     };
 91 |   }
 92 | 
 93 |   /**
 94 |    * Convert n8n execution data to MCP resource format
 95 |    */
 96 |   static n8nExecutionToMCPResource(execution: any): any {
 97 |     return {
 98 |       uri: `execution://${execution.id}`,
 99 |       name: `Execution ${execution.id}`,
100 |       description: `Workflow: ${execution.workflowData?.name || 'Unknown'}`,
101 |       mimeType: 'application/json',
102 |       data: {
103 |         id: execution.id,
104 |         workflowId: execution.workflowId,
105 |         status: execution.finished ? 'completed' : execution.stoppedAt ? 'stopped' : 'running',
106 |         mode: execution.mode,
107 |         startedAt: execution.startedAt,
108 |         stoppedAt: execution.stoppedAt,
109 |         error: execution.data?.resultData?.error,
110 |         executionData: execution.data,
111 |       },
112 |     };
113 |   }
114 | 
115 |   /**
116 |    * Convert MCP prompt arguments to n8n-compatible format
117 |    */
118 |   static mcpPromptArgsToN8N(promptArgs: any): IDataObject {
119 |     return {
120 |       prompt: promptArgs.name || '',
121 |       arguments: promptArgs.arguments || {},
122 |       messages: promptArgs.messages || [],
123 |     };
124 |   }
125 | 
126 |   /**
127 |    * Validate and sanitize data before conversion
128 |    */
129 |   static sanitizeData(data: any): any {
130 |     if (data === null || data === undefined) {
131 |       return {};
132 |     }
133 | 
134 |     if (typeof data !== 'object') {
135 |       return { value: data };
136 |     }
137 | 
138 |     // Remove circular references
139 |     const seen = new WeakSet();
140 |     return JSON.parse(JSON.stringify(data, (_key, value) => {
141 |       if (typeof value === 'object' && value !== null) {
142 |         if (seen.has(value)) {
143 |           return '[Circular]';
144 |         }
145 |         seen.add(value);
146 |       }
147 |       return value;
148 |     }));
149 |   }
150 | 
151 |   /**
152 |    * Extract error information for both n8n and MCP formats
153 |    */
154 |   static formatError(error: any): any {
155 |     return {
156 |       message: error.message || 'Unknown error',
157 |       type: error.name || 'Error',
158 |       stack: error.stack,
159 |       details: {
160 |         code: error.code,
161 |         statusCode: error.statusCode,
162 |         data: error.data,
163 |       },
164 |     };
165 |   }
166 | }
```

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

```typescript
  1 | /**
  2 |  * Integration Tests: handleDeleteWorkflow
  3 |  *
  4 |  * Tests workflow deletion against a real n8n instance.
  5 |  * Covers successful deletion, error handling, and cleanup verification.
  6 |  */
  7 | 
  8 | import { describe, it, expect, beforeEach, afterEach, afterAll } from 'vitest';
  9 | import { createTestContext, TestContext, createTestWorkflowName } from '../utils/test-context';
 10 | import { getTestN8nClient } from '../utils/n8n-client';
 11 | import { N8nApiClient } from '../../../../src/services/n8n-api-client';
 12 | import { SIMPLE_WEBHOOK_WORKFLOW } from '../utils/fixtures';
 13 | import { cleanupOrphanedWorkflows } from '../utils/cleanup-helpers';
 14 | import { createMcpContext } from '../utils/mcp-context';
 15 | import { InstanceContext } from '../../../../src/types/instance-context';
 16 | import { handleDeleteWorkflow } from '../../../../src/mcp/handlers-n8n-manager';
 17 | 
 18 | describe('Integration: handleDeleteWorkflow', () => {
 19 |   let context: TestContext;
 20 |   let client: N8nApiClient;
 21 |   let mcpContext: InstanceContext;
 22 | 
 23 |   beforeEach(() => {
 24 |     context = createTestContext();
 25 |     client = getTestN8nClient();
 26 |     mcpContext = createMcpContext();
 27 |   });
 28 | 
 29 |   afterEach(async () => {
 30 |     await context.cleanup();
 31 |   });
 32 | 
 33 |   afterAll(async () => {
 34 |     if (!process.env.CI) {
 35 |       await cleanupOrphanedWorkflows();
 36 |     }
 37 |   });
 38 | 
 39 |   // ======================================================================
 40 |   // Successful Deletion
 41 |   // ======================================================================
 42 | 
 43 |   describe('Successful Deletion', () => {
 44 |     it('should delete an existing workflow', async () => {
 45 |       // Create workflow
 46 |       const workflow = {
 47 |         ...SIMPLE_WEBHOOK_WORKFLOW,
 48 |         name: createTestWorkflowName('Delete - Success'),
 49 |         tags: ['mcp-integration-test']
 50 |       };
 51 | 
 52 |       const created = await client.createWorkflow(workflow);
 53 |       expect(created.id).toBeTruthy();
 54 |       if (!created.id) throw new Error('Workflow ID is missing');
 55 | 
 56 |       // Do NOT track workflow since we're testing deletion
 57 |       // context.trackWorkflow(created.id);
 58 | 
 59 |       // Delete using MCP handler
 60 |       const response = await handleDeleteWorkflow(
 61 |         { id: created.id },
 62 |         mcpContext
 63 |       );
 64 | 
 65 |       // Verify MCP response
 66 |       expect(response.success).toBe(true);
 67 |       expect(response.data).toBeDefined();
 68 | 
 69 |       // Verify workflow is actually deleted
 70 |       await expect(async () => {
 71 |         await client.getWorkflow(created.id!);
 72 |       }).rejects.toThrow();
 73 |     });
 74 |   });
 75 | 
 76 |   // ======================================================================
 77 |   // Error Handling
 78 |   // ======================================================================
 79 | 
 80 |   describe('Error Handling', () => {
 81 |     it('should return error for non-existent workflow ID', async () => {
 82 |       const response = await handleDeleteWorkflow(
 83 |         { id: '99999999' },
 84 |         mcpContext
 85 |       );
 86 | 
 87 |       expect(response.success).toBe(false);
 88 |       expect(response.error).toBeDefined();
 89 |     });
 90 |   });
 91 | 
 92 |   // ======================================================================
 93 |   // Cleanup Verification
 94 |   // ======================================================================
 95 | 
 96 |   describe('Cleanup Verification', () => {
 97 |     it('should verify workflow is actually deleted from n8n', async () => {
 98 |       // Create workflow
 99 |       const workflow = {
100 |         ...SIMPLE_WEBHOOK_WORKFLOW,
101 |         name: createTestWorkflowName('Delete - Cleanup Check'),
102 |         tags: ['mcp-integration-test']
103 |       };
104 | 
105 |       const created = await client.createWorkflow(workflow);
106 |       expect(created.id).toBeTruthy();
107 |       if (!created.id) throw new Error('Workflow ID is missing');
108 | 
109 |       // Verify workflow exists
110 |       const beforeDelete = await client.getWorkflow(created.id);
111 |       expect(beforeDelete.id).toBe(created.id);
112 | 
113 |       // Delete workflow
114 |       const deleteResponse = await handleDeleteWorkflow(
115 |         { id: created.id },
116 |         mcpContext
117 |       );
118 | 
119 |       expect(deleteResponse.success).toBe(true);
120 | 
121 |       // Verify workflow no longer exists
122 |       try {
123 |         await client.getWorkflow(created.id);
124 |         // If we reach here, workflow wasn't deleted
125 |         throw new Error('Workflow should have been deleted but still exists');
126 |       } catch (error: any) {
127 |         // Expected: workflow should not be found
128 |         expect(error.message).toMatch(/not found|404/i);
129 |       }
130 |     });
131 |   });
132 | });
133 | 
```

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

```json
  1 | {
  2 |   "nodes": [
  3 |     {
  4 |       "style": "programmatic",
  5 |       "nodeType": "nodes-base.httpRequest",
  6 |       "displayName": "HTTP Request",
  7 |       "description": "Makes HTTP requests and returns the response",
  8 |       "category": "Core Nodes",
  9 |       "properties": [
 10 |         {
 11 |           "name": "url",
 12 |           "displayName": "URL",
 13 |           "type": "string",
 14 |           "required": true,
 15 |           "default": ""
 16 |         },
 17 |         {
 18 |           "name": "method",
 19 |           "displayName": "Method",
 20 |           "type": "options",
 21 |           "options": [
 22 |             { "name": "GET", "value": "GET" },
 23 |             { "name": "POST", "value": "POST" },
 24 |             { "name": "PUT", "value": "PUT" },
 25 |             { "name": "DELETE", "value": "DELETE" }
 26 |           ],
 27 |           "default": "GET"
 28 |         }
 29 |       ],
 30 |       "credentials": [],
 31 |       "isAITool": true,
 32 |       "isTrigger": false,
 33 |       "isWebhook": false,
 34 |       "operations": [],
 35 |       "version": "1",
 36 |       "isVersioned": false,
 37 |       "packageName": "n8n-nodes-base",
 38 |       "documentation": "The HTTP Request node makes HTTP requests and returns the response data."
 39 |     },
 40 |     {
 41 |       "style": "programmatic",
 42 |       "nodeType": "nodes-base.webhook",
 43 |       "displayName": "Webhook",
 44 |       "description": "Receives data from external services via webhooks",
 45 |       "category": "Core Nodes",
 46 |       "properties": [
 47 |         {
 48 |           "name": "httpMethod",
 49 |           "displayName": "HTTP Method",
 50 |           "type": "options",
 51 |           "options": [
 52 |             { "name": "GET", "value": "GET" },
 53 |             { "name": "POST", "value": "POST" }
 54 |           ],
 55 |           "default": "POST"
 56 |         },
 57 |         {
 58 |           "name": "path",
 59 |           "displayName": "Path",
 60 |           "type": "string",
 61 |           "default": "webhook"
 62 |         }
 63 |       ],
 64 |       "credentials": [],
 65 |       "isAITool": false,
 66 |       "isTrigger": true,
 67 |       "isWebhook": true,
 68 |       "operations": [],
 69 |       "version": "1",
 70 |       "isVersioned": false,
 71 |       "packageName": "n8n-nodes-base",
 72 |       "documentation": "The Webhook node creates an endpoint to receive data from external services."
 73 |     },
 74 |     {
 75 |       "style": "declarative",
 76 |       "nodeType": "nodes-base.slack",
 77 |       "displayName": "Slack",
 78 |       "description": "Send messages and interact with Slack",
 79 |       "category": "Communication",
 80 |       "properties": [],
 81 |       "credentials": [
 82 |         {
 83 |           "name": "slackApi",
 84 |           "required": true
 85 |         }
 86 |       ],
 87 |       "isAITool": true,
 88 |       "isTrigger": false,
 89 |       "isWebhook": false,
 90 |       "operations": [
 91 |         {
 92 |           "name": "Message",
 93 |           "value": "message",
 94 |           "operations": [
 95 |             {
 96 |               "name": "Send",
 97 |               "value": "send",
 98 |               "description": "Send a message to a channel or user"
 99 |             }
100 |           ]
101 |         }
102 |       ],
103 |       "version": "2.1",
104 |       "isVersioned": true,
105 |       "packageName": "n8n-nodes-base",
106 |       "documentation": "The Slack node allows you to send messages and interact with Slack workspaces."
107 |     }
108 |   ],
109 |   "templates": [
110 |     {
111 |       "id": 1001,
112 |       "name": "HTTP to Webhook",
113 |       "description": "Fetch data from HTTP and send to webhook",
114 |       "workflow": {
115 |         "nodes": [
116 |           {
117 |             "id": "1",
118 |             "name": "HTTP Request",
119 |             "type": "n8n-nodes-base.httpRequest",
120 |             "position": [250, 300],
121 |             "parameters": {
122 |               "url": "https://api.example.com/data",
123 |               "method": "GET"
124 |             }
125 |           },
126 |           {
127 |             "id": "2",
128 |             "name": "Webhook",
129 |             "type": "n8n-nodes-base.webhook",
130 |             "position": [450, 300],
131 |             "parameters": {
132 |               "path": "data-webhook",
133 |               "httpMethod": "POST"
134 |             }
135 |           }
136 |         ],
137 |         "connections": {
138 |           "HTTP Request": {
139 |             "main": [[{ "node": "Webhook", "type": "main", "index": 0 }]]
140 |           }
141 |         }
142 |       },
143 |       "nodes": [
144 |         { "id": 1, "name": "HTTP Request", "icon": "http" },
145 |         { "id": 2, "name": "Webhook", "icon": "webhook" }
146 |       ],
147 |       "categories": ["Data Processing"],
148 |       "user": {
149 |         "id": 1,
150 |         "name": "Test User",
151 |         "username": "testuser",
152 |         "verified": false
153 |       },
154 |       "views": 150,
155 |       "createdAt": "2024-01-15T10:00:00Z",
156 |       "updatedAt": "2024-01-20T15:30:00Z",
157 |       "totalViews": 150
158 |     }
159 |   ]
160 | }
```

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

```typescript
 1 | import { ToolDocumentation } from '../types';
 2 | 
 3 | export const validateWorkflowDoc: ToolDocumentation = {
 4 |   name: 'validate_workflow',
 5 |   category: 'validation',
 6 |   essentials: {
 7 |     description: 'Full workflow validation: structure, connections, expressions, AI tools. Returns errors/warnings/fixes. Essential before deploy.',
 8 |     keyParameters: ['workflow', 'options'],
 9 |     example: 'validate_workflow({workflow: {nodes: [...], connections: {...}}})',
10 |     performance: 'Moderate (100-500ms)',
11 |     tips: [
12 |       'Always validate before n8n_create_workflow to catch errors early',
13 |       'Use options.profile="minimal" for quick checks during development',
14 |       'AI tool connections are automatically validated for proper node references',
15 |       'Detects operator structure issues (binary vs unary, singleValue requirements)'
16 |     ]
17 |   },
18 |   full: {
19 |     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.',
20 |     parameters: {
21 |       workflow: { 
22 |         type: 'object', 
23 |         required: true, 
24 |         description: 'The complete workflow JSON to validate. Must include nodes array and connections object.' 
25 |       },
26 |       options: { 
27 |         type: 'object', 
28 |         required: false, 
29 |         description: 'Validation options object' 
30 |       },
31 |       'options.validateNodes': { 
32 |         type: 'boolean', 
33 |         required: false, 
34 |         description: 'Validate individual node configurations. Default: true' 
35 |       },
36 |       'options.validateConnections': { 
37 |         type: 'boolean', 
38 |         required: false, 
39 |         description: 'Validate node connections and flow. Default: true' 
40 |       },
41 |       'options.validateExpressions': { 
42 |         type: 'boolean', 
43 |         required: false, 
44 |         description: 'Validate n8n expressions syntax and references. Default: true' 
45 |       },
46 |       'options.profile': { 
47 |         type: 'string', 
48 |         required: false, 
49 |         description: 'Validation profile for node validation: minimal, runtime (default), ai-friendly, strict' 
50 |       }
51 |     },
52 |     returns: 'Object with valid (boolean), errors (array), warnings (array), statistics (object), and suggestions (array)',
53 |     examples: [
54 |       'validate_workflow({workflow: myWorkflow}) - Full validation with default settings',
55 |       'validate_workflow({workflow: myWorkflow, options: {profile: "minimal"}}) - Quick validation for editing',
56 |       'validate_workflow({workflow: myWorkflow, options: {validateExpressions: false}}) - Skip expression validation'
57 |     ],
58 |     useCases: [
59 |       'Pre-deployment validation to catch all workflow issues',
60 |       'Quick validation during workflow development',
61 |       'Validate workflows with AI Agent nodes and tool connections',
62 |       'Check expression syntax before workflow execution',
63 |       'Ensure workflow structure integrity after modifications'
64 |     ],
65 |     performance: 'Moderate (100-500ms). Depends on workflow size and validation options. Expression validation adds ~50-100ms.',
66 |     bestPractices: [
67 |       'Always validate workflows before creating or updating in n8n',
68 |       'Use minimal profile during development, strict profile before production',
69 |       'Pay attention to warnings - they often indicate potential runtime issues',
70 |       'Validate after any workflow modifications, especially connection changes',
71 |       'Check statistics to understand workflow complexity',
72 |       '**Auto-sanitization runs during create/update**: Operator structures and missing metadata are automatically fixed when workflows are created or updated, but validation helps catch issues before they reach n8n',
73 |       'If validation detects operator issues, they will be auto-fixed during n8n_create_workflow or n8n_update_partial_workflow'
74 |     ],
75 |     pitfalls: [
76 |       'Large workflows (100+ nodes) may take longer to validate',
77 |       'Expression validation requires proper node references to exist',
78 |       'Some warnings may be acceptable depending on use case',
79 |       'Validation cannot catch all runtime errors (e.g., API failures)',
80 |       'Profile setting only affects node validation, not connection/expression checks'
81 |     ],
82 |     relatedTools: ['validate_node', 'n8n_create_workflow', 'n8n_update_partial_workflow', 'n8n_autofix_workflow']
83 |   }
84 | };
```

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

```typescript
  1 | /**
  2 |  * Integration Tests: handleHealthCheck
  3 |  *
  4 |  * Tests API health check against a real n8n instance.
  5 |  * Covers connectivity verification and feature availability.
  6 |  */
  7 | 
  8 | import { describe, it, expect, beforeEach } from 'vitest';
  9 | import { createMcpContext } from '../utils/mcp-context';
 10 | import { InstanceContext } from '../../../../src/types/instance-context';
 11 | import { handleHealthCheck } from '../../../../src/mcp/handlers-n8n-manager';
 12 | import { HealthCheckResponse } from '../utils/response-types';
 13 | 
 14 | describe('Integration: handleHealthCheck', () => {
 15 |   let mcpContext: InstanceContext;
 16 | 
 17 |   beforeEach(() => {
 18 |     mcpContext = createMcpContext();
 19 |   });
 20 | 
 21 |   // ======================================================================
 22 |   // Successful Health Check
 23 |   // ======================================================================
 24 | 
 25 |   describe('API Available', () => {
 26 |     it('should successfully check n8n API health', async () => {
 27 |       const response = await handleHealthCheck(mcpContext);
 28 | 
 29 |       expect(response.success).toBe(true);
 30 |       expect(response.data).toBeDefined();
 31 | 
 32 |       const data = response.data as HealthCheckResponse;
 33 | 
 34 |       // Verify required fields
 35 |       expect(data).toHaveProperty('status');
 36 |       expect(data).toHaveProperty('apiUrl');
 37 |       expect(data).toHaveProperty('mcpVersion');
 38 |       expect(data).toHaveProperty('versionCheck');
 39 |       expect(data).toHaveProperty('performance');
 40 |       expect(data).toHaveProperty('nextSteps');
 41 | 
 42 |       // Status should be a string (e.g., "ok", "healthy")
 43 |       if (data.status) {
 44 |         expect(typeof data.status).toBe('string');
 45 |       }
 46 | 
 47 |       // API URL should match configuration
 48 |       expect(data.apiUrl).toBeDefined();
 49 |       expect(typeof data.apiUrl).toBe('string');
 50 | 
 51 |       // MCP version should be defined
 52 |       expect(data.mcpVersion).toBeDefined();
 53 |       expect(typeof data.mcpVersion).toBe('string');
 54 | 
 55 |       // Version check should be present
 56 |       expect(data.versionCheck).toBeDefined();
 57 |       expect(data.versionCheck).toHaveProperty('current');
 58 |       expect(data.versionCheck).toHaveProperty('upToDate');
 59 |       expect(typeof data.versionCheck.upToDate).toBe('boolean');
 60 | 
 61 |       // Performance metrics should be present
 62 |       expect(data.performance).toBeDefined();
 63 |       expect(data.performance).toHaveProperty('responseTimeMs');
 64 |       expect(typeof data.performance.responseTimeMs).toBe('number');
 65 |       expect(data.performance.responseTimeMs).toBeGreaterThan(0);
 66 | 
 67 |       // Next steps should be present
 68 |       expect(data.nextSteps).toBeDefined();
 69 |       expect(Array.isArray(data.nextSteps)).toBe(true);
 70 |     });
 71 | 
 72 |     it('should include feature availability information', async () => {
 73 |       const response = await handleHealthCheck(mcpContext);
 74 | 
 75 |       expect(response.success).toBe(true);
 76 |       const data = response.data as HealthCheckResponse;
 77 | 
 78 |       // Check for feature information
 79 |       // Note: Features may vary by n8n instance configuration
 80 |       if (data.features) {
 81 |         expect(typeof data.features).toBe('object');
 82 |       }
 83 | 
 84 |       // Check for version information
 85 |       if (data.n8nVersion) {
 86 |         expect(typeof data.n8nVersion).toBe('string');
 87 |       }
 88 | 
 89 |       if (data.supportedN8nVersion) {
 90 |         expect(typeof data.supportedN8nVersion).toBe('string');
 91 |       }
 92 | 
 93 |       // Should include version note for AI agents
 94 |       if (data.versionNote) {
 95 |         expect(typeof data.versionNote).toBe('string');
 96 |         expect(data.versionNote).toContain('version');
 97 |       }
 98 |     });
 99 |   });
100 | 
101 |   // ======================================================================
102 |   // Response Format Verification
103 |   // ======================================================================
104 | 
105 |   describe('Response Format', () => {
106 |     it('should return complete health check response structure', async () => {
107 |       const response = await handleHealthCheck(mcpContext);
108 | 
109 |       expect(response.success).toBe(true);
110 |       expect(response.data).toBeDefined();
111 | 
112 |       const data = response.data as HealthCheckResponse;
113 | 
114 |       // Verify all expected fields are present
115 |       const expectedFields = ['status', 'apiUrl', 'mcpVersion'];
116 |       expectedFields.forEach(field => {
117 |         expect(data).toHaveProperty(field);
118 |       });
119 | 
120 |       // Optional fields that may be present
121 |       const optionalFields = ['instanceId', 'n8nVersion', 'features', 'supportedN8nVersion', 'versionNote'];
122 |       optionalFields.forEach(field => {
123 |         if (data[field] !== undefined) {
124 |           expect(data[field]).not.toBeNull();
125 |         }
126 |       });
127 |     });
128 |   });
129 | });
130 | 
```

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

```typescript
  1 | /**
  2 |  * Integration Tests: handleDeleteExecution
  3 |  *
  4 |  * Tests execution deletion against a real n8n instance.
  5 |  * Covers successful deletion, error handling, and cleanup verification.
  6 |  */
  7 | 
  8 | import { describe, it, expect, beforeEach, beforeAll } from 'vitest';
  9 | import { createMcpContext } from '../utils/mcp-context';
 10 | import { InstanceContext } from '../../../../src/types/instance-context';
 11 | import { handleDeleteExecution, handleTriggerWebhookWorkflow, handleGetExecution } from '../../../../src/mcp/handlers-n8n-manager';
 12 | import { getN8nCredentials } from '../utils/credentials';
 13 | 
 14 | describe('Integration: handleDeleteExecution', () => {
 15 |   let mcpContext: InstanceContext;
 16 |   let webhookUrl: string;
 17 | 
 18 |   beforeEach(() => {
 19 |     mcpContext = createMcpContext();
 20 |   });
 21 | 
 22 |   beforeAll(() => {
 23 |     const creds = getN8nCredentials();
 24 |     webhookUrl = creds.webhookUrls.get;
 25 |   });
 26 | 
 27 |   // ======================================================================
 28 |   // Successful Deletion
 29 |   // ======================================================================
 30 | 
 31 |   describe('Successful Deletion', () => {
 32 |     it('should delete an execution successfully', async () => {
 33 |       // First, create an execution to delete
 34 |       const triggerResponse = await handleTriggerWebhookWorkflow(
 35 |         {
 36 |           webhookUrl,
 37 |           httpMethod: 'GET',
 38 |           waitForResponse: true
 39 |         },
 40 |         mcpContext
 41 |       );
 42 | 
 43 |       // Try to extract execution ID
 44 |       let executionId: string | undefined;
 45 |       if (triggerResponse.success && triggerResponse.data) {
 46 |         const responseData = triggerResponse.data as any;
 47 |         executionId = responseData.executionId ||
 48 |                       responseData.id ||
 49 |                       responseData.execution?.id ||
 50 |                       responseData.workflowData?.executionId;
 51 |       }
 52 | 
 53 |       if (!executionId) {
 54 |         console.warn('Could not extract execution ID for deletion test');
 55 |         return;
 56 |       }
 57 | 
 58 |       // Delete the execution
 59 |       const response = await handleDeleteExecution(
 60 |         { id: executionId },
 61 |         mcpContext
 62 |       );
 63 | 
 64 |       expect(response.success).toBe(true);
 65 |       expect(response.data).toBeDefined();
 66 |     }, 30000);
 67 | 
 68 |     it('should verify execution is actually deleted', async () => {
 69 |       // Create an execution
 70 |       const triggerResponse = await handleTriggerWebhookWorkflow(
 71 |         {
 72 |           webhookUrl,
 73 |           httpMethod: 'GET',
 74 |           waitForResponse: true
 75 |         },
 76 |         mcpContext
 77 |       );
 78 | 
 79 |       let executionId: string | undefined;
 80 |       if (triggerResponse.success && triggerResponse.data) {
 81 |         const responseData = triggerResponse.data as any;
 82 |         executionId = responseData.executionId ||
 83 |                       responseData.id ||
 84 |                       responseData.execution?.id ||
 85 |                       responseData.workflowData?.executionId;
 86 |       }
 87 | 
 88 |       if (!executionId) {
 89 |         console.warn('Could not extract execution ID for deletion verification test');
 90 |         return;
 91 |       }
 92 | 
 93 |       // Delete it
 94 |       const deleteResponse = await handleDeleteExecution(
 95 |         { id: executionId },
 96 |         mcpContext
 97 |       );
 98 | 
 99 |       expect(deleteResponse.success).toBe(true);
100 | 
101 |       // Try to fetch the deleted execution
102 |       const getResponse = await handleGetExecution(
103 |         { id: executionId },
104 |         mcpContext
105 |       );
106 | 
107 |       // Should fail to find the deleted execution
108 |       expect(getResponse.success).toBe(false);
109 |       expect(getResponse.error).toBeDefined();
110 |     }, 30000);
111 |   });
112 | 
113 |   // ======================================================================
114 |   // Error Handling
115 |   // ======================================================================
116 | 
117 |   describe('Error Handling', () => {
118 |     it('should handle non-existent execution ID', async () => {
119 |       const response = await handleDeleteExecution(
120 |         { id: '99999999' },
121 |         mcpContext
122 |       );
123 | 
124 |       expect(response.success).toBe(false);
125 |       expect(response.error).toBeDefined();
126 |     });
127 | 
128 |     it('should handle invalid execution ID format', async () => {
129 |       const response = await handleDeleteExecution(
130 |         { id: 'invalid-id-format' },
131 |         mcpContext
132 |       );
133 | 
134 |       expect(response.success).toBe(false);
135 |       expect(response.error).toBeDefined();
136 |     });
137 | 
138 |     it('should handle missing execution ID', async () => {
139 |       const response = await handleDeleteExecution(
140 |         {} as any,
141 |         mcpContext
142 |       );
143 | 
144 |       expect(response.success).toBe(false);
145 |       expect(response.error).toBeDefined();
146 |     });
147 |   });
148 | });
149 | 
```

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

```typescript
 1 | import { ToolDocumentation } from '../types';
 2 | 
 3 | export const validateNodeDoc: ToolDocumentation = {
 4 |   name: 'validate_node',
 5 |   category: 'validation',
 6 |   essentials: {
 7 |     description: 'Validate n8n node configuration. Use mode="full" for comprehensive validation with errors/warnings/suggestions, mode="minimal" for quick required fields check.',
 8 |     keyParameters: ['nodeType', 'config', 'mode', 'profile'],
 9 |     example: 'validate_node({nodeType: "nodes-base.slack", config: {resource: "channel", operation: "create"}})',
10 |     performance: 'Fast (<100ms)',
11 |     tips: [
12 |       'Always call get_node({detail:"standard"}) first to see required fields',
13 |       'Use mode="minimal" for quick checks during development',
14 |       'Use mode="full" with profile="strict" before production deployment',
15 |       'Includes automatic structure validation for filter, resourceMapper, etc.'
16 |     ]
17 |   },
18 |   full: {
19 |     description: `**Validation Modes:**
20 | - full (default): Comprehensive validation with errors, warnings, suggestions, and automatic structure validation
21 | - minimal: Quick check for required fields only - fast but less thorough
22 | 
23 | **Validation Profiles (for mode="full"):**
24 | - minimal: Very lenient, basic checks only
25 | - runtime: Standard validation (default)
26 | - ai-friendly: Balanced for AI agent workflows
27 | - strict: Most thorough, recommended for production
28 | 
29 | **Automatic Structure Validation:**
30 | Validates complex n8n types automatically:
31 | - filter (FilterValue): 40+ operations (equals, contains, regex, etc.)
32 | - resourceMapper (ResourceMapperValue): Data mapping configuration
33 | - assignmentCollection (AssignmentCollectionValue): Variable assignments
34 | - resourceLocator (INodeParameterResourceLocator): Resource selection modes`,
35 |     parameters: {
36 |       nodeType: { type: 'string', required: true, description: 'Node type with prefix: "nodes-base.slack"' },
37 |       config: { type: 'object', required: true, description: 'Configuration object to validate. Use {} for empty config' },
38 |       mode: { type: 'string', required: false, description: 'Validation mode: "full" (default) or "minimal"' },
39 |       profile: { type: 'string', required: false, description: 'Validation profile for mode=full: "minimal", "runtime" (default), "ai-friendly", "strict"' }
40 |     },
41 |     returns: `Object containing:
42 | - nodeType: The validated node type
43 | - workflowNodeType: Type to use in workflow JSON
44 | - displayName: Human-readable node name
45 | - valid: Boolean indicating if configuration is valid
46 | - errors: Array of error objects with type, property, message, fix
47 | - warnings: Array of warning objects with suggestions
48 | - suggestions: Array of improvement suggestions
49 | - missingRequiredFields: (mode=minimal only) Array of missing required field names
50 | - summary: Object with hasErrors, errorCount, warningCount, suggestionCount`,
51 |     examples: [
52 |       '// Full validation with default profile\nvalidate_node({nodeType: "nodes-base.slack", config: {resource: "channel", operation: "create"}})',
53 |       '// Quick required fields check\nvalidate_node({nodeType: "nodes-base.webhook", config: {}, mode: "minimal"})',
54 |       '// Strict validation for production\nvalidate_node({nodeType: "nodes-base.httpRequest", config: {...}, mode: "full", profile: "strict"})',
55 |       '// Validate IF node with filter\nvalidate_node({nodeType: "nodes-base.if", config: {conditions: {combinator: "and", conditions: [...]}}})'
56 |     ],
57 |     useCases: [
58 |       'Validate node configuration before adding to workflow',
59 |       'Quick check for required fields during development',
60 |       'Pre-production validation with strict profile',
61 |       'Validate complex structures (filters, resource mappers)',
62 |       'Get suggestions for improving node configuration'
63 |     ],
64 |     performance: 'Fast validation: <50ms for minimal mode, <100ms for full mode. Structure validation adds minimal overhead.',
65 |     bestPractices: [
66 |       'Always call get_node() first to understand required fields',
67 |       'Use mode="minimal" for rapid iteration during development',
68 |       'Use profile="strict" before deploying to production',
69 |       'Pay attention to warnings - they often prevent runtime issues',
70 |       'Validate after any configuration changes'
71 |     ],
72 |     pitfalls: [
73 |       'Empty config {} is valid for some nodes (e.g., manual trigger)',
74 |       'mode="minimal" only checks required fields, not value validity',
75 |       'Some warnings may be acceptable for specific use cases',
76 |       'Credential validation requires runtime context'
77 |     ],
78 |     relatedTools: ['get_node', 'validate_workflow', 'n8n_autofix_workflow']
79 |   }
80 | };
81 | 
```

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

```typescript
  1 | #!/usr/bin/env node
  2 | /**
  3 |  * Copyright (c) 2024 AiAdvisors Romuald Czlonkowski
  4 |  * Licensed under the Sustainable Use License v1.0
  5 |  */
  6 | import { createDatabaseAdapter } from '../database/database-adapter';
  7 | 
  8 | interface NodeRow {
  9 |   node_type: string;
 10 |   package_name: string;
 11 |   display_name: string;
 12 |   description?: string;
 13 |   category?: string;
 14 |   development_style?: string;
 15 |   is_ai_tool: number;
 16 |   is_trigger: number;
 17 |   is_webhook: number;
 18 |   is_versioned: number;
 19 |   version?: string;
 20 |   documentation?: string;
 21 |   properties_schema?: string;
 22 |   operations?: string;
 23 |   credentials_required?: string;
 24 |   updated_at: string;
 25 | }
 26 | 
 27 | async function validate() {
 28 |   const db = await createDatabaseAdapter('./data/nodes.db');
 29 |   
 30 |   console.log('🔍 Validating critical nodes...\n');
 31 |   
 32 |   const criticalChecks = [
 33 |     { 
 34 |       type: 'nodes-base.httpRequest', 
 35 |       checks: {
 36 |         hasDocumentation: true,
 37 |         documentationContains: 'HTTP Request',
 38 |         style: 'programmatic'
 39 |       }
 40 |     },
 41 |     { 
 42 |       type: 'nodes-base.code', 
 43 |       checks: {
 44 |         hasDocumentation: true,
 45 |         documentationContains: 'Code'
 46 |       }
 47 |     },
 48 |     { 
 49 |       type: 'nodes-base.slack', 
 50 |       checks: {
 51 |         hasOperations: true,
 52 |         style: 'programmatic'
 53 |       }
 54 |     },
 55 |     {
 56 |       type: 'nodes-langchain.agent',
 57 |       checks: {
 58 |         isAITool: false, // According to the database, it's not marked as AI tool
 59 |         packageName: '@n8n/n8n-nodes-langchain'
 60 |       }
 61 |     }
 62 |   ];
 63 |   
 64 |   let passed = 0;
 65 |   let failed = 0;
 66 |   
 67 |   for (const check of criticalChecks) {
 68 |     const node = db.prepare('SELECT * FROM nodes WHERE node_type = ?').get(check.type) as NodeRow | undefined;
 69 |     
 70 |     if (!node) {
 71 |       console.log(`❌ ${check.type}: NOT FOUND`);
 72 |       failed++;
 73 |       continue;
 74 |     }
 75 |     
 76 |     let nodeOk = true;
 77 |     const issues: string[] = [];
 78 |     
 79 |     // Run checks
 80 |     if (check.checks.hasDocumentation && !node.documentation) {
 81 |       nodeOk = false;
 82 |       issues.push('missing documentation');
 83 |     }
 84 |     
 85 |     if (check.checks.documentationContains && 
 86 |         !node.documentation?.includes(check.checks.documentationContains)) {
 87 |       nodeOk = false;
 88 |       issues.push(`documentation doesn't contain "${check.checks.documentationContains}"`);
 89 |     }
 90 |     
 91 |     if (check.checks.style && node.development_style !== check.checks.style) {
 92 |       nodeOk = false;
 93 |       issues.push(`wrong style: ${node.development_style}`);
 94 |     }
 95 |     
 96 |     if (check.checks.hasOperations) {
 97 |       const operations = JSON.parse(node.operations || '[]');
 98 |       if (!operations.length) {
 99 |         nodeOk = false;
100 |         issues.push('no operations found');
101 |       }
102 |     }
103 |     
104 |     if (check.checks.isAITool !== undefined && !!node.is_ai_tool !== check.checks.isAITool) {
105 |       nodeOk = false;
106 |       issues.push(`AI tool flag mismatch: expected ${check.checks.isAITool}, got ${!!node.is_ai_tool}`);
107 |     }
108 |     
109 |     if ('isVersioned' in check.checks && check.checks.isVersioned && !node.is_versioned) {
110 |       nodeOk = false;
111 |       issues.push('not marked as versioned');
112 |     }
113 |     
114 |     if (check.checks.packageName && node.package_name !== check.checks.packageName) {
115 |       nodeOk = false;
116 |       issues.push(`wrong package: ${node.package_name}`);
117 |     }
118 |     
119 |     if (nodeOk) {
120 |       console.log(`✅ ${check.type}`);
121 |       passed++;
122 |     } else {
123 |       console.log(`❌ ${check.type}: ${issues.join(', ')}`);
124 |       failed++;
125 |     }
126 |   }
127 |   
128 |   console.log(`\n📊 Results: ${passed} passed, ${failed} failed`);
129 |   
130 |   // Additional statistics
131 |   const stats = db.prepare(`
132 |     SELECT 
133 |       COUNT(*) as total,
134 |       SUM(is_ai_tool) as ai_tools,
135 |       SUM(is_trigger) as triggers,
136 |       SUM(is_versioned) as versioned,
137 |       COUNT(DISTINCT package_name) as packages
138 |     FROM nodes
139 |   `).get() as any;
140 |   
141 |   console.log('\n📈 Database Statistics:');
142 |   console.log(`   Total nodes: ${stats.total}`);
143 |   console.log(`   AI tools: ${stats.ai_tools}`);
144 |   console.log(`   Triggers: ${stats.triggers}`);
145 |   console.log(`   Versioned: ${stats.versioned}`);
146 |   console.log(`   Packages: ${stats.packages}`);
147 |   
148 |   // Check documentation coverage
149 |   const docStats = db.prepare(`
150 |     SELECT 
151 |       COUNT(*) as total,
152 |       SUM(CASE WHEN documentation IS NOT NULL THEN 1 ELSE 0 END) as with_docs
153 |     FROM nodes
154 |   `).get() as any;
155 |   
156 |   console.log(`\n📚 Documentation Coverage:`);
157 |   console.log(`   Nodes with docs: ${docStats.with_docs}/${docStats.total} (${Math.round(docStats.with_docs / docStats.total * 100)}%)`);
158 |   
159 |   db.close();
160 |   process.exit(failed > 0 ? 1 : 0);
161 | }
162 | 
163 | if (require.main === module) {
164 |   validate().catch(console.error);
165 | }
```

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

```typescript
  1 | #!/usr/bin/env node
  2 | import { createDatabaseAdapter } from '../database/database-adapter';
  3 | import { TemplateRepository } from '../templates/template-repository';
  4 | import { TemplateFetcher } from '../templates/template-fetcher';
  5 | import * as fs from 'fs';
  6 | import * as path from 'path';
  7 | 
  8 | async function fetchTemplatesRobust() {
  9 |   console.log('🌐 Fetching n8n workflow templates (last year)...\n');
 10 |   
 11 |   // Ensure data directory exists
 12 |   const dataDir = './data';
 13 |   if (!fs.existsSync(dataDir)) {
 14 |     fs.mkdirSync(dataDir, { recursive: true });
 15 |   }
 16 |   
 17 |   // Initialize database
 18 |   const db = await createDatabaseAdapter('./data/nodes.db');
 19 |   
 20 |   // Drop existing templates table to ensure clean schema
 21 |   try {
 22 |     db.exec('DROP TABLE IF EXISTS templates');
 23 |     db.exec('DROP TABLE IF EXISTS templates_fts');
 24 |     console.log('🗑️  Dropped existing templates tables\n');
 25 |   } catch (error) {
 26 |     // Ignore errors if tables don't exist
 27 |   }
 28 |   
 29 |   // Apply schema with updated constraint
 30 |   const schema = fs.readFileSync(path.join(__dirname, '../../src/database/schema.sql'), 'utf8');
 31 |   db.exec(schema);
 32 |   
 33 |   // Create repository and fetcher
 34 |   const repository = new TemplateRepository(db);
 35 |   const fetcher = new TemplateFetcher();
 36 |   
 37 |   // Progress tracking
 38 |   let lastMessage = '';
 39 |   const startTime = Date.now();
 40 |   
 41 |   try {
 42 |     // Fetch template list
 43 |     console.log('📋 Phase 1: Fetching template list from n8n.io API\n');
 44 |     const templates = await fetcher.fetchTemplates((current, total) => {
 45 |       // Clear previous line
 46 |       if (lastMessage) {
 47 |         process.stdout.write('\r' + ' '.repeat(lastMessage.length) + '\r');
 48 |       }
 49 |       
 50 |       const progress = Math.round((current / total) * 100);
 51 |       lastMessage = `📊 Fetching template list: ${current}/${total} (${progress}%)`;
 52 |       process.stdout.write(lastMessage);
 53 |     });
 54 |     
 55 |     console.log('\n');
 56 |     console.log(`✅ Found ${templates.length} templates from last year\n`);
 57 |     
 58 |     // Fetch details and save incrementally
 59 |     console.log('📥 Phase 2: Fetching details and saving to database\n');
 60 |     let saved = 0;
 61 |     let errors = 0;
 62 |     
 63 |     for (let i = 0; i < templates.length; i++) {
 64 |       const template = templates[i];
 65 |       
 66 |       try {
 67 |         // Clear previous line
 68 |         if (lastMessage) {
 69 |           process.stdout.write('\r' + ' '.repeat(lastMessage.length) + '\r');
 70 |         }
 71 |         
 72 |         const progress = Math.round(((i + 1) / templates.length) * 100);
 73 |         lastMessage = `📊 Processing: ${i + 1}/${templates.length} (${progress}%) - Saved: ${saved}, Errors: ${errors}`;
 74 |         process.stdout.write(lastMessage);
 75 |         
 76 |         // Fetch detail
 77 |         const detail = await fetcher.fetchTemplateDetail(template.id);
 78 | 
 79 |         if (detail !== null) {
 80 |           // Save immediately
 81 |           repository.saveTemplate(template, detail);
 82 |           saved++;
 83 |         } else {
 84 |           errors++;
 85 |           console.error(`\n❌ Failed to fetch template ${template.id} (${template.name}) after retries`);
 86 |         }
 87 |         
 88 |         // Rate limiting
 89 |         await new Promise(resolve => setTimeout(resolve, 200));
 90 |       } catch (error: any) {
 91 |         errors++;
 92 |         console.error(`\n❌ Error processing template ${template.id} (${template.name}): ${error.message}`);
 93 |         // Continue with next template
 94 |       }
 95 |     }
 96 |     
 97 |     console.log('\n');
 98 |     
 99 |     // Get stats
100 |     const elapsed = Math.round((Date.now() - startTime) / 1000);
101 |     const stats = await repository.getTemplateStats();
102 |     
103 |     console.log('✅ Template fetch complete!\n');
104 |     console.log('📈 Statistics:');
105 |     console.log(`   - Templates found: ${templates.length}`);
106 |     console.log(`   - Templates saved: ${saved}`);
107 |     console.log(`   - Errors: ${errors}`);
108 |     console.log(`   - Success rate: ${Math.round((saved / templates.length) * 100)}%`);
109 |     console.log(`   - Time elapsed: ${elapsed} seconds`);
110 |     console.log(`   - Average time per template: ${(elapsed / saved).toFixed(2)} seconds`);
111 |     
112 |     if (stats.topUsedNodes && stats.topUsedNodes.length > 0) {
113 |       console.log('\n🔝 Top used nodes:');
114 |       stats.topUsedNodes.slice(0, 10).forEach((node: any, index: number) => {
115 |         console.log(`   ${index + 1}. ${node.node} (${node.count} templates)`);
116 |       });
117 |     }
118 |     
119 |   } catch (error) {
120 |     console.error('\n❌ Fatal error:', error);
121 |     process.exit(1);
122 |   }
123 |   
124 |   // Close database
125 |   if ('close' in db && typeof db.close === 'function') {
126 |     db.close();
127 |   }
128 | }
129 | 
130 | // Run if called directly
131 | if (require.main === module) {
132 |   fetchTemplatesRobust().catch(console.error);
133 | }
134 | 
135 | export { fetchTemplatesRobust };
```

--------------------------------------------------------------------------------
/src/scripts/test-telemetry-mutations-verbose.ts:
--------------------------------------------------------------------------------

```typescript
  1 | /**
  2 |  * Test telemetry mutations with enhanced logging
  3 |  * Verifies that mutations are properly tracked and persisted
  4 |  */
  5 | 
  6 | import { telemetry } from '../telemetry/telemetry-manager.js';
  7 | import { TelemetryConfigManager } from '../telemetry/config-manager.js';
  8 | import { logger } from '../utils/logger.js';
  9 | 
 10 | async function testMutations() {
 11 |   console.log('Starting verbose telemetry mutation test...\n');
 12 | 
 13 |   const configManager = TelemetryConfigManager.getInstance();
 14 |   console.log('Telemetry config is enabled:', configManager.isEnabled());
 15 |   console.log('Telemetry config file:', configManager['configPath']);
 16 | 
 17 |   // Test data with valid workflow structure
 18 |   const testMutation = {
 19 |     sessionId: 'test_session_' + Date.now(),
 20 |     toolName: 'n8n_update_partial_workflow',
 21 |     userIntent: 'Add a Merge node for data consolidation',
 22 |     operations: [
 23 |       {
 24 |         type: 'addNode',
 25 |         nodeId: 'Merge1',
 26 |         node: {
 27 |           id: 'Merge1',
 28 |           type: 'n8n-nodes-base.merge',
 29 |           name: 'Merge',
 30 |           position: [600, 200],
 31 |           parameters: {}
 32 |         }
 33 |       },
 34 |       {
 35 |         type: 'addConnection',
 36 |         source: 'previous_node',
 37 |         target: 'Merge1'
 38 |       }
 39 |     ],
 40 |     workflowBefore: {
 41 |       id: 'test-workflow',
 42 |       name: 'Test Workflow',
 43 |       active: true,
 44 |       nodes: [
 45 |         {
 46 |           id: 'previous_node',
 47 |           type: 'n8n-nodes-base.manualTrigger',
 48 |           name: 'When called',
 49 |           position: [300, 200],
 50 |           parameters: {}
 51 |         }
 52 |       ],
 53 |       connections: {},
 54 |       nodeIds: []
 55 |     },
 56 |     workflowAfter: {
 57 |       id: 'test-workflow',
 58 |       name: 'Test Workflow',
 59 |       active: true,
 60 |       nodes: [
 61 |         {
 62 |           id: 'previous_node',
 63 |           type: 'n8n-nodes-base.manualTrigger',
 64 |           name: 'When called',
 65 |           position: [300, 200],
 66 |           parameters: {}
 67 |         },
 68 |         {
 69 |           id: 'Merge1',
 70 |           type: 'n8n-nodes-base.merge',
 71 |           name: 'Merge',
 72 |           position: [600, 200],
 73 |           parameters: {}
 74 |         }
 75 |       ],
 76 |       connections: {
 77 |         'previous_node': [
 78 |           {
 79 |             node: 'Merge1',
 80 |             type: 'main',
 81 |             index: 0,
 82 |             source: 0,
 83 |             destination: 0
 84 |           }
 85 |         ]
 86 |       },
 87 |       nodeIds: []
 88 |     },
 89 |     mutationSuccess: true,
 90 |     durationMs: 125
 91 |   };
 92 | 
 93 |   console.log('\nTest Mutation Data:');
 94 |   console.log('==================');
 95 |   console.log(JSON.stringify({
 96 |     intent: testMutation.userIntent,
 97 |     tool: testMutation.toolName,
 98 |     operationCount: testMutation.operations.length,
 99 |     sessionId: testMutation.sessionId
100 |   }, null, 2));
101 |   console.log('\n');
102 | 
103 |   // Call trackWorkflowMutation
104 |   console.log('Calling telemetry.trackWorkflowMutation...');
105 |   try {
106 |     await telemetry.trackWorkflowMutation(testMutation);
107 |     console.log('✓ trackWorkflowMutation completed successfully\n');
108 |   } catch (error) {
109 |     console.error('✗ trackWorkflowMutation failed:', error);
110 |     console.error('\n');
111 |   }
112 | 
113 |   // Check queue size before flush
114 |   const metricsBeforeFlush = telemetry.getMetrics();
115 |   console.log('Metrics before flush:');
116 |   console.log('- mutationQueueSize:', metricsBeforeFlush.tracking.mutationQueueSize);
117 |   console.log('- eventsTracked:', metricsBeforeFlush.processing.eventsTracked);
118 |   console.log('- eventsFailed:', metricsBeforeFlush.processing.eventsFailed);
119 |   console.log('\n');
120 | 
121 |   // Flush telemetry with 10-second wait for Supabase
122 |   console.log('Flushing telemetry (waiting 10 seconds for Supabase)...');
123 |   try {
124 |     await telemetry.flush();
125 |     console.log('✓ Telemetry flush completed\n');
126 |   } catch (error) {
127 |     console.error('✗ Flush failed:', error);
128 |     console.error('\n');
129 |   }
130 | 
131 |   // Wait a bit for async operations
132 |   await new Promise(resolve => setTimeout(resolve, 2000));
133 | 
134 |   // Get final metrics
135 |   const metricsAfterFlush = telemetry.getMetrics();
136 |   console.log('Metrics after flush:');
137 |   console.log('- mutationQueueSize:', metricsAfterFlush.tracking.mutationQueueSize);
138 |   console.log('- eventsTracked:', metricsAfterFlush.processing.eventsTracked);
139 |   console.log('- eventsFailed:', metricsAfterFlush.processing.eventsFailed);
140 |   console.log('- batchesSent:', metricsAfterFlush.processing.batchesSent);
141 |   console.log('- batchesFailed:', metricsAfterFlush.processing.batchesFailed);
142 |   console.log('- circuitBreakerState:', metricsAfterFlush.processing.circuitBreakerState);
143 |   console.log('\n');
144 | 
145 |   console.log('Test completed. Check workflow_mutations table in Supabase.');
146 | }
147 | 
148 | testMutations().catch(error => {
149 |   console.error('Test failed:', error);
150 |   process.exit(1);
151 | });
152 | 
```

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

```typescript
  1 | import { promises as fs } from 'fs';
  2 | import path from 'path';
  3 | 
  4 | export class DocsMapper {
  5 |   private docsPath = path.join(process.cwd(), 'n8n-docs');
  6 |   
  7 |   // Known documentation mapping fixes
  8 |   private readonly KNOWN_FIXES: Record<string, string> = {
  9 |     'httpRequest': 'httprequest',
 10 |     'code': 'code',
 11 |     'webhook': 'webhook',
 12 |     'respondToWebhook': 'respondtowebhook',
 13 |     // With package prefix
 14 |     'n8n-nodes-base.httpRequest': 'httprequest',
 15 |     'n8n-nodes-base.code': 'code',
 16 |     'n8n-nodes-base.webhook': 'webhook',
 17 |     'n8n-nodes-base.respondToWebhook': 'respondtowebhook'
 18 |   };
 19 | 
 20 |   async fetchDocumentation(nodeType: string): Promise<string | null> {
 21 |     // Apply known fixes first
 22 |     const fixedType = this.KNOWN_FIXES[nodeType] || nodeType;
 23 |     
 24 |     // Extract node name
 25 |     const nodeName = fixedType.split('.').pop()?.toLowerCase();
 26 |     if (!nodeName) {
 27 |       console.log(`⚠️  Could not extract node name from: ${nodeType}`);
 28 |       return null;
 29 |     }
 30 |     
 31 |     console.log(`📄 Looking for docs for: ${nodeType} -> ${nodeName}`);
 32 |     
 33 |     // Try different documentation paths - both files and directories
 34 |     const possiblePaths = [
 35 |       // Direct file paths
 36 |       `docs/integrations/builtin/core-nodes/n8n-nodes-base.${nodeName}.md`,
 37 |       `docs/integrations/builtin/app-nodes/n8n-nodes-base.${nodeName}.md`,
 38 |       `docs/integrations/builtin/trigger-nodes/n8n-nodes-base.${nodeName}.md`,
 39 |       `docs/integrations/builtin/cluster-nodes/root-nodes/n8n-nodes-langchain.${nodeName}.md`,
 40 |       `docs/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.${nodeName}.md`,
 41 |       // Directory with index.md
 42 |       `docs/integrations/builtin/core-nodes/n8n-nodes-base.${nodeName}/index.md`,
 43 |       `docs/integrations/builtin/app-nodes/n8n-nodes-base.${nodeName}/index.md`,
 44 |       `docs/integrations/builtin/trigger-nodes/n8n-nodes-base.${nodeName}/index.md`,
 45 |       `docs/integrations/builtin/cluster-nodes/root-nodes/n8n-nodes-langchain.${nodeName}/index.md`,
 46 |       `docs/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.${nodeName}/index.md`
 47 |     ];
 48 |     
 49 |     // Try each path
 50 |     for (const relativePath of possiblePaths) {
 51 |       try {
 52 |         const fullPath = path.join(this.docsPath, relativePath);
 53 |         let content = await fs.readFile(fullPath, 'utf-8');
 54 |         console.log(`  ✓ Found docs at: ${relativePath}`);
 55 |         
 56 |         // Inject special guidance for loop nodes
 57 |         content = this.enhanceLoopNodeDocumentation(nodeType, content);
 58 |         
 59 |         return content;
 60 |       } catch (error) {
 61 |         // File doesn't exist, try next
 62 |         continue;
 63 |       }
 64 |     }
 65 |     
 66 |     console.log(`  ✗ No docs found for ${nodeName}`);
 67 |     return null;
 68 |   }
 69 | 
 70 |   private enhanceLoopNodeDocumentation(nodeType: string, content: string): string {
 71 |     // Add critical output index information for SplitInBatches
 72 |     if (nodeType.includes('splitInBatches')) {
 73 |       const outputGuidance = `
 74 | 
 75 | ## CRITICAL OUTPUT CONNECTION INFORMATION
 76 | 
 77 | **⚠️ OUTPUT INDICES ARE COUNTERINTUITIVE ⚠️**
 78 | 
 79 | The SplitInBatches node has TWO outputs with specific indices:
 80 | - **Output 0 (index 0) = "done"**: Receives final processed data when loop completes
 81 | - **Output 1 (index 1) = "loop"**: Receives current batch data during iteration
 82 | 
 83 | ### Correct Connection Pattern:
 84 | 1. Connect nodes that PROCESS items inside the loop to **Output 1 ("loop")**
 85 | 2. Connect nodes that run AFTER the loop completes to **Output 0 ("done")**
 86 | 3. The last processing node in the loop must connect back to the SplitInBatches node
 87 | 
 88 | ### Common Mistake:
 89 | AI assistants often connect these backwards because the logical flow (loop first, then done) doesn't match the technical indices (done=0, loop=1).
 90 | 
 91 | `;
 92 |       // Insert after the main description
 93 |       const insertPoint = content.indexOf('## When to use');
 94 |       if (insertPoint > -1) {
 95 |         content = content.slice(0, insertPoint) + outputGuidance + content.slice(insertPoint);
 96 |       } else {
 97 |         // Append if no good insertion point found
 98 |         content = outputGuidance + '\n' + content;
 99 |       }
100 |     }
101 | 
102 |     // Add guidance for IF node
103 |     if (nodeType.includes('.if')) {
104 |       const outputGuidance = `
105 | 
106 | ## Output Connection Information
107 | 
108 | The IF node has TWO outputs:
109 | - **Output 0 (index 0) = "true"**: Items that match the condition
110 | - **Output 1 (index 1) = "false"**: Items that do not match the condition
111 | 
112 | `;
113 |       const insertPoint = content.indexOf('## Node parameters');
114 |       if (insertPoint > -1) {
115 |         content = content.slice(0, insertPoint) + outputGuidance + content.slice(insertPoint);
116 |       }
117 |     }
118 | 
119 |     return content;
120 |   }
121 | }
```

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

```typescript
  1 | import { ToolDocumentation } from '../types';
  2 | 
  3 | export const n8nHealthCheckDoc: ToolDocumentation = {
  4 |   name: 'n8n_health_check',
  5 |   category: 'system',
  6 |   essentials: {
  7 |     description: 'Check n8n instance health, API connectivity, version status, and performance metrics',
  8 |     keyParameters: ['mode', 'verbose'],
  9 |     example: 'n8n_health_check({mode: "status"})',
 10 |     performance: 'Fast - single API call (~150-200ms median)',
 11 |     tips: [
 12 |       'Use before starting workflow operations to ensure n8n is responsive',
 13 |       'Automatically checks if n8n-mcp version is outdated',
 14 |       'Returns version info, performance metrics, and next-step recommendations',
 15 |       'New: Shows cache hit rate and response time for performance monitoring'
 16 |     ]
 17 |   },
 18 |   full: {
 19 |     description: `Performs a comprehensive health check of the configured n8n instance through its API.
 20 | 
 21 | This tool verifies:
 22 | - API endpoint accessibility and response time
 23 | - n8n instance version and build information
 24 | - Authentication status and permissions
 25 | - Available features and enterprise capabilities
 26 | - Database connectivity (as reported by n8n)
 27 | - Queue system status (if configured)
 28 | 
 29 | Health checks are crucial for:
 30 | - Monitoring n8n instance availability
 31 | - Detecting performance degradation
 32 | - Verifying API compatibility before operations
 33 | - Ensuring authentication is working correctly`,
 34 |     parameters: {
 35 |       mode: {
 36 |         type: 'string',
 37 |         required: false,
 38 |         description: 'Operation mode: "status" (default) for quick health check, "diagnostic" for detailed debug info including env vars and tool status',
 39 |         default: 'status',
 40 |         enum: ['status', 'diagnostic']
 41 |       },
 42 |       verbose: {
 43 |         type: 'boolean',
 44 |         required: false,
 45 |         description: 'Include extra details in diagnostic mode',
 46 |         default: false
 47 |       }
 48 |     },
 49 |     returns: `Health status object containing:
 50 | - status: Overall health status ('healthy', 'degraded', 'error')
 51 | - n8nVersion: n8n instance version information
 52 | - instanceId: Unique identifier for the n8n instance
 53 | - features: Object listing available features and their status
 54 | - mcpVersion: Current n8n-mcp version
 55 | - supportedN8nVersion: Recommended n8n version for compatibility
 56 | - versionCheck: Version status information
 57 |   - current: Current n8n-mcp version
 58 |   - latest: Latest available version from npm
 59 |   - upToDate: Boolean indicating if version is current
 60 |   - message: Formatted version status message
 61 |   - updateCommand: Command to update (if outdated)
 62 | - performance: Performance metrics
 63 |   - responseTimeMs: API response time in milliseconds
 64 |   - cacheHitRate: Cache efficiency percentage
 65 |   - cachedInstances: Number of cached API instances
 66 | - nextSteps: Recommended actions after health check
 67 | - updateWarning: Warning if version is outdated (if applicable)`,
 68 |     examples: [
 69 |       'n8n_health_check({}) - Complete health check with version and performance data',
 70 |       '// 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);',
 71 |       '// 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);'
 72 |     ],
 73 |     useCases: [
 74 |       'Pre-flight checks before workflow deployments',
 75 |       'Continuous monitoring of n8n instance health',
 76 |       'Troubleshooting connectivity or performance issues',
 77 |       'Verifying n8n version compatibility with workflows',
 78 |       'Detecting feature availability (enterprise features, queue mode, etc.)'
 79 |     ],
 80 |     performance: `Fast response expected:
 81 | - Single HTTP request to /health endpoint
 82 | - Typically responds in <100ms for healthy instances
 83 | - Timeout after 10 seconds indicates severe issues
 84 | - Minimal server load - safe for frequent polling`,
 85 |     bestPractices: [
 86 |       'Run health checks before batch operations or deployments',
 87 |       'Set up automated monitoring with regular health checks',
 88 |       'Log response times to detect performance trends',
 89 |       'Check version compatibility when deploying workflows',
 90 |       'Use health status to implement circuit breaker patterns'
 91 |     ],
 92 |     pitfalls: [
 93 |       'Requires N8N_API_URL and N8N_API_KEY to be configured',
 94 |       'Network issues may cause false negatives',
 95 |       'Does not check individual workflow health',
 96 |       'Health endpoint might be cached - not real-time for all metrics'
 97 |     ],
 98 |     relatedTools: ['n8n_list_workflows', 'n8n_validate_workflow', 'n8n_workflow_versions']
 99 |   }
100 | };
```

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

```typescript
  1 | #!/usr/bin/env node
  2 | 
  3 | /**
  4 |  * Test script for webhook path autofixer functionality
  5 |  */
  6 | 
  7 | import { NodeRepository } from '../database/node-repository';
  8 | import { createDatabaseAdapter } from '../database/database-adapter';
  9 | import { WorkflowAutoFixer } from '../services/workflow-auto-fixer';
 10 | import { WorkflowValidator } from '../services/workflow-validator';
 11 | import { EnhancedConfigValidator } from '../services/enhanced-config-validator';
 12 | import { Workflow } from '../types/n8n-api';
 13 | import { Logger } from '../utils/logger';
 14 | import { join } from 'path';
 15 | 
 16 | const logger = new Logger({ prefix: '[TestWebhookAutofix]' });
 17 | 
 18 | // Test workflow with webhook missing path
 19 | const testWorkflow: Workflow = {
 20 |   id: 'test_webhook_fix',
 21 |   name: 'Test Webhook Autofix',
 22 |   active: false,
 23 |   nodes: [
 24 |     {
 25 |       id: '1',
 26 |       name: 'Webhook',
 27 |       type: 'n8n-nodes-base.webhook',
 28 |       typeVersion: 2.1,
 29 |       position: [250, 300],
 30 |       parameters: {}, // Empty parameters - missing path
 31 |     },
 32 |     {
 33 |       id: '2',
 34 |       name: 'HTTP Request',
 35 |       type: 'n8n-nodes-base.httpRequest',
 36 |       typeVersion: 4.2,
 37 |       position: [450, 300],
 38 |       parameters: {
 39 |         url: 'https://api.example.com/data',
 40 |         method: 'GET'
 41 |       }
 42 |     }
 43 |   ],
 44 |   connections: {
 45 |     'Webhook': {
 46 |       main: [[{
 47 |         node: 'HTTP Request',
 48 |         type: 'main',
 49 |         index: 0
 50 |       }]]
 51 |     }
 52 |   },
 53 |   settings: {
 54 |     executionOrder: 'v1'
 55 |   },
 56 |   staticData: undefined
 57 | };
 58 | 
 59 | async function testWebhookAutofix() {
 60 |   logger.info('Testing webhook path autofixer...');
 61 | 
 62 |   // Initialize database and repository
 63 |   const dbPath = join(process.cwd(), 'data', 'nodes.db');
 64 |   const adapter = await createDatabaseAdapter(dbPath);
 65 |   const repository = new NodeRepository(adapter);
 66 | 
 67 |   // Create validators
 68 |   const validator = new WorkflowValidator(repository, EnhancedConfigValidator);
 69 |   const autoFixer = new WorkflowAutoFixer(repository);
 70 | 
 71 |   // Step 1: Validate workflow to identify issues
 72 |   logger.info('Step 1: Validating workflow to identify issues...');
 73 |   const validationResult = await validator.validateWorkflow(testWorkflow);
 74 | 
 75 |   console.log('\n📋 Validation Summary:');
 76 |   console.log(`- Valid: ${validationResult.valid}`);
 77 |   console.log(`- Errors: ${validationResult.errors.length}`);
 78 |   console.log(`- Warnings: ${validationResult.warnings.length}`);
 79 | 
 80 |   if (validationResult.errors.length > 0) {
 81 |     console.log('\n❌ Errors found:');
 82 |     validationResult.errors.forEach(error => {
 83 |       console.log(`  - [${error.nodeName || error.nodeId}] ${error.message}`);
 84 |     });
 85 |   }
 86 | 
 87 |   // Step 2: Generate fixes (preview mode)
 88 |   logger.info('\nStep 2: Generating fixes in preview mode...');
 89 | 
 90 |   const fixResult = await autoFixer.generateFixes(
 91 |     testWorkflow,
 92 |     validationResult,
 93 |     [], // No expression format issues to pass
 94 |     {
 95 |       applyFixes: false, // Preview mode
 96 |       fixTypes: ['webhook-missing-path'] // Only test webhook fixes
 97 |     }
 98 |   );
 99 | 
100 |   console.log('\n🔧 Fix Results:');
101 |   console.log(`- Summary: ${fixResult.summary}`);
102 |   console.log(`- Total fixes: ${fixResult.stats.total}`);
103 |   console.log(`- Webhook path fixes: ${fixResult.stats.byType['webhook-missing-path']}`);
104 | 
105 |   if (fixResult.fixes.length > 0) {
106 |     console.log('\n📝 Detailed Fixes:');
107 |     fixResult.fixes.forEach(fix => {
108 |       console.log(`  - Node: ${fix.node}`);
109 |       console.log(`    Field: ${fix.field}`);
110 |       console.log(`    Type: ${fix.type}`);
111 |       console.log(`    Before: ${fix.before || 'undefined'}`);
112 |       console.log(`    After: ${fix.after}`);
113 |       console.log(`    Confidence: ${fix.confidence}`);
114 |       console.log(`    Description: ${fix.description}`);
115 |     });
116 |   }
117 | 
118 |   if (fixResult.operations.length > 0) {
119 |     console.log('\n🔄 Operations to Apply:');
120 |     fixResult.operations.forEach(op => {
121 |       if (op.type === 'updateNode') {
122 |         console.log(`  - Update Node: ${op.nodeId}`);
123 |         console.log(`    Updates: ${JSON.stringify(op.updates, null, 2)}`);
124 |       }
125 |     });
126 |   }
127 | 
128 |   // Step 3: Verify UUID format
129 |   if (fixResult.fixes.length > 0) {
130 |     const webhookFix = fixResult.fixes.find(f => f.type === 'webhook-missing-path');
131 |     if (webhookFix) {
132 |       const uuid = webhookFix.after as string;
133 |       const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
134 |       const isValidUUID = uuidRegex.test(uuid);
135 | 
136 |       console.log('\n✅ UUID Validation:');
137 |       console.log(`  - Generated UUID: ${uuid}`);
138 |       console.log(`  - Valid format: ${isValidUUID ? 'Yes' : 'No'}`);
139 |     }
140 |   }
141 | 
142 |   logger.info('\n✨ Webhook autofix test completed successfully!');
143 | }
144 | 
145 | // Run test
146 | testWebhookAutofix().catch(error => {
147 |   logger.error('Test failed:', error);
148 |   process.exit(1);
149 | });
```

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

```typescript
 1 | import { ToolDocumentation } from '../types';
 2 | 
 3 | export const n8nExecutionsDoc: ToolDocumentation = {
 4 |   name: 'n8n_executions',
 5 |   category: 'workflow_management',
 6 |   essentials: {
 7 |     description: 'Manage workflow executions: get details, list, or delete. Unified tool for all execution operations.',
 8 |     keyParameters: ['action', 'id', 'workflowId', 'status'],
 9 |     example: 'n8n_executions({action: "list", workflowId: "abc123", status: "error"})',
10 |     performance: 'Fast (50-200ms)',
11 |     tips: [
12 |       'action="get": Get execution details by ID',
13 |       'action="list": List executions with filters',
14 |       'action="delete": Delete execution record',
15 |       'Use mode parameter for action=get to control detail level'
16 |     ]
17 |   },
18 |   full: {
19 |     description: `**Actions:**
20 | - get: Retrieve execution details by ID with configurable detail level
21 | - list: List executions with filtering and pagination
22 | - delete: Remove an execution record from history
23 | 
24 | **Detail Modes for action="get":**
25 | - preview: Structure only, no data
26 | - summary: 2 items per node (default)
27 | - filtered: Custom items limit, optionally filter by node names
28 | - full: All execution data (can be very large)`,
29 |     parameters: {
30 |       action: { type: 'string', required: true, description: 'Operation: "get", "list", or "delete"' },
31 |       id: { type: 'string', required: false, description: 'Execution ID (required for action=get or action=delete)' },
32 |       mode: { type: 'string', required: false, description: 'For action=get: "preview", "summary" (default), "filtered", "full"' },
33 |       nodeNames: { type: 'array', required: false, description: 'For action=get with mode=filtered: Filter to specific nodes by name' },
34 |       itemsLimit: { type: 'number', required: false, description: 'For action=get with mode=filtered: Items per node (0=structure, 2=default, -1=unlimited)' },
35 |       includeInputData: { type: 'boolean', required: false, description: 'For action=get: Include input data in addition to output (default: false)' },
36 |       workflowId: { type: 'string', required: false, description: 'For action=list: Filter by workflow ID' },
37 |       status: { type: 'string', required: false, description: 'For action=list: Filter by status ("success", "error", "waiting")' },
38 |       limit: { type: 'number', required: false, description: 'For action=list: Number of results (1-100, default: 100)' },
39 |       cursor: { type: 'string', required: false, description: 'For action=list: Pagination cursor from previous response' },
40 |       projectId: { type: 'string', required: false, description: 'For action=list: Filter by project ID (enterprise)' },
41 |       includeData: { type: 'boolean', required: false, description: 'For action=list: Include execution data (default: false)' }
42 |     },
43 |     returns: `Depends on action:
44 | - get: Execution object with data based on mode
45 | - list: { data: [...executions], nextCursor?: string }
46 | - delete: { success: boolean, message: string }`,
47 |     examples: [
48 |       '// List recent executions for a workflow\nn8n_executions({action: "list", workflowId: "abc123", limit: 10})',
49 |       '// List failed executions\nn8n_executions({action: "list", status: "error"})',
50 |       '// Get execution summary\nn8n_executions({action: "get", id: "exec_456"})',
51 |       '// Get full execution data\nn8n_executions({action: "get", id: "exec_456", mode: "full"})',
52 |       '// Get specific nodes from execution\nn8n_executions({action: "get", id: "exec_456", mode: "filtered", nodeNames: ["HTTP Request", "Slack"]})',
53 |       '// Delete an execution\nn8n_executions({action: "delete", id: "exec_456"})'
54 |     ],
55 |     useCases: [
56 |       'Debug workflow failures (get with mode=full)',
57 |       'Monitor workflow health (list with status filter)',
58 |       'Audit execution history',
59 |       'Clean up old execution records',
60 |       'Analyze specific node outputs'
61 |     ],
62 |     performance: `Response times:
63 | - list: 50-150ms depending on filters
64 | - get (preview/summary): 30-100ms
65 | - get (full): 100-500ms+ depending on data size
66 | - delete: 30-80ms`,
67 |     bestPractices: [
68 |       'Use mode="summary" (default) for debugging - shows enough data',
69 |       'Use mode="filtered" with nodeNames for large workflows',
70 |       'Filter by workflowId when listing to reduce results',
71 |       'Use cursor for pagination through large result sets',
72 |       'Delete old executions to save storage'
73 |     ],
74 |     pitfalls: [
75 |       'Requires N8N_API_URL and N8N_API_KEY configured',
76 |       'mode="full" can return very large responses for complex workflows',
77 |       'Execution must exist or returns 404',
78 |       'Delete is permanent - cannot undo'
79 |     ],
80 |     relatedTools: ['n8n_get_workflow', 'n8n_trigger_webhook_workflow', 'n8n_validate_workflow']
81 |   }
82 | };
83 | 
```

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

```typescript
  1 | #!/usr/bin/env npx tsx
  2 | 
  3 | /**
  4 |  * Test script to verify n8n_autofix_workflow documentation is properly integrated
  5 |  */
  6 | 
  7 | import { toolsDocumentation } from '../mcp/tool-docs';
  8 | import { getToolDocumentation } from '../mcp/tools-documentation';
  9 | import { Logger } from '../utils/logger';
 10 | 
 11 | const logger = new Logger({ prefix: '[AutofixDoc Test]' });
 12 | 
 13 | async function testAutofixDocumentation() {
 14 |   logger.info('Testing n8n_autofix_workflow documentation...\n');
 15 | 
 16 |   // Test 1: Check if documentation exists in the registry
 17 |   logger.info('Test 1: Checking documentation registry');
 18 |   const hasDoc = 'n8n_autofix_workflow' in toolsDocumentation;
 19 |   if (hasDoc) {
 20 |     logger.info('✅ Documentation found in registry');
 21 |   } else {
 22 |     logger.error('❌ Documentation NOT found in registry');
 23 |     logger.info('Available tools:', Object.keys(toolsDocumentation).filter(k => k.includes('autofix')));
 24 |   }
 25 | 
 26 |   // Test 2: Check documentation structure
 27 |   if (hasDoc) {
 28 |     logger.info('\nTest 2: Checking documentation structure');
 29 |     const doc = toolsDocumentation['n8n_autofix_workflow'];
 30 | 
 31 |     const hasEssentials = doc.essentials &&
 32 |                          doc.essentials.description &&
 33 |                          doc.essentials.keyParameters &&
 34 |                          doc.essentials.example;
 35 | 
 36 |     const hasFull = doc.full &&
 37 |                    doc.full.description &&
 38 |                    doc.full.parameters &&
 39 |                    doc.full.examples;
 40 | 
 41 |     if (hasEssentials) {
 42 |       logger.info('✅ Essentials documentation complete');
 43 |       logger.info(`  Description: ${doc.essentials.description.substring(0, 80)}...`);
 44 |       logger.info(`  Key params: ${doc.essentials.keyParameters.join(', ')}`);
 45 |     } else {
 46 |       logger.error('❌ Essentials documentation incomplete');
 47 |     }
 48 | 
 49 |     if (hasFull) {
 50 |       logger.info('✅ Full documentation complete');
 51 |       logger.info(`  Parameters: ${Object.keys(doc.full.parameters).join(', ')}`);
 52 |       logger.info(`  Examples: ${doc.full.examples.length} provided`);
 53 |     } else {
 54 |       logger.error('❌ Full documentation incomplete');
 55 |     }
 56 |   }
 57 | 
 58 |   // Test 3: Test getToolDocumentation function
 59 |   logger.info('\nTest 3: Testing getToolDocumentation function');
 60 | 
 61 |   try {
 62 |     const essentialsDoc = getToolDocumentation('n8n_autofix_workflow', 'essentials');
 63 |     if (essentialsDoc.includes("Tool 'n8n_autofix_workflow' not found")) {
 64 |       logger.error('❌ Essentials documentation retrieval failed');
 65 |     } else {
 66 |       logger.info('✅ Essentials documentation retrieved');
 67 |       const lines = essentialsDoc.split('\n').slice(0, 3);
 68 |       lines.forEach(line => logger.info(`  ${line}`));
 69 |     }
 70 |   } catch (error) {
 71 |     logger.error('❌ Error retrieving essentials documentation:', error);
 72 |   }
 73 | 
 74 |   try {
 75 |     const fullDoc = getToolDocumentation('n8n_autofix_workflow', 'full');
 76 |     if (fullDoc.includes("Tool 'n8n_autofix_workflow' not found")) {
 77 |       logger.error('❌ Full documentation retrieval failed');
 78 |     } else {
 79 |       logger.info('✅ Full documentation retrieved');
 80 |       const lines = fullDoc.split('\n').slice(0, 3);
 81 |       lines.forEach(line => logger.info(`  ${line}`));
 82 |     }
 83 |   } catch (error) {
 84 |     logger.error('❌ Error retrieving full documentation:', error);
 85 |   }
 86 | 
 87 |   // Test 4: Check if tool is listed in workflow management tools
 88 |   logger.info('\nTest 4: Checking workflow management tools listing');
 89 |   const workflowTools = Object.keys(toolsDocumentation).filter(k => k.startsWith('n8n_'));
 90 |   const hasAutofix = workflowTools.includes('n8n_autofix_workflow');
 91 | 
 92 |   if (hasAutofix) {
 93 |     logger.info('✅ n8n_autofix_workflow is listed in workflow management tools');
 94 |     logger.info(`  Total workflow tools: ${workflowTools.length}`);
 95 | 
 96 |     // Show related tools
 97 |     const relatedTools = workflowTools.filter(t =>
 98 |       t.includes('validate') || t.includes('update') || t.includes('fix')
 99 |     );
100 |     logger.info(`  Related tools: ${relatedTools.join(', ')}`);
101 |   } else {
102 |     logger.error('❌ n8n_autofix_workflow NOT listed in workflow management tools');
103 |   }
104 | 
105 |   // Summary
106 |   logger.info('\n' + '='.repeat(60));
107 |   logger.info('Summary:');
108 | 
109 |   if (hasDoc && hasAutofix) {
110 |     logger.info('✨ Documentation integration successful!');
111 |     logger.info('The n8n_autofix_workflow tool documentation is properly integrated.');
112 |     logger.info('\nTo use in MCP:');
113 |     logger.info('  - Essentials: tools_documentation({topic: "n8n_autofix_workflow"})');
114 |     logger.info('  - Full: tools_documentation({topic: "n8n_autofix_workflow", depth: "full"})');
115 |   } else {
116 |     logger.error('⚠️ Documentation integration incomplete');
117 |     logger.info('Please check the implementation and rebuild the project.');
118 |   }
119 | }
120 | 
121 | testAutofixDocumentation().catch(console.error);
```
Page 5/67FirstPrevNextLast