#
tokens: 46710/50000 18/252 files (page 3/8)
lines: off (toggle) GitHub
raw markdown copy
This is page 3 of 8. Use http://codebase.md/jakedismo/master-mcp-server?page={x} to view the full context.

# Directory Structure

```
├── .env.example
├── .eslintignore
├── .eslintrc.cjs
├── .eslintrc.js
├── .gitignore
├── .prettierignore
├── .prettierrc
├── .prettierrc.json
├── CHANGELOG.md
├── config
│   ├── default.json
│   ├── development.json
│   ├── production.json
│   └── schema.json
├── config.json
├── CONTRIBUTING.md
├── debug-stdio.cjs
├── debug-stdio.js
├── deploy
│   ├── cloudflare
│   │   ├── .gitkeep
│   │   ├── README.md
│   │   └── wrangler.toml
│   ├── docker
│   │   ├── .gitkeep
│   │   ├── docker-compose.yml
│   │   ├── Dockerfile
│   │   └── entrypoint.sh
│   ├── koyeb
│   │   ├── .gitkeep
│   │   └── koyeb.yaml
│   └── README.md
├── docker-compose.yml
├── Dockerfile
├── docs
│   ├── .DS_Store
│   ├── .vitepress
│   │   ├── cache
│   │   │   └── deps
│   │   │       ├── _metadata.json
│   │   │       ├── chunk-HVR2FF6M.js
│   │   │       ├── chunk-HVR2FF6M.js.map
│   │   │       ├── chunk-P2XGSYO7.js
│   │   │       ├── chunk-P2XGSYO7.js.map
│   │   │       ├── package.json
│   │   │       ├── vitepress___@vue_devtools-api.js
│   │   │       ├── vitepress___@vue_devtools-api.js.map
│   │   │       ├── vitepress___@vueuse_core.js
│   │   │       ├── vitepress___@vueuse_core.js.map
│   │   │       ├── vitepress___@vueuse_integrations_useFocusTrap.js
│   │   │       ├── vitepress___@vueuse_integrations_useFocusTrap.js.map
│   │   │       ├── vitepress___mark__js_src_vanilla__js.js
│   │   │       ├── vitepress___mark__js_src_vanilla__js.js.map
│   │   │       ├── vitepress___minisearch.js
│   │   │       ├── vitepress___minisearch.js.map
│   │   │       ├── vue.js
│   │   │       └── vue.js.map
│   │   ├── config.ts
│   │   ├── dist
│   │   │   ├── 404.html
│   │   │   ├── advanced
│   │   │   │   ├── extensibility.html
│   │   │   │   ├── index.html
│   │   │   │   ├── monitoring.html
│   │   │   │   ├── performance.html
│   │   │   │   └── security.html
│   │   │   ├── api
│   │   │   │   ├── index.html
│   │   │   │   └── README.html
│   │   │   ├── assets
│   │   │   │   ├── advanced_extensibility.md.TrXUn5w5.js
│   │   │   │   ├── advanced_extensibility.md.TrXUn5w5.lean.js
│   │   │   │   ├── advanced_index.md.CPcpUlw_.js
│   │   │   │   ├── advanced_index.md.CPcpUlw_.lean.js
│   │   │   │   ├── advanced_monitoring.md.DTybdNg-.js
│   │   │   │   ├── advanced_monitoring.md.DTybdNg-.lean.js
│   │   │   │   ├── advanced_performance.md.DKmzK0ia.js
│   │   │   │   ├── advanced_performance.md.DKmzK0ia.lean.js
│   │   │   │   ├── advanced_security.md.B-oBD7IB.js
│   │   │   │   ├── advanced_security.md.B-oBD7IB.lean.js
│   │   │   │   ├── api_index.md.Dl1JB08_.js
│   │   │   │   ├── api_index.md.Dl1JB08_.lean.js
│   │   │   │   ├── chunks
│   │   │   │   │   └── framework.CHl2ywxc.js
│   │   │   │   ├── configuration_environment-variables.md.Ddy3P_Wz.js
│   │   │   │   ├── configuration_environment-variables.md.Ddy3P_Wz.lean.js
│   │   │   │   ├── configuration_environment.md.DxcTQ623.js
│   │   │   │   ├── configuration_environment.md.DxcTQ623.lean.js
│   │   │   │   ├── configuration_overview.md.DIkVDv7V.js
│   │   │   │   ├── configuration_overview.md.DIkVDv7V.lean.js
│   │   │   │   ├── configuration_performance.md.DbJdmLrW.js
│   │   │   │   ├── configuration_performance.md.DbJdmLrW.lean.js
│   │   │   │   ├── configuration_reference.md.27IKWqtk.js
│   │   │   │   ├── configuration_reference.md.27IKWqtk.lean.js
│   │   │   │   ├── configuration_security.md.-OOlkzN4.js
│   │   │   │   ├── configuration_security.md.-OOlkzN4.lean.js
│   │   │   │   ├── contributing_dev-setup.md.Ceqh4w-R.js
│   │   │   │   ├── contributing_dev-setup.md.Ceqh4w-R.lean.js
│   │   │   │   ├── contributing_guidelines.md.ZEAX2yVh.js
│   │   │   │   ├── contributing_guidelines.md.ZEAX2yVh.lean.js
│   │   │   │   ├── contributing_index.md.DYq9R6wr.js
│   │   │   │   ├── contributing_index.md.DYq9R6wr.lean.js
│   │   │   │   ├── contributing_maintenance.md.k2bR0IaR.js
│   │   │   │   ├── contributing_maintenance.md.k2bR0IaR.lean.js
│   │   │   │   ├── deployment_cicd.md.Ci2T0UYC.js
│   │   │   │   ├── deployment_cicd.md.Ci2T0UYC.lean.js
│   │   │   │   ├── deployment_cloudflare-workers.md.D2WHsfep.js
│   │   │   │   ├── deployment_cloudflare-workers.md.D2WHsfep.lean.js
│   │   │   │   ├── deployment_docker.md.B8bQDQTo.js
│   │   │   │   ├── deployment_docker.md.B8bQDQTo.lean.js
│   │   │   │   ├── deployment_index.md.ClYeOkpy.js
│   │   │   │   ├── deployment_index.md.ClYeOkpy.lean.js
│   │   │   │   ├── deployment_koyeb.md.B_wJhvF7.js
│   │   │   │   ├── deployment_koyeb.md.B_wJhvF7.lean.js
│   │   │   │   ├── examples_advanced-routing.md.B3CqhLZ7.js
│   │   │   │   ├── examples_advanced-routing.md.B3CqhLZ7.lean.js
│   │   │   │   ├── examples_basic-node.md.CaDZzGlO.js
│   │   │   │   ├── examples_basic-node.md.CaDZzGlO.lean.js
│   │   │   │   ├── examples_cloudflare-worker.md.DwVSz-c7.js
│   │   │   │   ├── examples_cloudflare-worker.md.DwVSz-c7.lean.js
│   │   │   │   ├── examples_index.md.CBF_BLkl.js
│   │   │   │   ├── examples_index.md.CBF_BLkl.lean.js
│   │   │   │   ├── examples_oauth-delegation.md.1hZxoqDl.js
│   │   │   │   ├── examples_oauth-delegation.md.1hZxoqDl.lean.js
│   │   │   │   ├── examples_overview.md.CZN0JbZ7.js
│   │   │   │   ├── examples_overview.md.CZN0JbZ7.lean.js
│   │   │   │   ├── examples_testing.md.Dek4GpNs.js
│   │   │   │   ├── examples_testing.md.Dek4GpNs.lean.js
│   │   │   │   ├── getting-started_concepts.md.D7ON9iGB.js
│   │   │   │   ├── getting-started_concepts.md.D7ON9iGB.lean.js
│   │   │   │   ├── getting-started_installation.md.BKnVqAGg.js
│   │   │   │   ├── getting-started_installation.md.BKnVqAGg.lean.js
│   │   │   │   ├── getting-started_overview.md.DvJDFL2N.js
│   │   │   │   ├── getting-started_overview.md.DvJDFL2N.lean.js
│   │   │   │   ├── getting-started_quickstart-node.md.GOO4aGas.js
│   │   │   │   ├── getting-started_quickstart-node.md.GOO4aGas.lean.js
│   │   │   │   ├── getting-started_quickstart-workers.md.Cpofh8Mj.js
│   │   │   │   ├── getting-started_quickstart-workers.md.Cpofh8Mj.lean.js
│   │   │   │   ├── getting-started.md.DG9ndneo.js
│   │   │   │   ├── getting-started.md.DG9ndneo.lean.js
│   │   │   │   ├── guides_configuration-management.md.B-jwYMbA.js
│   │   │   │   ├── guides_configuration-management.md.B-jwYMbA.lean.js
│   │   │   │   ├── guides_configuration.md.Ci3zYDFA.js
│   │   │   │   ├── guides_configuration.md.Ci3zYDFA.lean.js
│   │   │   │   ├── guides_index.md.CIlq2fmx.js
│   │   │   │   ├── guides_index.md.CIlq2fmx.lean.js
│   │   │   │   ├── guides_module-loading.md.BkJvuRnQ.js
│   │   │   │   ├── guides_module-loading.md.BkJvuRnQ.lean.js
│   │   │   │   ├── guides_oauth-delegation.md.DEOZ-_G0.js
│   │   │   │   ├── guides_oauth-delegation.md.DEOZ-_G0.lean.js
│   │   │   │   ├── guides_request-routing.md.Bdzf0VLg.js
│   │   │   │   ├── guides_request-routing.md.Bdzf0VLg.lean.js
│   │   │   │   ├── guides_testing.md.kYfHqJLu.js
│   │   │   │   ├── guides_testing.md.kYfHqJLu.lean.js
│   │   │   │   ├── inter-italic-cyrillic-ext.r48I6akx.woff2
│   │   │   │   ├── inter-italic-cyrillic.By2_1cv3.woff2
│   │   │   │   ├── inter-italic-greek-ext.1u6EdAuj.woff2
│   │   │   │   ├── inter-italic-greek.DJ8dCoTZ.woff2
│   │   │   │   ├── inter-italic-latin-ext.CN1xVJS-.woff2
│   │   │   │   ├── inter-italic-latin.C2AdPX0b.woff2
│   │   │   │   ├── inter-italic-vietnamese.BSbpV94h.woff2
│   │   │   │   ├── inter-roman-cyrillic-ext.BBPuwvHQ.woff2
│   │   │   │   ├── inter-roman-cyrillic.C5lxZ8CY.woff2
│   │   │   │   ├── inter-roman-greek-ext.CqjqNYQ-.woff2
│   │   │   │   ├── inter-roman-greek.BBVDIX6e.woff2
│   │   │   │   ├── inter-roman-latin-ext.4ZJIpNVo.woff2
│   │   │   │   ├── inter-roman-latin.Di8DUHzh.woff2
│   │   │   │   ├── inter-roman-vietnamese.BjW4sHH5.woff2
│   │   │   │   ├── README.md.BO5r5M9u.js
│   │   │   │   ├── README.md.BO5r5M9u.lean.js
│   │   │   │   ├── style.BQrfSMzK.css
│   │   │   │   ├── troubleshooting_common-issues.md.CScvzWM1.js
│   │   │   │   ├── troubleshooting_common-issues.md.CScvzWM1.lean.js
│   │   │   │   ├── troubleshooting_deployment.md.DUhpqnLE.js
│   │   │   │   ├── troubleshooting_deployment.md.DUhpqnLE.lean.js
│   │   │   │   ├── troubleshooting_errors.md.BSCsEmGc.js
│   │   │   │   ├── troubleshooting_errors.md.BSCsEmGc.lean.js
│   │   │   │   ├── troubleshooting_oauth.md.Cw60Eka3.js
│   │   │   │   ├── troubleshooting_oauth.md.Cw60Eka3.lean.js
│   │   │   │   ├── troubleshooting_performance.md.DxY6LJcT.js
│   │   │   │   ├── troubleshooting_performance.md.DxY6LJcT.lean.js
│   │   │   │   ├── troubleshooting_routing.md.BHN-MDhs.js
│   │   │   │   ├── troubleshooting_routing.md.BHN-MDhs.lean.js
│   │   │   │   ├── troubleshooting_security-best-practices.md.Yiu8E-zt.js
│   │   │   │   ├── troubleshooting_security-best-practices.md.Yiu8E-zt.lean.js
│   │   │   │   ├── tutorials_beginner-getting-started.md.BXObgobW.js
│   │   │   │   ├── tutorials_beginner-getting-started.md.BXObgobW.lean.js
│   │   │   │   ├── tutorials_cloudflare-workers-tutorial.md.MPHsc0aT.js
│   │   │   │   ├── tutorials_cloudflare-workers-tutorial.md.MPHsc0aT.lean.js
│   │   │   │   ├── tutorials_load-balancing-and-resilience.md.Dv9r9jyW.js
│   │   │   │   ├── tutorials_load-balancing-and-resilience.md.Dv9r9jyW.lean.js
│   │   │   │   ├── tutorials_oauth-delegation-github.md.Nq4glqCe.js
│   │   │   │   └── tutorials_oauth-delegation-github.md.Nq4glqCe.lean.js
│   │   │   ├── configuration
│   │   │   │   ├── environment-variables.html
│   │   │   │   ├── environment.html
│   │   │   │   ├── examples.html
│   │   │   │   ├── overview.html
│   │   │   │   ├── performance.html
│   │   │   │   ├── reference.html
│   │   │   │   └── security.html
│   │   │   ├── contributing
│   │   │   │   ├── dev-setup.html
│   │   │   │   ├── guidelines.html
│   │   │   │   ├── index.html
│   │   │   │   └── maintenance.html
│   │   │   ├── deployment
│   │   │   │   ├── cicd.html
│   │   │   │   ├── cloudflare-workers.html
│   │   │   │   ├── docker.html
│   │   │   │   ├── index.html
│   │   │   │   └── koyeb.html
│   │   │   ├── diagrams
│   │   │   │   └── architecture.svg
│   │   │   ├── examples
│   │   │   │   ├── advanced-routing.html
│   │   │   │   ├── basic-node.html
│   │   │   │   ├── cloudflare-worker.html
│   │   │   │   ├── index.html
│   │   │   │   ├── oauth-delegation.html
│   │   │   │   ├── overview.html
│   │   │   │   └── testing.html
│   │   │   ├── getting-started
│   │   │   │   ├── concepts.html
│   │   │   │   ├── installation.html
│   │   │   │   ├── overview.html
│   │   │   │   ├── quick-start.html
│   │   │   │   ├── quickstart-node.html
│   │   │   │   └── quickstart-workers.html
│   │   │   ├── getting-started.html
│   │   │   ├── guides
│   │   │   │   ├── authentication.html
│   │   │   │   ├── client-integration.html
│   │   │   │   ├── configuration-management.html
│   │   │   │   ├── configuration.html
│   │   │   │   ├── index.html
│   │   │   │   ├── module-loading.html
│   │   │   │   ├── oauth-delegation.html
│   │   │   │   ├── request-routing.html
│   │   │   │   ├── server-management.html
│   │   │   │   ├── server-sharing.html
│   │   │   │   └── testing.html
│   │   │   ├── hashmap.json
│   │   │   ├── index.html
│   │   │   ├── logo.svg
│   │   │   ├── README.html
│   │   │   ├── reports
│   │   │   │   └── mcp-compliance-audit.html
│   │   │   ├── troubleshooting
│   │   │   │   ├── common-issues.html
│   │   │   │   ├── deployment.html
│   │   │   │   ├── errors.html
│   │   │   │   ├── index.html
│   │   │   │   ├── oauth.html
│   │   │   │   ├── performance.html
│   │   │   │   ├── routing.html
│   │   │   │   └── security-best-practices.html
│   │   │   ├── tutorials
│   │   │   │   ├── beginner-getting-started.html
│   │   │   │   ├── cloudflare-workers-tutorial.html
│   │   │   │   ├── load-balancing-and-resilience.html
│   │   │   │   └── oauth-delegation-github.html
│   │   │   └── vp-icons.css
│   │   └── theme
│   │       ├── components
│   │       │   ├── ApiPlayground.vue
│   │       │   ├── AuthFlowDemo.vue
│   │       │   ├── CodeTabs.vue
│   │       │   └── ConfigGenerator.vue
│   │       ├── index.ts
│   │       └── style.css
│   ├── advanced
│   │   ├── extensibility.md
│   │   ├── index.md
│   │   ├── monitoring.md
│   │   ├── performance.md
│   │   └── security.md
│   ├── api
│   │   ├── functions
│   │   │   └── createServer.md
│   │   ├── index.md
│   │   ├── interfaces
│   │   │   └── RunningServer.md
│   │   └── README.md
│   ├── architecture
│   │   └── images
│   │       └── mcp_master_architecture.svg
│   ├── configuration
│   │   ├── environment-variables.md
│   │   ├── environment.md
│   │   ├── examples.md
│   │   ├── overview.md
│   │   ├── performance.md
│   │   ├── reference.md
│   │   └── security.md
│   ├── contributing
│   │   ├── dev-setup.md
│   │   ├── guidelines.md
│   │   ├── index.md
│   │   └── maintenance.md
│   ├── deployment
│   │   ├── cicd.md
│   │   ├── cloudflare-workers.md
│   │   ├── docker.md
│   │   ├── docs-site.md
│   │   ├── index.md
│   │   └── koyeb.md
│   ├── examples
│   │   ├── advanced-routing.md
│   │   ├── basic-node.md
│   │   ├── cloudflare-worker.md
│   │   ├── index.md
│   │   ├── oauth-delegation.md
│   │   ├── overview.md
│   │   └── testing.md
│   ├── getting-started
│   │   ├── concepts.md
│   │   ├── installation.md
│   │   ├── overview.md
│   │   ├── quick-start.md
│   │   ├── quickstart-node.md
│   │   └── quickstart-workers.md
│   ├── getting-started.md
│   ├── guides
│   │   ├── authentication.md
│   │   ├── client-integration.md
│   │   ├── configuration-management.md
│   │   ├── configuration.md
│   │   ├── index.md
│   │   ├── module-loading.md
│   │   ├── oauth-delegation.md
│   │   ├── request-routing.md
│   │   ├── server-management.md
│   │   ├── server-sharing.md
│   │   └── testing.md
│   ├── index.html
│   ├── public
│   │   ├── diagrams
│   │   │   └── architecture.svg
│   │   ├── github-social.png
│   │   │   └── image.png
│   │   ├── logo.png
│   │   └── logo.svg
│   ├── README.md
│   ├── stdio-servers.md
│   ├── testing
│   │   └── phase-9-testing-architecture.md
│   ├── troubleshooting
│   │   ├── common-issues.md
│   │   ├── deployment.md
│   │   ├── errors.md
│   │   ├── index.md
│   │   ├── oauth.md
│   │   ├── performance.md
│   │   ├── routing.md
│   │   └── security-best-practices.md
│   └── tutorials
│       ├── beginner-getting-started.md
│       ├── cloudflare-workers-tutorial.md
│       ├── load-balancing-and-resilience.md
│       └── oauth-delegation-github.md
├── examples
│   ├── advanced-routing
│   │   ├── config.yaml
│   │   └── README.md
│   ├── basic-node
│   │   ├── config.yaml
│   │   ├── README.md
│   │   └── server.ts
│   ├── cloudflare-worker
│   │   ├── README.md
│   │   └── worker.ts
│   ├── custom-auth
│   │   ├── config.yaml
│   │   ├── index.ts
│   │   └── README.md
│   ├── multi-server
│   │   ├── config.yaml
│   │   └── README.md
│   ├── oauth-delegation
│   │   └── README.md
│   ├── oauth-node
│   │   ├── config.yaml
│   │   └── README.md
│   ├── performance
│   │   ├── config.yaml
│   │   └── README.md
│   ├── sample-configs
│   │   ├── basic.yaml
│   │   └── simple-setup.yaml
│   ├── security-hardening
│   │   └── README.md
│   ├── stdio-mcp-server.cjs
│   ├── test-mcp-server.js
│   └── test-stdio-server.js
├── LICENSE
├── master-mcp-definition.md
├── package-lock.json
├── package.json
├── README.md
├── reports
│   └── claude_report_20250815_222153.html
├── scripts
│   └── generate-config-docs.ts
├── src
│   ├── auth
│   │   ├── multi-auth-manager.ts
│   │   ├── oauth-providers.ts
│   │   └── token-manager.ts
│   ├── config
│   │   ├── config-loader.ts
│   │   ├── environment-manager.ts
│   │   ├── schema-validator.ts
│   │   └── secret-manager.ts
│   ├── index.ts
│   ├── mcp-server.ts
│   ├── modules
│   │   ├── capability-aggregator.ts
│   │   ├── module-loader.ts
│   │   ├── request-router.ts
│   │   ├── stdio-capability-discovery.ts
│   │   └── stdio-manager.ts
│   ├── oauth
│   │   ├── callback-handler.ts
│   │   ├── flow-controller.ts
│   │   ├── flow-validator.ts
│   │   ├── pkce-manager.ts
│   │   ├── state-manager.ts
│   │   └── web-interface.ts
│   ├── routing
│   │   ├── circuit-breaker.ts
│   │   ├── load-balancer.ts
│   │   ├── retry-handler.ts
│   │   └── route-registry.ts
│   ├── runtime
│   │   ├── node.ts
│   │   └── worker.ts
│   ├── server
│   │   ├── config-manager.ts
│   │   ├── dependency-container.ts
│   │   ├── master-server.ts
│   │   └── protocol-handler.ts
│   ├── types
│   │   ├── auth.ts
│   │   ├── config.ts
│   │   ├── jose-shim.d.ts
│   │   ├── mcp.ts
│   │   └── server.ts
│   └── utils
│       ├── cache.ts
│       ├── crypto.ts
│       ├── dev.ts
│       ├── errors.ts
│       ├── http.ts
│       ├── logger.ts
│       ├── monitoring.ts
│       ├── string.ts
│       ├── time.ts
│       ├── validation.ts
│       └── validators.ts
├── static
│   └── oauth
│       ├── consent.html
│       ├── error.html
│       ├── script.js
│       ├── style.css
│       └── success.html
├── tests
│   ├── _setup
│   │   ├── miniflare.setup.ts
│   │   └── vitest.setup.ts
│   ├── _utils
│   │   ├── log-capture.ts
│   │   ├── mock-fetch.ts
│   │   └── test-server.ts
│   ├── .gitkeep
│   ├── e2e
│   │   ├── flow-controller.express.test.ts
│   │   └── flow-controller.worker.test.ts
│   ├── factories
│   │   ├── configFactory.ts
│   │   ├── mcpFactory.ts
│   │   └── oauthFactory.ts
│   ├── fixtures
│   │   ├── capabilities.json
│   │   └── stdio-server.js
│   ├── integration
│   │   ├── modules.capability-aggregator.test.ts
│   │   ├── modules.module-loader-health.test.ts
│   │   ├── oauth.callback-handler.test.ts
│   │   └── request-router.test.ts
│   ├── mocks
│   │   ├── mcp
│   │   │   └── fake-backend.ts
│   │   └── oauth
│   │       └── mock-oidc-provider.ts
│   ├── perf
│   │   ├── artillery
│   │   │   └── auth-routing.yaml
│   │   └── perf.auth-and-routing.test.ts
│   ├── security
│   │   └── security.oauth-and-input.test.ts
│   ├── servers
│   │   ├── test-auth-simple.js
│   │   ├── test-debug.js
│   │   ├── test-master-mcp.js
│   │   ├── test-mcp-client.js
│   │   ├── test-streaming-both-complete.js
│   │   ├── test-streaming-both-full.js
│   │   ├── test-streaming-both-simple.js
│   │   ├── test-streaming-both.js
│   │   └── test-streaming.js
│   ├── setup
│   │   └── test-setup.ts
│   ├── unit
│   │   ├── auth.multi-auth-manager.test.ts
│   │   ├── auth.token-manager.test.ts
│   │   ├── config.environment-manager.test.ts
│   │   ├── config.schema-validator.test.ts
│   │   ├── config.secret-manager.test.ts
│   │   ├── modules
│   │   │   ├── stdio-capability-discovery.test.ts
│   │   │   └── stdio-manager.test.ts
│   │   ├── modules.route-registry.test.ts
│   │   ├── oauth.pkce-state.test.ts
│   │   ├── routing
│   │   │   └── circuit-breaker.test.ts
│   │   ├── routing.core.test.ts
│   │   ├── stdio-capability-discovery.test.ts
│   │   ├── utils.crypto.test.ts
│   │   ├── utils.logger.test.ts
│   │   └── utils.monitoring.test.ts
│   └── utils
│       ├── fake-express.ts
│       ├── mock-http.ts
│       ├── oauth-mocks.ts
│       └── token-storages.ts
├── tsconfig.base.json
├── tsconfig.json
├── tsconfig.node.json
├── tsconfig.worker.json
├── typedoc.json
├── vitest.config.ts
└── vitest.worker.config.ts
```

# Files

--------------------------------------------------------------------------------
/src/utils/validation.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Validation and sanitization helpers with a small schema system.
 * No external dependencies; suitable for Node and Workers.
 */

export function isNonEmptyString(value: unknown): value is string {
  return typeof value === 'string' && value.trim().length > 0
}

export function isRecord(value: unknown): value is Record<string, unknown> {
  return !!value && typeof value === 'object' && !Array.isArray(value)
}

export function sanitizeString(input: unknown, opts?: { maxLength?: number; trim?: boolean }): string {
  let s = typeof input === 'string' ? input : String(input ?? '')
  if (opts?.trim !== false) s = s.trim()
  // Remove control characters except tab, newline, carriage return
  s = s.replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g, '')
  if (opts?.maxLength && s.length > opts.maxLength) s = s.slice(0, opts.maxLength)
  return s
}

export function sanitizeObject<T extends Record<string, unknown>>(obj: T): T {
  const dangerous = ['__proto__', 'constructor', 'prototype']
  for (const k of Object.keys(obj)) {
    if (dangerous.includes(k)) delete (obj as any)[k]
  }
  return obj
}

export function assert(condition: unknown, message = 'Assertion failed'): asserts condition {
  if (!condition) throw new Error(message)
}

export function assertString(value: unknown, message = 'Expected string'): asserts value is string {
  if (typeof value !== 'string') throw new Error(message)
}

export function assertNumber(value: unknown, message = 'Expected number'): asserts value is number {
  if (typeof value !== 'number' || Number.isNaN(value)) throw new Error(message)
}

export function assertBoolean(value: unknown, message = 'Expected boolean'): asserts value is boolean {
  if (typeof value !== 'boolean') throw new Error(message)
}

export type SafeParseResult<T> = { success: true; data: T } | { success: false; error: string }

export interface Schema<T> {
  parse(input: unknown): T
  safeParse(input: unknown): SafeParseResult<T>
}

function makeSchema<T>(name: string, parse: (i: unknown) => T): Schema<T> {
  return {
    parse(input: unknown): T {
      try {
        return parse(input)
      } catch (e) {
        const msg = e instanceof Error ? e.message : String(e)
        throw new Error(`${name} validation failed: ${msg}`)
      }
    },
    safeParse(input: unknown): SafeParseResult<T> {
      try {
        return { success: true, data: parse(input) }
      } catch (e) {
        return { success: false, error: e instanceof Error ? e.message : String(e) }
      }
    },
  }
}

export const v = {
  string: (opts?: { min?: number; max?: number; pattern?: RegExp }) =>
    makeSchema<string>('string', (i) => {
      if (typeof i !== 'string') throw new Error('not a string')
      const s = i
      if (opts?.min !== undefined && s.length < opts.min) throw new Error(`min length ${opts.min}`)
      if (opts?.max !== undefined && s.length > opts.max) throw new Error(`max length ${opts.max}`)
      if (opts?.pattern && !opts.pattern.test(s)) throw new Error('pattern mismatch')
      return s
    }),

  number: (opts?: { min?: number; max?: number; int?: boolean }) =>
    makeSchema<number>('number', (i) => {
      if (typeof i !== 'number' || Number.isNaN(i)) throw new Error('not a number')
      const n = i
      if (opts?.int) {
        if (!Number.isInteger(n)) throw new Error('not an integer')
      }
      if (opts?.min !== undefined && n < opts.min) throw new Error(`min ${opts.min}`)
      if (opts?.max !== undefined && n > opts.max) throw new Error(`max ${opts.max}`)
      return n
    }),

  boolean: () => makeSchema<boolean>('boolean', (i) => {
    if (typeof i !== 'boolean') throw new Error('not a boolean')
    return i
  }),

  literal: <T extends string | number | boolean | null>(val: T) =>
    makeSchema<T>('literal', (i) => {
      if (i !== val) throw new Error(`expected ${String(val)}`)
      return i as T
    }),

  array: <T>(inner: Schema<T>, opts?: { min?: number; max?: number }) =>
    makeSchema<T[]>('array', (i) => {
      if (!Array.isArray(i)) throw new Error('not an array')
      if (opts?.min !== undefined && i.length < opts.min) throw new Error(`min length ${opts.min}`)
      if (opts?.max !== undefined && i.length > opts.max) throw new Error(`max length ${opts.max}`)
      return i.map((x) => inner.parse(x))
    }),

  object: <S extends Record<string, Schema<any>>>(shape: S) =>
    makeSchema<{ [K in keyof S]: S[K] extends Schema<infer U> ? U : never }>('object', (i) => {
      if (!isRecord(i)) throw new Error('not an object')
      const out: Record<string, unknown> = {}
      for (const [k, s] of Object.entries(shape)) {
        out[k] = (s as Schema<unknown>).parse((i as any)[k])
      }
      return out as any
    }),

  union: <A, B>(a: Schema<A>, b: Schema<B>) =>
    makeSchema<A | B>('union', (i) => {
      const ra = a.safeParse(i)
      if (ra.success) return ra.data
      const rb = b.safeParse(i)
      if (rb.success) return rb.data
      throw new Error(`no union match: ${ra.error}; ${rb.error}`)
    }),

  optional: <T>(inner: Schema<T>) =>
    makeSchema<T | undefined>('optional', (i) => {
      if (i === undefined) return undefined
      return inner.parse(i)
    }),
}

export function isEmail(input: string): boolean {
  // Simple and conservative
  return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(input)
}

export function isUrl(input: string): boolean {
  try {
    const u = new URL(input)
    return u.protocol === 'http:' || u.protocol === 'https:'
  } catch {
    return false
  }
}

export function isUUID(input: string): boolean {
  return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(input)
}

export function safeHeaderName(name: string): boolean {
  return /^[A-Za-z0-9-]+$/.test(name) && !/__/g.test(name)
}

export function safeHeaderValue(value: string): boolean {
  return !/[\r\n]/.test(value) && value.length < 8192
}

export function validateAgainstSchema<T>(schema: Schema<T>, input: unknown): T {
  return schema.parse(input)
}


```

--------------------------------------------------------------------------------
/src/auth/multi-auth-manager.ts:
--------------------------------------------------------------------------------

```typescript
import { createRemoteJWKSet, jwtVerify } from 'jose'
import type { AuthHeaders, OAuthDelegation, OAuthToken } from '../types/auth.js'
import type { MasterAuthConfig, ServerAuthConfig } from '../types/config.js'
import { AuthStrategy } from '../types/config.js'
import { Logger } from '../utils/logger.js'
import { getOAuthProvider } from './oauth-providers.js'
import { TokenManager } from './token-manager.js'

export class MultiAuthManager {
  private serverAuth: Map<string, { strategy: AuthStrategy; config?: ServerAuthConfig }> = new Map()
  private jwks?: ReturnType<typeof createRemoteJWKSet>
  private tokenManager = new TokenManager()

  constructor(private readonly config: MasterAuthConfig) {
    if (config.jwks_uri) {
      try {
        this.jwks = createRemoteJWKSet(new URL(config.jwks_uri))
      } catch (err) {
        Logger.warn('Failed to initialize JWKS for client token validation', err)
      }
    }
  }

  registerServerAuth(serverId: string, strategy: AuthStrategy, authConfig?: ServerAuthConfig): void {
    this.serverAuth.set(serverId, { strategy, config: authConfig })
  }

  private keyFor(clientToken: string, serverId: string): string {
    return `${serverId}::${clientToken.slice(0, 16)}`
  }

  async validateClientToken(token: string): Promise<boolean> {
    if (!token || typeof token !== 'string') return false
    if (!this.jwks) {
      // Best-effort: check structural validity and expiration if it is a JWT; otherwise accept as opaque bearer
      try {
        const { payload } = await jwtVerify(token, async () => {
          // No key ⇒ force failure to reach catch where we treat opaque tokens as valid
          throw new Error('no-jwks')
        })
        const now = Math.floor(Date.now() / 1000)
        return typeof payload.exp !== 'number' || payload.exp > now
      } catch {
        return true // Accept opaque tokens when no JWKS is configured
      }
    }

    try {
      await jwtVerify(token, this.jwks, {
        issuer: this.config.issuer,
        audience: this.config.audience ?? this.config.client_id,
      })
      return true
    } catch (err) {
      Logger.warn('Client token verification failed', String(err))
      return false
    }
  }

  async prepareAuthForBackend(serverId: string, clientToken: string): Promise<AuthHeaders | OAuthDelegation> {
    const isValid = await this.validateClientToken(clientToken)
    if (!isValid) throw new Error('Invalid client token')

    const entry = this.serverAuth.get(serverId)
    if (!entry) {
      // Default: pass-through
      return { Authorization: `Bearer ${clientToken}` }
    }

    const { strategy, config } = entry
    switch (strategy) {
      case AuthStrategy.MASTER_OAUTH:
        return this.handleMasterOAuth(serverId, clientToken)
      case AuthStrategy.DELEGATE_OAUTH:
        if (!config) throw new Error(`Missing auth config for server ${serverId}`)
        return this.handleDelegatedOAuth(serverId, clientToken, config)
      case AuthStrategy.BYPASS_AUTH:
        return {}
      case AuthStrategy.PROXY_OAUTH:
        if (!config) throw new Error(`Missing auth config for server ${serverId}`)
        return this.handleProxyOAuth(serverId, clientToken, config)
      default:
        return { Authorization: `Bearer ${clientToken}` }
    }
  }

  public async handleMasterOAuth(_serverId: string, clientToken: string): Promise<AuthHeaders> {
    // Pass-through the client's master token
    return { Authorization: `Bearer ${clientToken}` }
  }

  public async handleDelegatedOAuth(
    serverId: string,
    clientToken: string,
    serverAuthConfig: ServerAuthConfig
  ): Promise<OAuthDelegation> {
    // Return instructions for the client to complete OAuth against the provider
    const scopes = Array.isArray(serverAuthConfig.scopes) ? serverAuthConfig.scopes : ['openid']
    // Create state binding server + client
    const state = this.tokenManager.generateState({ serverId })
    // Store a minimal pending marker for later exchange if needed
    await this.tokenManager.storeToken(this.keyFor(clientToken, serverId), {
      access_token: '',
      expires_at: 0,
      scope: [],
    })

    return {
      type: 'oauth_delegation',
      auth_endpoint: serverAuthConfig.authorization_endpoint,
      token_endpoint: serverAuthConfig.token_endpoint,
      client_info: { client_id: serverAuthConfig.client_id, metadata: { state } },
      required_scopes: scopes,
      redirect_after_auth: true,
    }
  }

  public async handleProxyOAuth(
    serverId: string,
    clientToken: string,
    serverAuthConfig: ServerAuthConfig
  ): Promise<AuthHeaders> {
    const key = this.keyFor(clientToken, serverId)
    const existing = await this.tokenManager.getToken(key)
    const now = Date.now()
    if (existing && existing.access_token && existing.expires_at > now + 30_000) {
      return { Authorization: `Bearer ${existing.access_token}` }
    }

    if (existing?.refresh_token) {
      try {
        const provider = getOAuthProvider(serverAuthConfig as any)
        const refreshed = await provider.refreshToken(existing.refresh_token)
        await this.tokenManager.storeToken(key, refreshed)
        return { Authorization: `Bearer ${refreshed.access_token}` }
      } catch (err) {
        Logger.warn('Refresh token failed; falling back to pass-through', err)
      }
    }

    // Fallback: pass through the client token (may be accepted by backend if configured)
    return { Authorization: `Bearer ${clientToken}` }
  }

  async storeDelegatedToken(clientToken: string, serverId: string, serverToken: string | OAuthToken): Promise<void> {
    const key = this.keyFor(clientToken, serverId)
    const tokenObj: OAuthToken = typeof serverToken === 'string'
      ? { access_token: serverToken, expires_at: Date.now() + 3600_000, scope: [] }
      : serverToken
    await this.tokenManager.storeToken(key, tokenObj)
  }

  async getStoredServerToken(serverId: string, clientToken: string): Promise<string | undefined> {
    const tok = await this.tokenManager.getToken(this.keyFor(clientToken, serverId))
    return tok?.access_token
  }
}

```

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

```typescript
import type { AuthInfo } from '../types/auth.js'

export type LogLevel = 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace'

export interface LogFields {
  [key: string]: unknown
  correlationId?: string
}

interface LoggerOptions {
  level?: LogLevel
  json?: boolean
  base?: LogFields
}

/**
 * Lightweight, structured, context-aware logger with JSON output support and
 * timing utilities. Designed to run on Node.js and Workers without deps.
 */
export class Logger {
  private static level: LogLevel = ((): LogLevel => {
    const env = (globalThis as any)?.process?.env
    const raw = (env?.LOG_LEVEL || env?.NODE_LOG_LEVEL || 'info').toLowerCase()
    const allowed: LogLevel[] = ['fatal', 'error', 'warn', 'info', 'debug', 'trace']
    return (allowed.includes(raw as LogLevel) ? (raw as LogLevel) : 'info') as LogLevel
  })()

  private static json: boolean = ((): boolean => {
    const env = (globalThis as any)?.process?.env
    const raw = env?.LOG_FORMAT || env?.LOG_JSON
    if (!raw) return (env?.NODE_ENV === 'production') as boolean
    return String(raw).toLowerCase() === 'true' || String(raw).toLowerCase() === 'json'
  })()

  private static base: LogFields = {}

  static configure(opts: LoggerOptions): void {
    if (opts.level) this.level = opts.level
    if (typeof opts.json === 'boolean') this.json = opts.json
    if (opts.base) this.base = { ...this.base, ...sanitizeFields(opts.base) }
  }

  static with(fields: LogFields): typeof Logger {
    const merged = { ...this.base, ...sanitizeFields(fields) }
    const child = new Proxy(this, {
      get: (target, prop) => {
        if (prop === 'base') return merged
        return (target as any)[prop]
      },
    }) as typeof Logger
    return child
  }

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

  static enableJSON(enabled: boolean): void {
    this.json = enabled
  }

  static getLevel(): LogLevel {
    return this.level
  }

  static trace(message: string, fields?: LogFields | unknown): void {
    const f = fieldsToLogFields(fields)
    this._log('trace', message, f)
  }
  static debug(message: string, fields?: LogFields | unknown): void {
    const envDebug = (globalThis as any)?.process?.env?.DEBUG
    const f = fieldsToLogFields(fields)
    if (envDebug || this.levelAllowed('debug')) this._log('debug', message, f)
  }
  static info(message: string, fields?: LogFields | unknown): void {
    const f = fieldsToLogFields(fields)
    this._log('info', message, f)
  }
  static warn(message: string, fields?: LogFields | unknown): void {
    const f = fieldsToLogFields(fields)
    this._log('warn', message, f)
  }
  static error(message: string, fields?: LogFields | unknown): void {
    const f = fieldsToLogFields(fields)
    this._log('error', message, f)
  }
  static fatal(message: string, fields?: LogFields | unknown): void {
    const f = fieldsToLogFields(fields)
    this._log('fatal', message, f)
  }

  /**
   * Structured auth event helper for backward compatibility.
   */
  static logAuthEvent(event: string, context: AuthInfo): void {
    this.info('auth_event', { event, ...context })
  }

  /**
   * Structured server event helper for backward compatibility.
   */
  static logServerEvent(event: string, serverId: string, context?: unknown): void {
    const fields = fieldsToLogFields(context)
    this.info('server_event', { event, serverId, ...(fields ?? {}) })
  }

  /**
   * Starts a performance timer, returning a function to log completion.
   *
   * Usage:
   * const done = Logger.time('load_config', { id })
   * ...work...
   * done({ status: 'ok' })
   */
  static time(name: string, fields?: LogFields): (extra?: LogFields) => void {
    const start = now()
    const base = { name, ...(fields ? sanitizeFields(fields) : {}) }
    return (extra?: LogFields) => {
      const durationMs = Math.max(0, now() - start)
      this.info('perf', { ...base, ...(extra ? sanitizeFields(extra) : {}), durationMs })
    }
  }

  /**
   * Low-level log method honoring level and output format.
   */
  private static _log(level: LogLevel, message: string, fields?: LogFields): void {
    if (!this.levelAllowed(level)) return
    const ts = new Date().toISOString()
    const entry = {
      ts,
      level,
      msg: message,
      ...this.base,
      ...(fields ? sanitizeFields(fields) : {}),
    }

    // eslint-disable-next-line no-console
    if (this.json) console.log(JSON.stringify(entry))
    else console.log(formatHuman(entry))
  }

  private static levelAllowed(check: LogLevel): boolean {
    const order: LogLevel[] = ['trace', 'debug', 'info', 'warn', 'error', 'fatal']
    const curIdx = order.indexOf(this.level)
    const chkIdx = order.indexOf(check)
    return chkIdx >= curIdx
  }
}

function now(): number {
  if (typeof performance !== 'undefined' && typeof performance.now === 'function') {
    return performance.now()
  }
  return Date.now()
}

function sanitizeFields(fields: LogFields): LogFields {
  const out: LogFields = {}
  for (const [k, v] of Object.entries(fields)) {
    if (v === undefined) continue
    if (v instanceof Error) {
      out[k] = {
        name: v.name,
        message: v.message,
        stack: v.stack,
      }
    } else if (typeof v === 'object' && v !== null) {
      try {
        // Avoid circular structures
        out[k] = JSON.parse(JSON.stringify(v))
      } catch {
        out[k] = String(v)
      }
    } else {
      out[k] = v as any
    }
  }
  return out
}

function formatHuman(entry: { [k: string]: unknown }): string {
  const { ts, level, msg, ...rest } = entry as any
  const head = `[${String(level).toUpperCase()}] ${ts} ${msg}`
  const restKeys = Object.keys(rest)
  if (restKeys.length === 0) return head
  return `${head} ${safeStringify(rest)}`
}

function safeStringify(obj: any): string {
  try {
    return JSON.stringify(obj)
  } catch {
    return '[object]'
  }
}

function fieldsToLogFields(f?: LogFields | unknown): LogFields | undefined {
  if (!f) return undefined
  if (typeof f === 'object' && !(f instanceof Error)) return f as LogFields
  return { detail: f }
}

```

--------------------------------------------------------------------------------
/src/server/master-server.ts:
--------------------------------------------------------------------------------

```typescript
// Phase 1: avoid hard dependency on SDK types to ensure compilation
import type { ServerCapabilities, LoadedServer } from '../types/server.js'
import type { MasterConfig, RoutingConfig, ServerConfig } from '../types/config.js'
import type { AuthHeaders, OAuthDelegation } from '../types/auth.js'
import { ProtocolHandler } from './protocol-handler.js'
import { DefaultModuleLoader } from '../modules/module-loader.js'
import { CapabilityAggregator } from '../modules/capability-aggregator.js'
import { RequestRouter } from '../modules/request-router.js'
import { Logger } from '../utils/logger.js'
import { MultiAuthManager } from '../auth/multi-auth-manager.js'
import { OAuthFlowController } from '../oauth/flow-controller.js'

export class MasterServer {
  readonly server: unknown
  readonly handler: ProtocolHandler

  private readonly loader = new DefaultModuleLoader()
  private readonly aggregator = new CapabilityAggregator()
  private readonly servers = new Map<string, LoadedServer>()
  private router!: RequestRouter
  private config?: MasterConfig
  private authManager?: MultiAuthManager
  private oauthController?: OAuthFlowController
  private getAuthHeaders: (
    serverId: string,
    clientToken?: string
  ) => Promise<AuthHeaders | OAuthDelegation | undefined>

  constructor(capabilities?: Partial<ServerCapabilities>, routing?: RoutingConfig) {
    const version = (globalThis as any)?.process?.env?.APP_VERSION ?? '0.1.0'
    this.server = { name: 'master-mcp-server', version }
    this.getAuthHeaders = async (_serverId: string, clientToken?: string) =>
      clientToken ? { Authorization: `Bearer ${clientToken}` } : undefined
    this.router = new RequestRouter(this.servers, this.aggregator, this.getAuthHeaders.bind(this), { routing })
    this.handler = new ProtocolHandler({ aggregator: this.aggregator, router: this.router })
    void capabilities
  }

  async startFromConfig(config: MasterConfig, clientToken?: string): Promise<void> {
    Logger.info('Starting MasterServer from config')
    this.config = config
    await this.loadServers(config.servers, clientToken)
    await this.discoverAllCapabilities(clientToken)
  }

  async loadServers(servers: ServerConfig[], clientToken?: string): Promise<void> {
    Logger.info('Loading servers', { servers })
    const loaded = await this.loader.loadServers(servers, clientToken)
    Logger.info('Loaded servers', { loaded: Array.from(loaded.entries()) })
    this.servers.clear()
    for (const [id, s] of loaded) this.servers.set(id, s)
    this.router = new RequestRouter(this.servers, this.aggregator, this.getAuthHeaders.bind(this), {
      routing: this.config?.routing,
    })
    ;(this as any).handler = new ProtocolHandler({ aggregator: this.aggregator, router: this.router })
  }

  async discoverAllCapabilities(clientToken?: string): Promise<void> {
    Logger.info('Discovering all capabilities', { servers: Array.from(this.servers.entries()) })
    const headersOnly = async (serverId: string, token?: string) => {
      const res = await this.getAuthHeaders(serverId, token)
      if (res && (res as OAuthDelegation).type === 'oauth_delegation') {
        return token ? { Authorization: `Bearer ${token}` } : undefined
      }
      return res as AuthHeaders | undefined
    }
    await this.aggregator.discoverCapabilities(this.servers, clientToken, headersOnly)
    Logger.info('Discovered all capabilities', { tools: this.aggregator.getAllTools(this.servers), resources: this.aggregator.getAllResources(this.servers) })
  }

  // Allow host app to inject an auth header strategy (e.g., MultiAuthManager)
  setAuthHeaderProvider(
    fn: (serverId: string, clientToken?: string) => Promise<AuthHeaders | OAuthDelegation | undefined>
  ): void {
    this.getAuthHeaders = fn
    this.router = new RequestRouter(this.servers, this.aggregator, this.getAuthHeaders.bind(this), {
      routing: this.config?.routing,
    })
    ;(this as any).handler = new ProtocolHandler({ aggregator: this.aggregator, router: this.router })
  }

  getRouter(): RequestRouter {
    return this.router
  }

  getAggregatedTools(): ServerCapabilities['tools'] {
    return this.aggregator.getAllTools(this.servers)
  }

  getAggregatedResources(): ServerCapabilities['resources'] {
    return this.aggregator.getAllResources(this.servers)
  }

  async performHealthChecks(clientToken?: string): Promise<Record<string, boolean>> {
    const results: Record<string, boolean> = {}
    for (const [id, s] of this.servers) {
      results[id] = await this.loader.performHealthCheck(s, clientToken)
    }
    return results
  }

  async restartServer(id: string): Promise<void> {
    await this.loader.restartServer(id)
  }

  async unloadAll(): Promise<void> {
    await Promise.all(Array.from(this.servers.keys()).map((id) => this.loader.unload(id)))
    this.servers.clear()
  }

  attachAuthManager(manager: MultiAuthManager): void {
    this.authManager = manager
    this.setAuthHeaderProvider((serverId: string, clientToken?: string) => {
      if (!clientToken) return Promise.resolve(undefined)
      return this.authManager!.prepareAuthForBackend(serverId, clientToken)
    })
  }

  // Provide an OAuthFlowController wired to the current config and auth manager.
  // Host runtimes (Node/Workers) can use this to mount HTTP endpoints without coupling MasterServer to a specific HTTP framework.
  getOAuthFlowController(): OAuthFlowController {
    if (!this.config) throw new Error('MasterServer config not initialized')
    if (!this.authManager) throw new Error('Auth manager not attached')
    if (!this.oauthController) {
      this.oauthController = new OAuthFlowController(
        {
          getConfig: () => this.config!,
          storeDelegatedToken: async (clientToken, serverId, token) => {
            await this.authManager!.storeDelegatedToken(clientToken, serverId, token)
          },
        },
        '/oauth'
      )
    }
    return this.oauthController
  }

  updateRouting(routing?: RoutingConfig): void {
    this.router = new RequestRouter(this.servers, this.aggregator, this.getAuthHeaders.bind(this), { routing })
    ;(this as any).handler = new ProtocolHandler({ aggregator: this.aggregator, router: this.router })
  }
}

```

--------------------------------------------------------------------------------
/tests/servers/test-streaming-both-complete.js:
--------------------------------------------------------------------------------

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

import { spawn } from 'node:child_process'
import { Client } from '@modelcontextprotocol/sdk/client/index.js'
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'

async function startHttpServer() {
  console.log('Starting HTTP test server...')
  
  // Start the HTTP server as a background process
  const httpServer = spawn('node', ['examples/test-mcp-server.js'], {
    stdio: ['ignore', 'pipe', 'pipe'],
    env: { ...process.env, PORT: '3006' }
  })
  
  // Capture stdout and stderr
  httpServer.stdout.on('data', (data) => {
    console.log(`[HTTP Server] ${data.toString().trim()}`)
  })
  
  httpServer.stderr.on('data', (data) => {
    console.error(`[HTTP Server ERROR] ${data.toString().trim()}`)
  })
  
  // Wait for the server to start
  await new Promise((resolve, reject) => {
    let timeout = setTimeout(() => {
      reject(new Error('HTTP server startup timeout'))
    }, 5000)
    
    httpServer.stdout.on('data', (data) => {
      if (data.toString().includes('Test MCP server listening')) {
        clearTimeout(timeout)
        resolve()
      }
    })
  })
  
  return httpServer
}

async function startMasterServer() {
  console.log('Starting Master MCP server...')
  
  // Start the master server as a background process
  const masterServer = spawn('npm', ['run', 'dev'], {
    stdio: ['ignore', 'pipe', 'pipe'],
    cwd: process.cwd()
  })
  
  // Capture stdout and stderr
  masterServer.stdout.on('data', (data) => {
    const output = data.toString().trim()
    // Only log important messages to avoid too much output
    if (output.includes('Master MCP listening') || output.includes('error') || output.includes('ERROR')) {
      console.log(`[Master Server] ${output}`)
    }
  })
  
  masterServer.stderr.on('data', (data) => {
    console.error(`[Master Server ERROR] ${data.toString().trim()}`)
  })
  
  // Wait for the server to start
  await new Promise((resolve, reject) => {
    let timeout = setTimeout(() => {
      reject(new Error('Master server startup timeout'))
    }, 15000)
    
    masterServer.stdout.on('data', (data) => {
      if (data.toString().includes('Master MCP listening')) {
        clearTimeout(timeout)
        console.log('[Master Server] Server is ready!')
        resolve()
      }
    })
  })
  
  return masterServer
}

async function runStreamingTest() {
  try {
    console.log('Testing Master MCP Server with HTTP Streaming...')
    
    // Create a streamable HTTP transport to connect to our MCP server
    const transport = new StreamableHTTPClientTransport(new URL('http://localhost:3005/mcp'))
    
    // Create the MCP client
    const client = new Client({
      name: 'master-mcp-streaming-test-client',
      version: '1.0.0'
    })
    
    // Initialize the client
    await client.connect(transport)
    console.log('✅ Server initialized with streaming transport')
    console.log('Server info:', client.getServerVersion())
    console.log('Server capabilities:', client.getServerCapabilities())
    
    // List tools using streaming
    console.log('\n--- Testing tools/list with streaming ---')
    const toolsResult = await client.listTools({})
    console.log('✅ tools/list successful with streaming')
    console.log('Number of tools:', toolsResult.tools.length)
    console.log('Tools:', toolsResult.tools.map(t => t.name))
    
    // Verify both servers are present
    const hasHttpTool = toolsResult.tools.some(t => t.name === 'test-server.echo')
    const hasStdioTool = toolsResult.tools.some(t => t.name === 'stdio-server.stdio-echo')
    
    if (hasHttpTool) {
      console.log('✅ HTTP server tool found')
    } else {
      console.log('❌ HTTP server tool not found')
    }
    
    if (hasStdioTool) {
      console.log('✅ STDIO server tool found')
    } else {
      console.log('❌ STDIO server tool not found')
    }
    
    // List resources using streaming
    console.log('\n--- Testing resources/list with streaming ---')
    const resourcesResult = await client.listResources({})
    console.log('✅ resources/list successful with streaming')
    console.log('Number of resources:', resourcesResult.resources.length)
    console.log('Resources:', resourcesResult.resources.map(r => r.uri))
    
    // Verify both servers are present
    const hasHttpResource = resourcesResult.resources.some(r => r.uri === 'test-server.test://example')
    const hasStdioResource = resourcesResult.resources.some(r => r.uri === 'stdio-server.stdio://example/resource')
    
    if (hasHttpResource) {
      console.log('✅ HTTP server resource found')
    } else {
      console.log('❌ HTTP server resource not found')
    }
    
    if (hasStdioResource) {
      console.log('✅ STDIO server resource found')
    } else {
      console.log('❌ STDIO server resource not found')
    }
    
    // Test ping
    console.log('\n--- Testing ping with streaming ---')
    const pingResult = await client.ping()
    console.log('✅ ping successful with streaming')
    console.log('Ping result:', pingResult)
    
    // Summary
    console.log('\n--- Test Summary ---')
    if (hasHttpTool && hasStdioTool && hasHttpResource && hasStdioResource) {
      console.log('🎉 All tests passed! Both HTTP and STDIO servers are working correctly.')
    } else {
      console.log('⚠️ Some tests failed. Check the output above for details.')
    }
    
    // Close the connection
    await client.close()
    console.log('\n✅ Disconnected from MCP server')
    
  } catch (error) {
    console.error('❌ Streaming test failed:', error)
    console.error('Error stack:', error.stack)
  }
}

async function main() {
  let httpServer, masterServer
  
  try {
    // Start the HTTP server
    httpServer = await startHttpServer()
    
    // Start the master server
    masterServer = await startMasterServer()
    
    // Wait a bit for discovery to happen
    console.log('Waiting for server discovery...')
    await new Promise(resolve => setTimeout(resolve, 3000))
    
    // Run the streaming test
    await runStreamingTest()
  } catch (error) {
    console.error('Test failed:', error)
  } finally {
    // Clean up: kill the servers
    if (httpServer) {
      console.log('Stopping HTTP server...')
      httpServer.kill()
    }
    if (masterServer) {
      console.log('Stopping Master server...')
      masterServer.kill()
    }
  }
}

// Run the test
main()
```

--------------------------------------------------------------------------------
/src/config/schema-validator.ts:
--------------------------------------------------------------------------------

```typescript
import type { MasterConfig } from '../types/config.js'
import { Logger } from '../utils/logger.js'

type JSONSchema = {
  $id?: string
  type?: string | string[]
  properties?: Record<string, JSONSchema>
  required?: string[]
  additionalProperties?: boolean
  enum?: unknown[]
  items?: JSONSchema
  format?: 'url' | 'secret' | 'integer'
  anyOf?: JSONSchema[]
  allOf?: JSONSchema[]
  description?: string
}

export interface SchemaValidationError {
  path: string
  message: string
}

export class SchemaValidator {
  // Lightweight JSON Schema validator supporting core features used by our config schema
  static async loadSchema(schemaPath?: string): Promise<JSONSchema | undefined> {
    if (!schemaPath) return defaultSchema
    try {
      const isNode = Boolean((globalThis as any)?.process?.versions?.node)
      if (!isNode) return defaultSchema
      const fs = await import('node:fs/promises')
      const raw = await fs.readFile(schemaPath, 'utf8')
      return JSON.parse(raw) as JSONSchema
    } catch (err) {
      Logger.warn(`Failed to read schema at ${schemaPath}; using built-in`, String(err))
      return defaultSchema
    }
  }

  static validate(config: unknown, schema: JSONSchema): { valid: boolean; errors: SchemaValidationError[] } {
    const errors: SchemaValidationError[] = []
    validateAgainst(config, schema, '', errors)
    return { valid: errors.length === 0, errors }
  }

  static assertValid<T = MasterConfig>(config: unknown, schema: JSONSchema): T {
    const { valid, errors } = this.validate(config, schema)
    if (!valid) {
      const msg = errors.map((e) => `${e.path || '<root>'}: ${e.message}`).join('\n')
      throw new Error(`Configuration validation failed:\n${msg}`)
    }
    return config as T
  }
}

function typeOf(val: unknown): string {
  if (Array.isArray(val)) return 'array'
  return typeof val
}

function validateAgainst(value: unknown, schema: JSONSchema, path: string, errors: SchemaValidationError[]): void {
  if (!schema) return
  // Type check
  if (schema.type) {
    const allowed = Array.isArray(schema.type) ? schema.type : [schema.type]
    const actual = typeOf(value)
    if (!allowed.includes(actual)) {
      errors.push({ path, message: `expected type ${allowed.join('|')}, got ${actual}` })
      return
    }
  }

  if (schema.enum && !schema.enum.includes(value)) {
    errors.push({ path, message: `must be one of ${schema.enum.join(', ')}` })
  }

  if (schema.format) {
    if (schema.format === 'url' && typeof value === 'string') {
      try {
        // eslint-disable-next-line no-new
        new URL(value)
      } catch {
        errors.push({ path, message: 'must be a valid URL' })
      }
    }
    if (schema.format === 'integer' && typeof value === 'number') {
      if (!Number.isInteger(value)) errors.push({ path, message: 'must be an integer' })
    }
  }

  if (schema.properties && value && typeof value === 'object' && !Array.isArray(value)) {
    const v = value as Record<string, unknown>
    const required = schema.required || []
    for (const r of required) {
      if (!(r in v)) errors.push({ path: join(path, r), message: 'is required' })
    }
    for (const [k, subschema] of Object.entries(schema.properties)) {
      if (k in v) validateAgainst(v[k], subschema, join(path, k), errors)
    }
    if (schema.additionalProperties === false) {
      for (const k of Object.keys(v)) {
        if (!schema.properties[k]) errors.push({ path: join(path, k), message: 'is not allowed' })
      }
    }
  }

  if (schema.items && Array.isArray(value)) {
    value.forEach((item, idx) => validateAgainst(item, schema.items!, join(path, String(idx)), errors))
  }

  if (schema.allOf) {
    for (const s of schema.allOf) validateAgainst(value, s, path, errors)
  }
  if (schema.anyOf) {
    const ok = schema.anyOf.some((s) => {
      const temp: SchemaValidationError[] = []
      validateAgainst(value, s, path, temp)
      return temp.length === 0
    })
    if (!ok) errors.push({ path, message: 'does not match any allowed schema' })
  }
}

function join(base: string, key: string): string {
  return base ? `${base}.${key}` : key
}

// Built-in fallback schema captures core fields and constraints.
const defaultSchema: JSONSchema = {
  type: 'object',
  required: ['master_oauth', 'hosting', 'servers'],
  properties: {
    master_oauth: {
      type: 'object',
      required: ['authorization_endpoint', 'token_endpoint', 'client_id', 'redirect_uri', 'scopes'],
      properties: {
        issuer: { type: 'string' },
        authorization_endpoint: { type: 'string', format: 'url' },
        token_endpoint: { type: 'string', format: 'url' },
        jwks_uri: { type: 'string' },
        client_id: { type: 'string' },
        client_secret: { type: 'string' },
        redirect_uri: { type: 'string' },
        scopes: { type: 'array', items: { type: 'string' } },
        audience: { type: 'string' },
      },
      additionalProperties: true,
    },
    hosting: {
      type: 'object',
      required: ['platform'],
      properties: {
        platform: { type: 'string', enum: ['node', 'cloudflare-workers', 'koyeb', 'docker', 'unknown'] },
        port: { type: 'number', format: 'integer' },
        base_url: { type: 'string' },
      },
      additionalProperties: true,
    },
    logging: {
      type: 'object',
      properties: { level: { type: 'string', enum: ['debug', 'info', 'warn', 'error'] } },
    },
    routing: {
      type: 'object',
      properties: {
        loadBalancer: { type: 'object', properties: { strategy: { type: 'string' } }, additionalProperties: true },
        circuitBreaker: { type: 'object', additionalProperties: true },
        retry: { type: 'object', additionalProperties: true },
      },
      additionalProperties: true,
    },
    servers: {
      type: 'array',
      items: {
        type: 'object',
        required: ['id', 'type', 'auth_strategy', 'config'],
        properties: {
          id: { type: 'string' },
          type: { type: 'string', enum: ['git', 'npm', 'pypi', 'docker', 'local'] },
          url: { type: 'string' },
          package: { type: 'string' },
          version: { type: 'string' },
          branch: { type: 'string' },
          auth_strategy: {
            type: 'string',
            enum: ['master_oauth', 'delegate_oauth', 'bypass_auth', 'proxy_oauth'],
          },
          auth_config: { type: 'object', additionalProperties: true },
          config: {
            type: 'object',
            properties: {
              environment: { type: 'object', additionalProperties: true },
              args: { type: 'array', items: { type: 'string' } },
              port: { type: 'number', format: 'integer' },
            },
            additionalProperties: true,
          },
        },
        additionalProperties: true,
      },
    },
  },
  additionalProperties: true,
}

```

--------------------------------------------------------------------------------
/tests/servers/test-streaming-both-full.js:
--------------------------------------------------------------------------------

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

import { spawn } from 'node:child_process'
import { Client } from '@modelcontextprotocol/sdk/client/index.js'
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'

async function startHttpServer() {
  console.log('Starting HTTP test server...')
  
  // Start the HTTP server as a background process
  const httpServer = spawn('node', ['examples/test-mcp-server.js'], {
    stdio: ['ignore', 'pipe', 'pipe'],
    env: { ...process.env, PORT: '3006' }
  })
  
  // Capture stdout and stderr
  httpServer.stdout.on('data', (data) => {
    console.log(`[HTTP Server] ${data.toString().trim()}`)
  })
  
  httpServer.stderr.on('data', (data) => {
    console.error(`[HTTP Server ERROR] ${data.toString().trim()}`)
  })
  
  // Wait for the server to start
  await new Promise((resolve, reject) => {
    let timeout = setTimeout(() => {
      reject(new Error('HTTP server startup timeout'))
    }, 5000)
    
    httpServer.stdout.on('data', (data) => {
      if (data.toString().includes('Test MCP server listening')) {
        clearTimeout(timeout)
        resolve()
      }
    })
  })
  
  return httpServer
}

async function startMasterServer() {
  console.log('Starting Master MCP server...')
  
  // Start the master server as a background process
  const masterServer = spawn('npm', ['run', 'dev'], {
    stdio: ['ignore', 'pipe', 'pipe'],
    cwd: process.cwd()
  })
  
  // Capture stdout and stderr
  masterServer.stdout.on('data', (data) => {
    const output = data.toString().trim()
    console.log(`[Master Server] ${output}`)
    // Don't log the full output as it's too verbose
  })
  
  masterServer.stderr.on('data', (data) => {
    console.error(`[Master Server ERROR] ${data.toString().trim()}`)
  })
  
  // Wait for the server to start
  await new Promise((resolve, reject) => {
    let timeout = setTimeout(() => {
      reject(new Error('Master server startup timeout'))
    }, 10000)
    
    masterServer.stdout.on('data', (data) => {
      if (data.toString().includes('Master MCP listening')) {
        clearTimeout(timeout)
        resolve()
      }
    })
  })
  
  return masterServer
}

async function runStreamingTest() {
  try {
    console.log('Testing Master MCP Server with HTTP Streaming...')
    
    // Create a streamable HTTP transport to connect to our MCP server
    const transport = new StreamableHTTPClientTransport(new URL('http://localhost:3005/mcp'))
    
    // Create the MCP client
    const client = new Client({
      name: 'master-mcp-streaming-test-client',
      version: '1.0.0'
    })
    
    // Initialize the client
    await client.connect(transport)
    console.log('✅ Server initialized with streaming transport')
    console.log('Server info:', client.getServerVersion())
    console.log('Server capabilities:', client.getServerCapabilities())
    
    // List tools using streaming
    console.log('\n--- Testing tools/list with streaming ---')
    const toolsResult = await client.listTools({})
    console.log('✅ tools/list successful with streaming')
    console.log('Number of tools:', toolsResult.tools.length)
    console.log('Tools:', toolsResult.tools.map(t => t.name))
    
    // List resources using streaming
    console.log('\n--- Testing resources/list with streaming ---')
    const resourcesResult = await client.listResources({})
    console.log('✅ resources/list successful with streaming')
    console.log('Number of resources:', resourcesResult.resources.length)
    console.log('Resources:', resourcesResult.resources.map(r => r.uri))
    
    // Test ping
    console.log('\n--- Testing ping with streaming ---')
    const pingResult = await client.ping()
    console.log('✅ ping successful with streaming')
    console.log('Ping result:', pingResult)
    
    // Try calling a tool from the HTTP server
    console.log('\n--- Testing tool call to HTTP server ---')
    try {
      const httpToolCallResult = await client.callTool({
        name: 'test-server.echo',  // Prefixed with server ID
        arguments: { message: 'Hello from HTTP server!' }
      })
      console.log('✅ HTTP tool call successful')
      console.log('HTTP tool result:', JSON.stringify(httpToolCallResult, null, 2))
    } catch (error) {
      console.log('⚠️ HTTP tool call failed (might not be available):', error.message)
    }
    
    // Try calling a tool from the STDIO server
    console.log('\n--- Testing tool call to STDIO server ---')
    try {
      const stdioToolCallResult = await client.callTool({
        name: 'stdio-server.stdio-echo',  // Prefixed with server ID
        arguments: { message: 'Hello from STDIO server!' }
      })
      console.log('✅ STDIO tool call successful')
      console.log('STDIO tool result:', JSON.stringify(stdioToolCallResult, null, 2))
    } catch (error) {
      console.log('⚠️ STDIO tool call failed (might not be available):', error.message)
    }
    
    // Try reading a resource from the HTTP server
    console.log('\n--- Testing resource read from HTTP server ---')
    try {
      const httpResourceResult = await client.readResource({
        uri: 'test-server.test://example'  // Prefixed with server ID
      })
      console.log('✅ HTTP resource read successful')
      console.log('HTTP resource result:', JSON.stringify(httpResourceResult, null, 2))
    } catch (error) {
      console.log('⚠️ HTTP resource read failed (might not be available):', error.message)
    }
    
    // Try reading a resource from the STDIO server
    console.log('\n--- Testing resource read from STDIO server ---')
    try {
      const stdioResourceResult = await client.readResource({
        uri: 'stdio-server.stdio://example/resource'  // Prefixed with server ID
      })
      console.log('✅ STDIO resource read successful')
      console.log('STDIO resource result:', JSON.stringify(stdioResourceResult, null, 2))
    } catch (error) {
      console.log('⚠️ STDIO resource read failed (might not be available):', error.message)
    }
    
    // Close the connection
    await client.close()
    console.log('\n✅ Disconnected from MCP server')
    console.log('\n🎉 All streaming tests completed successfully!')
    
  } catch (error) {
    console.error('❌ Streaming test failed:', error)
    console.error('Error stack:', error.stack)
  }
}

async function main() {
  let httpServer, masterServer
  
  try {
    // Start the HTTP server
    httpServer = await startHttpServer()
    
    // Start the master server
    masterServer = await startMasterServer()
    
    // Wait a bit for discovery to happen
    console.log('Waiting for server discovery...')
    await new Promise(resolve => setTimeout(resolve, 3000))
    
    // Run the streaming test
    await runStreamingTest()
  } catch (error) {
    console.error('Test failed:', error)
  } finally {
    // Clean up: kill the servers
    if (httpServer) {
      console.log('Stopping HTTP server...')
      httpServer.kill()
    }
    if (masterServer) {
      console.log('Stopping Master server...')
      masterServer.kill()
    }
  }
}

// Run the test
main()
```

--------------------------------------------------------------------------------
/src/modules/stdio-manager.ts:
--------------------------------------------------------------------------------

```typescript
import { spawn, ChildProcess } from 'node:child_process'
import { Logger } from '../utils/logger.js'
import type { ServerProcess } from '../types/server.js'

export class StdioManager {
  private processes = new Map<string, ChildProcess>()
  private responseQueues = new Map<string, Array<{ resolve: (value: any) => void; reject: (reason: any) => void; id: number | string }>>()
  private notificationCallbacks = new Map<string, (message: any) => void>()
  private messageBuffers = new Map<string, string>()

  async startServer(serverId: string, filePath: string, env?: Record<string, string>): Promise<ServerProcess> {
    Logger.info('Starting STDIO server', { serverId, filePath })
    
    return new Promise((resolve, reject) => {
      const timeout = setTimeout(() => {
        reject(new Error(`Timeout starting STDIO server ${serverId}`))
      }, 10000) // 10 second timeout

      try {
        const proc = spawn('node', [filePath], {
          stdio: ['pipe', 'pipe', 'pipe'],
          env: { ...process.env, ...env }
        })

        // Set up event handlers
        proc.stdout?.on('data', (data) => {
          this.handleStdoutData(serverId, data.toString())
        })

        proc.stderr?.on('data', (data) => {
          Logger.warn('STDIO server stderr', { serverId, data: data.toString() })
        })

        proc.on('close', (code) => {
          Logger.info('STDIO server process closed', { serverId, code })
          this.cleanupProcess(serverId, new Error(`STDIO server ${serverId} process closed with code ${code}`))
        })

        proc.on('error', (err) => {
          Logger.error('STDIO server process error', { serverId, error: err })
          clearTimeout(timeout)
          this.rejectPendingRequests(serverId, err)
          reject(err)
        })

        // Check if process started successfully
        proc.on('spawn', () => {
          clearTimeout(timeout)
          this.processes.set(serverId, proc)
          this.responseQueues.set(serverId, [])
          this.messageBuffers.set(serverId, '')

          resolve({
            pid: proc.pid,
            stop: async () => {
              return new Promise((resolve) => {
                if (proc.connected) {
                  proc.kill()
                  setTimeout(() => resolve(), 1000) // Wait 1 second for graceful shutdown
                } else {
                  resolve()
                }
              })
            }
          })
        })
      } catch (err) {
        clearTimeout(timeout)
        reject(err)
      }
    })
  }

  private handleStdoutData(serverId: string, data: string) {
    const buffer = this.messageBuffers.get(serverId) || ''
    const newBuffer = buffer + data
    
    // Try to parse complete JSON messages
    let remainingBuffer = newBuffer
    while (remainingBuffer.trim().startsWith('{')) {
      try {
        // Try to parse as JSON
        const trimmed = remainingBuffer.trim()
        let endIndex = 1
        let braceCount = 1
        
        // Find the matching closing brace
        for (let i = 1; i < trimmed.length; i++) {
          if (trimmed[i] === '{') {
            braceCount++
          } else if (trimmed[i] === '}') {
            braceCount--
            if (braceCount === 0) {
              endIndex = i + 1
              break
            }
          }
        }
        
        if (braceCount === 0) {
          // Found complete JSON object
          const jsonString = trimmed.substring(0, endIndex)
          const message = JSON.parse(jsonString)
          
          // Process the message
          this.processMessage(serverId, message)
          
          // Update buffer to remaining data
          remainingBuffer = trimmed.substring(endIndex)
          // Skip any whitespace after the JSON object
          remainingBuffer = remainingBuffer.replace(/^\\s+/, '')
        } else {
          // Incomplete JSON, wait for more data
          break
        }
      } catch (err) {
        // Incomplete JSON or parsing error, wait for more data
        break
      }
    }
    
    this.messageBuffers.set(serverId, remainingBuffer)
  }

  public onNotification(serverId: string, callback: (message: any) => void) {
    this.notificationCallbacks.set(serverId, callback)
  }

  private processMessage(serverId: string, message: any) {
    Logger.debug('Received message from STDIO server', { serverId, message })

    // Check if this is a response to a pending request
    if (message.id !== undefined) {
      const queue = this.responseQueues.get(serverId)
      if (queue) {
        const index = queue.findIndex((item) => item.id === message.id)
        if (index !== -1) {
          const { resolve } = queue.splice(index, 1)[0]
          resolve(message)
          return
        }
      }
    }

    // Handle notifications (no id) or unmatched responses
    const callback = this.notificationCallbacks.get(serverId)
    if (callback) {
      try {
        callback(message)
      } catch (err) {
        Logger.error('Error in notification callback', { serverId, error: err })
      }
    } else {
      Logger.debug('Received notification or unmatched response from STDIO server, but no callback registered', { serverId, message })
    }
  }

  async sendMessage(serverId: string, message: any): Promise<void> {
    const proc = this.processes.get(serverId)
    if (!proc || !proc.stdin) {
      throw new Error(`STDIO server ${serverId} not found or not connected`)
    }

    return new Promise((resolve, reject) => {
      const messageStr = JSON.stringify(message) + '\n'
      proc.stdin?.write(messageStr, (err) => {
        if (err) reject(err)
        else resolve()
      })
    })
  }

  async waitForResponse(serverId: string, messageId: number | string, timeoutMs = 30000): Promise<any> {
    const proc = this.processes.get(serverId)
    if (!proc) {
      throw new Error(`STDIO server ${serverId} not found`)
    }

    return new Promise((resolve, reject) => {
      const timeout = setTimeout(() => {
        // Remove the pending request from the queue
        const queue = this.responseQueues.get(serverId) || []
        const index = queue.findIndex(item => item.id === messageId)
        if (index !== -1) {
          queue.splice(index, 1)
        }
        reject(new Error(`Timeout waiting for response from STDIO server ${serverId} for message ${messageId}`))
      }, timeoutMs)

      // Add to response queue
      const queue = this.responseQueues.get(serverId) || []
      queue.push({
        id: messageId,
        resolve: (value: any) => {
          clearTimeout(timeout)
          resolve(value)
        },
        reject: (reason: any) => {
          clearTimeout(timeout)
          reject(reason)
        }
      })
      this.responseQueues.set(serverId, queue)
    })
  }

  private rejectPendingRequests(serverId: string, error: any) {
    const queue = this.responseQueues.get(serverId)
    if (queue) {
      while (queue.length > 0) {
        const { reject } = queue.shift()!
        reject(error)
      }
    }
  }

  private cleanupProcess(serverId: string, error?: any) {
    this.rejectPendingRequests(serverId, error || new Error(`STDIO server ${serverId} process closed`))
    this.processes.delete(serverId)
    this.responseQueues.delete(serverId)
    this.messageBuffers.delete(serverId)
    this.notificationCallbacks.delete(serverId)
  }
}
```

--------------------------------------------------------------------------------
/src/utils/crypto.ts:
--------------------------------------------------------------------------------

```typescript
import {
  createCipheriv,
  createDecipheriv,
  createHash,
  createHmac,
  randomBytes,
  randomUUID as nodeRandomUUID,
  timingSafeEqual,
  pbkdf2Sync,
  scryptSync,
  hkdfSync,
} from 'node:crypto'

const IV_LENGTH = 12 // AES-GCM recommended 12 bytes
const AUTH_TAG_LENGTH = 16

function deriveKey(key: string | Buffer): Buffer {
  return Buffer.isBuffer(key) ? createHash('sha256').update(key).digest() : createHash('sha256').update(Buffer.from(key)).digest()
}

function b64(input: ArrayBuffer | Uint8Array): string {
  return Buffer.from(input as any).toString('base64')
}

function fromB64(input: string): Buffer {
  return Buffer.from(input, 'base64')
}

/**
 * Node-focused crypto utilities used by the Master MCP Server runtime.
 * Worker builds exclude this file via tsconfig.worker.json.
 */
export class CryptoUtils {
  /** Encrypts UTF-8 text using AES-256-GCM. Returns base64(iv||tag||ciphertext). */
  static encrypt(data: string, key: string | Buffer): string {
    const iv = randomBytes(IV_LENGTH)
    const cipher = createCipheriv('aes-256-gcm', deriveKey(key), iv)
    const ciphertext = Buffer.concat([cipher.update(data, 'utf8'), cipher.final()])
    const authTag = cipher.getAuthTag()
    return Buffer.concat([iv, authTag, ciphertext]).toString('base64')
  }

  /** Decrypts base64(iv||tag||ciphertext) produced by encrypt(). */
  static decrypt(encryptedData: string, key: string | Buffer): string {
    const raw = Buffer.from(encryptedData, 'base64')
    const iv = raw.subarray(0, IV_LENGTH)
    const authTag = raw.subarray(IV_LENGTH, IV_LENGTH + AUTH_TAG_LENGTH)
    const ciphertext = raw.subarray(IV_LENGTH + AUTH_TAG_LENGTH)
    const decipher = createDecipheriv('aes-256-gcm', deriveKey(key), iv)
    decipher.setAuthTag(authTag)
    const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()])
    return plaintext.toString('utf8')
  }

  /** Secure random bytes as hex string. */
  static generateSecureRandom(length: number): string {
    return randomBytes(length).toString('hex')
  }

  /** Returns RFC4122 v4 UUID using crypto RNG. */
  static uuid(): string {
    return nodeRandomUUID()
  }

  /** SHA-256 digest as hex string. */
  static hash(input: string | Buffer): string {
    return createHash('sha256').update(input).digest('hex')
  }

  /** Constant-time equality check for hex strings produced by hash(). */
  static verify(input: string | Buffer, hash: string): boolean {
    const calculated = Buffer.from(this.hash(input), 'utf8')
    const provided = Buffer.from(hash, 'utf8')
    if (calculated.length !== provided.length) return false
    return timingSafeEqual(calculated, provided)
  }

  /** Derives a key using PBKDF2-HMAC-SHA256. Returns base64 key bytes. */
  static pbkdf2(
    password: string | Buffer,
    salt: string | Buffer,
    iterations = 100_000,
    keyLen = 32,
  ): string {
    const dk = pbkdf2Sync(password, salt, iterations, keyLen, 'sha256')
    return b64(dk)
  }

  /**
   * Hashes password using PBKDF2. Format: pbkdf2$sha256$iter$saltB64$hashB64
   */
  static pbkdf2Hash(password: string, iterations = 100_000, saltLen = 16): string {
    const salt = randomBytes(saltLen)
    const hash = pbkdf2Sync(password, salt, iterations, 32, 'sha256')
    return `pbkdf2$sha256$${iterations}$${b64(salt)}$${b64(hash)}`
  }

  static pbkdf2Verify(password: string, encoded: string): boolean {
    try {
      const [algo, hashName, iterStr, saltB64, hashB64] = encoded.split('$')
      if (algo !== 'pbkdf2' || hashName !== 'sha256') return false
      const iterations = Number(iterStr)
      const salt = fromB64(saltB64)
      const expected = fromB64(hashB64)
      const actual = pbkdf2Sync(password, salt, iterations, expected.length, 'sha256')
      return timingSafeEqual(actual, expected)
    } catch {
      return false
    }
  }

  /**
   * Hashes password using scrypt with defaults N=16384, r=8, p=1.
   * Format: scrypt$N$r$p$saltB64$hashB64
   */
  static scryptHash(password: string, opts?: { N?: number; r?: number; p?: number; saltLen?: number; keyLen?: number }): string {
    const N = opts?.N ?? 16384
    const r = opts?.r ?? 8
    const p = opts?.p ?? 1
    const saltLen = opts?.saltLen ?? 16
    const keyLen = opts?.keyLen ?? 32
    const salt = randomBytes(saltLen)
    const hash = scryptSync(password, salt, keyLen, { N, r, p })
    return `scrypt$${N}$${r}$${p}$${b64(salt)}$${b64(hash)}`
  }

  static scryptVerify(password: string, encoded: string): boolean {
    try {
      const [algo, nStr, rStr, pStr, saltB64, hashB64] = encoded.split('$')
      if (algo !== 'scrypt') return false
      const N = Number(nStr)
      const r = Number(rStr)
      const p = Number(pStr)
      const salt = fromB64(saltB64)
      const expected = fromB64(hashB64)
      const actual = scryptSync(password, salt, expected.length, { N, r, p })
      return timingSafeEqual(actual, expected)
    } catch {
      return false
    }
  }

  /**
   * Attempts bcrypt via optional dependency. If unavailable, falls back to scrypt
   * and encodes using the scrypt$... scheme. This ensures secure hashing without
   * adding runtime deps.
   */
  static async bcryptHash(password: string, rounds = 12): Promise<string> {
    try {
      // Attempt to use optional bcrypt packages if present via dynamic import
      const mod = await dynamicImportAny(['bcrypt', 'bcryptjs'])
      if (mod?.hash) return await mod.hash(password, rounds)
    } catch {
      // ignore and fallback
    }
    // Fallback to scrypt
    return this.scryptHash(password)
  }

  static async bcryptVerify(password: string, encoded: string): Promise<boolean> {
    // If it looks like a bcrypt hash, try optional bcrypt packages
    if (encoded.startsWith('$2a$') || encoded.startsWith('$2b$') || encoded.startsWith('$2y$')) {
      try {
        const mod = await dynamicImportAny(['bcrypt', 'bcryptjs'])
        if (mod?.compare) return await mod.compare(password, encoded)
      } catch {
        // ignore and fallback
      }
      return false
    }
    // Otherwise, support scrypt fallback
    if (encoded.startsWith('scrypt$')) return this.scryptVerify(password, encoded)
    if (encoded.startsWith('pbkdf2$')) return this.pbkdf2Verify(password, encoded)
    return false
  }

  /** HKDF with SHA-256. Returns base64 key bytes. */
  static hkdf(ikm: string | Buffer, salt: string | Buffer, info: string | Buffer, length = 32): string {
    try {
      const okm = hkdfSync('sha256', ikm, salt, info, length)
      return b64(okm)
    } catch {
      // Fallback manual HKDF implementation (RFC 5869)
      const prk = createHmac('sha256', salt as any).update(ikm as any).digest()
      const n = Math.ceil(length / 32)
      const t: any[] = []
      let prev: any = Buffer.alloc(0)
      for (let i = 0; i < n; i++) {
        prev = createHmac('sha256', prk as any)
          .update(Buffer.concat([prev, Buffer.from(info as any), Buffer.from([i + 1])]) as any)
          .digest() as Buffer
        t.push(prev)
      }
      return b64(Buffer.concat(t).subarray(0, length))
    }
  }
}

async function dynamicImportAny(modules: string[]): Promise<any | null> {
  for (const m of modules) {
    try {
      // Avoid triggering TS module resolution by computing the specifier
      const importer = new Function('m', 'return import(m)') as (m: string) => Promise<any>
      const mod = await importer(m)
      if (mod) return mod.default ?? mod
    } catch {
      // continue
    }
  }
  return null
}

```

--------------------------------------------------------------------------------
/src/auth/oauth-providers.ts:
--------------------------------------------------------------------------------

```typescript
import fetch from 'node-fetch'
import { createRemoteJWKSet, decodeJwt, jwtVerify } from 'jose'
import type { OAuthToken, TokenValidationResult, UserInfo } from '../types/auth.js'
import type { ServerAuthConfig } from '../types/config.js'
import { Logger } from '../utils/logger.js'

export interface OAuthProvider {
  validateToken(token: string): Promise<TokenValidationResult>
  refreshToken(refreshToken: string): Promise<OAuthToken>
  getUserInfo(token: string): Promise<UserInfo>
}

export class OAuthError extends Error {
  constructor(message: string, public override cause?: unknown) {
    super(message)
    this.name = 'OAuthError'
  }
}

async function postForm(url: string, body: Record<string, string>): Promise<any> {
  const res = await fetch(url, {
    method: 'POST',
    headers: { 'content-type': 'application/x-www-form-urlencoded', accept: 'application/json' },
    body: new URLSearchParams(body).toString(),
  })
  const text = await res.text()
  if (!res.ok) {
    throw new OAuthError(`Token endpoint error ${res.status}: ${text}`)
  }
  try {
    return JSON.parse(text)
  } catch {
    // GitHub may return urlencoded; parse fallback
    return Object.fromEntries(new URLSearchParams(text))
  }
}

function toOAuthToken(json: any): OAuthToken {
  const expiresIn = 'expires_in' in json ? Number(json.expires_in) : 3600
  const scope = Array.isArray(json.scope)
    ? (json.scope as string[])
    : typeof json.scope === 'string'
      ? (json.scope as string).split(/[ ,]+/).filter(Boolean)
      : []
  return {
    access_token: String(json.access_token),
    refresh_token: json.refresh_token ? String(json.refresh_token) : undefined,
    expires_at: Date.now() + expiresIn * 1000,
    scope,
  }
}

export class GitHubOAuthProvider implements OAuthProvider {
  constructor(private readonly config: ServerAuthConfig) {}

  async validateToken(token: string): Promise<TokenValidationResult> {
    try {
      const res = await fetch('https://api.github.com/user', {
        headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' },
      })
      if (!res.ok) {
        const text = await res.text()
        return { valid: false, error: `GitHub token invalid: ${res.status} ${text}` }
      }
      const scopesHeader = res.headers.get('x-oauth-scopes')
      const scopes = scopesHeader ? scopesHeader.split(',').map((s) => s.trim()).filter(Boolean) : undefined
      return { valid: true, scopes }
    } catch (err) {
      Logger.error('GitHub validateToken failed', err)
      return { valid: false, error: String(err) }
    }
  }

  async refreshToken(refreshToken: string): Promise<OAuthToken> {
    const json = await postForm(this.config.token_endpoint, {
      grant_type: 'refresh_token',
      refresh_token: refreshToken,
      client_id: this.config.client_id,
      ...(this.config.client_secret ? { client_secret: String(this.config.client_secret) } : {}),
    })
    return toOAuthToken(json)
  }

  async getUserInfo(token: string): Promise<UserInfo> {
    const res = await fetch('https://api.github.com/user', {
      headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' },
    })
    if (!res.ok) throw new OAuthError(`GitHub userinfo failed: ${res.status}`)
    const json = (await res.json()) as any
    return { id: String(json.id), name: json.name ?? undefined, email: json.email ?? undefined, avatarUrl: json.avatar_url ?? undefined }
  }
}

export class GoogleOAuthProvider implements OAuthProvider {
  private jwks = createRemoteJWKSet(new URL('https://www.googleapis.com/oauth2/v3/certs'))

  constructor(private readonly config: ServerAuthConfig) {}

  async validateToken(token: string): Promise<TokenValidationResult> {
    // Try as JWT (id_token); fallback to userinfo call for access_token
    try {
      const { payload } = await jwtVerify(token, this.jwks, {
        issuer: ['https://accounts.google.com', 'accounts.google.com'],
        audience: this.config.client_id ? String(this.config.client_id) : undefined,
      })
      const scopes = typeof payload.scope === 'string' ? payload.scope.split(' ') : undefined
      const exp = typeof payload.exp === 'number' ? payload.exp * 1000 : undefined
      return { valid: true, expiresAt: exp, scopes }
    } catch (_e) {
      // Not a valid id_token; try userinfo endpoint to validate access token
      try {
        const res = await fetch('https://openidconnect.googleapis.com/v1/userinfo', {
          headers: { Authorization: `Bearer ${token}` },
        })
        if (!res.ok) return { valid: false, error: `Google userinfo status ${res.status}` }
        return { valid: true }
      } catch (err) {
        return { valid: false, error: String(err) }
      }
    }
  }

  async refreshToken(refreshToken: string): Promise<OAuthToken> {
    const json = await postForm(this.config.token_endpoint, {
      grant_type: 'refresh_token',
      refresh_token: refreshToken,
      client_id: this.config.client_id,
      ...(this.config.client_secret ? { client_secret: String(this.config.client_secret) } : {}),
    })
    return toOAuthToken(json)
  }

  async getUserInfo(token: string): Promise<UserInfo> {
    const res = await fetch('https://openidconnect.googleapis.com/v1/userinfo', {
      headers: { Authorization: `Bearer ${token}` },
    })
    if (!res.ok) throw new OAuthError(`Google userinfo failed: ${res.status}`)
    const json = (await res.json()) as any
    return { id: String(json.sub), name: json.name, email: json.email, avatarUrl: json.picture }
  }
}

export class CustomOAuthProvider implements OAuthProvider {
  private jwks?: ReturnType<typeof createRemoteJWKSet>
  constructor(private readonly config: ServerAuthConfig & { jwks_uri?: string; issuer?: string; audience?: string }) {
    if (this.config['jwks_uri']) {
      this.jwks = createRemoteJWKSet(new URL(String(this.config['jwks_uri'])))
    }
  }

  async validateToken(token: string): Promise<TokenValidationResult> {
    // Prefer JWT validation if JWKS is provided, else try userinfo proxy via resource endpoint if configured
    if (this.jwks) {
      try {
        const { payload } = await jwtVerify(token, this.jwks, {
          issuer: this.config['issuer'] ? String(this.config['issuer']) : undefined,
          audience: this.config['audience'] ? String(this.config['audience']) : undefined,
        })
        const exp = typeof payload.exp === 'number' ? payload.exp * 1000 : undefined
        const scopes = typeof payload.scope === 'string' ? payload.scope.split(/[ ,]+/) : undefined
        return { valid: true, expiresAt: exp, scopes }
      } catch (err) {
        return { valid: false, error: String(err) }
      }
    }
    // As a generic fallback, we can't validate without provider-specific endpoint; treat as opaque Bearer
    try {
      decodeJwt(token) // will throw if not a JWT; but opaque tokens are allowed; just return valid unknown
      return { valid: true }
    } catch {
      return { valid: true } // opaque non-JWT tokens assumed valid at this layer
    }
  }

  async refreshToken(refreshToken: string): Promise<OAuthToken> {
    const json = await postForm(this.config.token_endpoint, {
      grant_type: 'refresh_token',
      refresh_token: refreshToken,
      client_id: this.config.client_id,
      ...(this.config.client_secret ? { client_secret: String(this.config.client_secret) } : {}),
    })
    return toOAuthToken(json)
  }

  async getUserInfo(token: string): Promise<UserInfo> {
    // Generic OIDC userinfo often available at `${issuer}/userinfo`; but we only have authorization/token endpoints here.
    const issuer = (this.config as any).issuer as string | undefined
    if (!issuer) throw new OAuthError('userinfo endpoint unknown for custom provider (missing issuer)')
    const url = new URL('/userinfo', issuer).toString()
    const res = await fetch(url, { headers: { Authorization: `Bearer ${token}` } })
    if (!res.ok) throw new OAuthError(`Custom OIDC userinfo failed: ${res.status}`)
    const json = (await res.json()) as any
    return { id: String(json.sub ?? json.id ?? 'unknown'), ...json }
  }
}

export function getOAuthProvider(config: ServerAuthConfig & { jwks_uri?: string; issuer?: string; audience?: string }): OAuthProvider {
  switch (config.provider) {
    case 'github':
      return new GitHubOAuthProvider(config)
    case 'google':
      return new GoogleOAuthProvider(config)
    default:
      return new CustomOAuthProvider(config)
  }
}

```

--------------------------------------------------------------------------------
/src/modules/capability-aggregator.ts:
--------------------------------------------------------------------------------

```typescript
import type { LoadedServer, ServerCapabilities } from '../types/server.js'
import type { ListResourcesResult, ListToolsResult, ToolDefinition, ResourceDefinition, PromptDefinition } from '../types/mcp.js'
import type { AuthHeaders } from '../types/auth.js'
import { Logger } from '../utils/logger.js'

export interface AggregatorOptions {
  prefixStrategy?: 'serverId' | 'none'
  // base path for discovery relative to server endpoint
  capabilitiesEndpoint?: string // default '/capabilities'
  toolsEndpoint?: string // default '/mcp/tools/list'
  resourcesEndpoint?: string // default '/mcp/resources/list'
}

export interface CapabilityMapEntry {
  serverId: string
  originalName: string
}

export class CapabilityAggregator {
  private readonly options: Required<AggregatorOptions>
  private toolMap = new Map<string, CapabilityMapEntry>()
  private resourceMap = new Map<string, CapabilityMapEntry>()

  constructor(options?: AggregatorOptions) {
    this.options = {
      prefixStrategy: options?.prefixStrategy ?? 'serverId',
      capabilitiesEndpoint: options?.capabilitiesEndpoint ?? '/capabilities',
      toolsEndpoint: options?.toolsEndpoint ?? '/mcp/tools/list',
      resourcesEndpoint: options?.resourcesEndpoint ?? '/mcp/resources/list',
    }
  }

  reset(): void {
    this.toolMap.clear()
    this.resourceMap.clear()
  }

  getMappingForTool(aggregatedName: string): CapabilityMapEntry | undefined {
    return this.toolMap.get(aggregatedName)
  }

  getMappingForResource(aggregatedUri: string): CapabilityMapEntry | undefined {
    return this.resourceMap.get(aggregatedUri)
  }

  async discoverCapabilities(
    servers: Map<string, LoadedServer>,
    clientToken?: string,
    getAuthHeaders?: (serverId: string, clientToken?: string) => Promise<AuthHeaders | undefined>
  ): Promise<void> {
    Logger.info('Discovering capabilities', { servers: Array.from(servers.entries()) })
    this.reset()
    const fallbackHeaders: AuthHeaders = {}
    if (clientToken) fallbackHeaders['Authorization'] = `Bearer ${clientToken}`

    await Promise.all(
      Array.from(servers.values()).map(async (server) => {
        if (!server.endpoint || server.endpoint === 'unknown') {
          // For STDIO servers, we need a different approach
          if (server.type === 'stdio' && server.config.url?.startsWith('file://')) {
            try {
              Logger.info('Discovering capabilities for STDIO server', { serverId: server.id })
              // Import the STDIO capability discovery module
              const { StdioCapabilityDiscovery } = await import('./stdio-capability-discovery.js')
              const stdioDiscovery = new StdioCapabilityDiscovery()
              
              const filePath = server.config.url.replace('file://', '')
              const caps = await stdioDiscovery.discoverCapabilities(server.id, filePath)
              Logger.info('Fetched capabilities for STDIO server', { serverId: server.id, caps })
              server.capabilities = caps
              this.index(server.id, caps)
              Logger.logServerEvent('capabilities_discovered', server.id, {
                tools: caps.tools.length,
                resources: caps.resources.length,
                prompts: caps.prompts?.length ?? 0,
              })
            } catch (err) {
              Logger.warn(`Failed capability discovery for STDIO server ${server.id}`, err)
            }
          } else {
            Logger.warn(`Skipping server with unknown endpoint`, { serverId: server.id, type: server.type })
          }
          return
        }
        try {
          Logger.info('Fetching capabilities for server', { serverId: server.id, endpoint: server.endpoint })
          const headers = (await getAuthHeaders?.(server.id, clientToken)) ?? fallbackHeaders
          const caps = await this.fetchCapabilities(server.endpoint, headers)
          Logger.info('Fetched capabilities for server', { serverId: server.id, caps })
          server.capabilities = caps
          this.index(server.id, caps)
          Logger.logServerEvent('capabilities_discovered', server.id, {
            tools: caps.tools.length,
            resources: caps.resources.length,
            prompts: caps.prompts?.length ?? 0,
          })
        } catch (err) {
          Logger.warn(`Failed capability discovery for ${server.id}`, err)
        }
      })
    )
  }

  getAllTools(servers: Map<string, LoadedServer>): ToolDefinition[] {
    const result: ToolDefinition[] = []
    for (const server of servers.values()) {
      const tools = server.capabilities?.tools ?? []
      for (const t of tools) {
        const name = this.aggregateName(server.id, t.name)
        result.push({ ...t, name })
      }
    }
    return result
  }

  getAllResources(servers: Map<string, LoadedServer>): ResourceDefinition[] {
    const result: ResourceDefinition[] = []
    for (const server of servers.values()) {
      const resources = server.capabilities?.resources ?? []
      for (const r of resources) {
        const uri = this.aggregateName(server.id, r.uri)
        result.push({ ...r, uri })
      }
    }
    return result
  }

  aggregate(servers: LoadedServer[]): ServerCapabilities {
    const tools = servers.flatMap((s) => (s.capabilities?.tools ?? []).map((t) => ({ ...t, name: this.aggregateName(s.id, t.name) })))
    const resources = servers.flatMap((s) => (s.capabilities?.resources ?? []).map((r) => ({ ...r, uri: this.aggregateName(s.id, r.uri) })))
    const prompts = servers.flatMap((s) => s.capabilities?.prompts ?? [])
    return { tools, resources, prompts: prompts.length ? prompts : undefined }
  }

  // --- internals ---
  private index(serverId: string, caps: ServerCapabilities): void {
    for (const t of caps.tools) this.toolMap.set(this.aggregateName(serverId, t.name), { serverId, originalName: t.name })
    for (const r of caps.resources) this.resourceMap.set(this.aggregateName(serverId, r.uri), { serverId, originalName: r.uri })
  }

  private aggregateName(serverId: string, name: string): string {
    if (this.options.prefixStrategy === 'none') return name
    return `${serverId}.${name}`
  }

  private ensureTrailingSlash(endpoint: string): string {
    return endpoint.endsWith('/') ? endpoint : `${endpoint}/`
  }

  private async fetchCapabilities(endpoint: string, headers: AuthHeaders): Promise<ServerCapabilities> {
    const urlCap = new URL(this.options.capabilitiesEndpoint, this.ensureTrailingSlash(endpoint)).toString()
    Logger.info('Fetching capabilities from endpoint', { urlCap, headers })
    try {
      const res = await fetch(urlCap, { headers })
      if (res.ok) {
        const json = (await res.json()) as any
        // Try to coerce shapes
        const tools: ToolDefinition[] = Array.isArray(json.tools) ? json.tools : (json.capabilities?.tools ?? [])
        const resources: ResourceDefinition[] = Array.isArray(json.resources) ? json.resources : (json.capabilities?.resources ?? [])
        const prompts: PromptDefinition[] | undefined = Array.isArray(json.prompts) ? json.prompts : (json.capabilities?.prompts ?? undefined)
        Logger.info('Fetched capabilities', { tools, resources, prompts })
        return { tools, resources, prompts }
      }
    } catch (err) {
      Logger.debug('Direct capabilities endpoint failed, trying fallbacks', err)
    }

    // Fallback: fetch tools and resources separately
    const [tools, resources] = await Promise.all([this.fetchTools(endpoint, headers), this.fetchResources(endpoint, headers)])
    Logger.info('Fetched capabilities using fallbacks', { tools, resources })
    return { tools, resources }
  }

  private async fetchTools(endpoint: string, headers: AuthHeaders): Promise<ToolDefinition[]> {
    const url = new URL(this.options.toolsEndpoint, this.ensureTrailingSlash(endpoint)).toString()
    try {
      const res = await fetch(url, { headers })
      if (res.ok) {
        const json = (await res.json()) as ListToolsResult
        return json.tools ?? []
      }
    } catch (err) {
      Logger.warn('fetchTools failed', err)
    }
    return []
  }

  private async fetchResources(endpoint: string, headers: AuthHeaders): Promise<ResourceDefinition[]> {
    const url = new URL(this.options.resourcesEndpoint, this.ensureTrailingSlash(endpoint)).toString()
    try {
      const res = await fetch(url, { headers })
      if (res.ok) {
        const json = (await res.json()) as ListResourcesResult
        return json.resources ?? []
      }
    } catch (err) {
      Logger.warn('fetchResources failed', err)
    }
    return []
  }
}

```

--------------------------------------------------------------------------------
/reports/claude_report_20250815_222153.html:
--------------------------------------------------------------------------------

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Claude Usage Report - Jul 16 to Aug 15, 2025</title>
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body { 
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            line-height: 1.6;
            color: #333;
            background: #f5f5f5;
        }
        .container { max-width: 1200px; margin: 0 auto; padding: 20px; }
        .header {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            padding: 40px;
            border-radius: 10px;
            margin-bottom: 30px;
        }
        .header h1 { font-size: 2.5em; margin-bottom: 10px; }
        .header .subtitle { opacity: 0.9; }
        .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px; margin-bottom: 30px; }
        .card {
            background: white;
            border-radius: 10px;
            padding: 20px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }
        .card h2 { 
            font-size: 0.9em;
            text-transform: uppercase;
            color: #666;
            margin-bottom: 10px;
        }
        .card .value { 
            font-size: 2em;
            font-weight: bold;
            color: #333;
        }
        .card .subtitle {
            font-size: 0.9em;
            color: #999;
            margin-top: 5px;
        }
        .chart-container {
            background: white;
            border-radius: 10px;
            padding: 20px;
            margin-bottom: 20px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }
        .chart-container h2 {
            margin-bottom: 20px;
            color: #333;
        }
        .insights {
            background: white;
            border-radius: 10px;
            padding: 20px;
            margin-bottom: 20px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }
        .insights h2 { margin-bottom: 15px; }
        .insights ul { list-style: none; }
        .insights li {
            padding: 10px;
            margin-bottom: 10px;
            border-left: 3px solid #667eea;
            background: #f8f9fa;
        }
        .warning { border-left-color: #f59e0b !important; background: #fef3c7 !important; }
        .opportunity { border-left-color: #10b981 !important; background: #d1fae5 !important; }
        table {
            width: 100%;
            border-collapse: collapse;
            margin-top: 20px;
        }
        th, td {
            padding: 12px;
            text-align: left;
            border-bottom: 1px solid #e5e5e5;
        }
        th {
            background: #f8f9fa;
            font-weight: 600;
        }
        .footer {
            text-align: center;
            margin-top: 40px;
            padding: 20px;
            color: #666;
            font-size: 0.9em;
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>Claude Usage Report - Jul 16 to Aug 15, 2025</h1>
            <div class="subtitle">
                Jul 16, 2025 - 
                Aug 15, 2025
            </div>
        </div>

        <div class="grid">
            <div class="card">
                <h2>Total Cost</h2>
                <div class="value">$4175.02</div>
                <div class="subtitle">Average $139.17/day</div>
            </div>
            <div class="card">
                <h2>Total Tokens</h2>
                <div class="value">2835.19M</div>
                <div class="subtitle">28919 requests</div>
            </div>
            <div class="card">
                <h2>Cache Efficiency</h2>
                <div class="value">9982.6%</div>
                <div class="subtitle">Saved $18780.13</div>
            </div>
            <div class="card">
                <h2>Burn Rate</h2>
                <div class="value">$5.81/hr</div>
                <div class="subtitle">UNKNOWN</div>
            </div>
        </div>

        
        <div class="chart-container">
            <h2>Daily Usage Trend</h2>
            <canvas id="dailyChart"></canvas>
        </div>

        <div class="chart-container">
            <h2>Model Distribution</h2>
            <canvas id="modelChart"></canvas>
        </div>
    

        <div class="insights">
            <h2>Insights & Recommendations</h2>
            
            
                <h3 style="color: #667eea; margin: 20px 0 10px;">💡 Recommendations</h3>
                <ul>
                    <li>Consider using Claude Haiku for simpler tasks to reduce costs</li><li>Implement request batching to reduce API calls</li>
                </ul>
            
            
                <h3 style="color: #10b981; margin: 20px 0 10px;">✨ Opportunities</h3>
                <ul>
                    <li class="opportunity">Excellent cache efficiency (9982.6%) - saving $18780.13</li>
                </ul>
            
        </div>

        <div class="chart-container">
            <h2>Model Usage</h2>
            <table>
                <thead>
                    <tr>
                        <th>Model</th>
                        <th>Requests</th>
                        <th>Total Tokens</th>
                        <th>Total Cost</th>
                        <th>Cache Hit Rate</th>
                    </tr>
                </thead>
                <tbody>
                    
                        <tr>
                            <td>claude-opus-4-1-20250805</td>
                            <td>6,629</td>
                            <td>673.84M</td>
                            <td>$1870.10</td>
                            <td>95.0%</td>
                        </tr>
                    
                        <tr>
                            <td>claude-opus-4-20250514</td>
                            <td>5,906</td>
                            <td>494.37M</td>
                            <td>$1464.34</td>
                            <td>94.4%</td>
                        </tr>
                    
                        <tr>
                            <td>claude-sonnet-4-20250514</td>
                            <td>16,384</td>
                            <td>1666.97M</td>
                            <td>$840.58</td>
                            <td>94.6%</td>
                        </tr>
                    
                </tbody>
            </table>
        </div>

        <div class="footer">
            Generated on Aug 15, 2025 22:21 • 
            Ouroboros Claude Monitoring System v1.0.0
        </div>
    </div>

    
    <script>
        // Daily usage chart
        const dailyCtx = document.getElementById('dailyChart').getContext('2d');
        new Chart(dailyCtx, {
            type: 'line',
            data: {
                labels: ["Jul 16","Jul 17","Jul 18","Jul 19","Jul 21","Jul 23","Jul 24","Jul 27","Jul 28","Aug 3","Aug 4","Aug 5","Aug 6","Aug 7","Aug 8","Aug 9","Aug 10","Aug 11","Aug 12","Aug 13","Aug 14","Aug 15"],
                datasets: [{
                    label: 'Daily Cost ($)',
                    data: ["13.96","174.73","116.18","3.61","166.24","81.62","95.45","3.53","78.99","108.26","544.52","10.80","193.35","549.84","300.48","583.22","348.56","28.68","233.65","455.15","6.09","78.10"],
                    borderColor: '#667eea',
                    backgroundColor: 'rgba(102, 126, 234, 0.1)',
                    tension: 0.4
                }]
            },
            options: {
                responsive: true,
                plugins: {
                    legend: { display: false }
                }
            }
        });

        // Model distribution chart
        const modelCtx = document.getElementById('modelChart').getContext('2d');
        new Chart(modelCtx, {
            type: 'doughnut',
            data: {
                labels: ["opus-4","opus-4","sonnet-4"],
                datasets: [{
                    data: ["1870.10","1464.34","840.58"],
                    backgroundColor: [
                        '#667eea',
                        '#764ba2',
                        '#f59e0b',
                        '#10b981',
                        '#ef4444'
                    ]
                }]
            },
            options: {
                responsive: true,
                plugins: {
                    legend: { position: 'right' }
                }
            }
        });
    </script>
    
</body>
</html>
```

--------------------------------------------------------------------------------
/src/modules/request-router.ts:
--------------------------------------------------------------------------------

```typescript
import type {
  CallToolRequest,
  CallToolResult,
  ListResourcesRequest,
  ListResourcesResult,
  ListToolsRequest,
  ListToolsResult,
  ReadResourceRequest,
  ReadResourceResult,
  SubscribeRequest,
  SubscribeResult,
} from '../types/mcp.js'
import type { LoadedServer } from '../types/server.js'
import type { AuthHeaders, OAuthDelegation } from '../types/auth.js'
import { CapabilityAggregator } from './capability-aggregator.js'
import { Logger } from '../utils/logger.js'
import { CircuitBreaker } from '../routing/circuit-breaker.js'
import { LoadBalancer } from '../routing/load-balancer.js'
import { RouteRegistry } from '../routing/route-registry.js'
import { RetryHandler } from '../routing/retry-handler.js'
import type { RoutingConfig } from '../types/config.js'

export interface RouterOptions {
  callToolEndpoint?: string // default '/mcp/tools/call'
  readResourceEndpoint?: string // default '/mcp/resources/read'
  routing?: RoutingConfig
}

export interface RouterOptions {
  callToolEndpoint?: string // default '/mcp/tools/call'
  readResourceEndpoint?: string // default '/mcp/resources/read'
  routing?: RoutingConfig
}

export class RequestRouter {
  private readonly options: Required<Omit<RouterOptions, 'routing'>> & { routing: RoutingConfig }
  private readonly circuit: CircuitBreaker
  private readonly retry: RetryHandler
  private readonly lb: LoadBalancer
  private readonly registry: RouteRegistry

  constructor(
    private readonly servers: Map<string, LoadedServer>,
    private readonly aggregator: CapabilityAggregator,
    private readonly getAuthHeaders?: (
      serverId: string,
      clientToken?: string
    ) => Promise<AuthHeaders | OAuthDelegation | undefined>,
    options?: RouterOptions
  ) {
    this.options = {
      callToolEndpoint: options?.callToolEndpoint ?? '/mcp/tools/call',
      readResourceEndpoint: options?.readResourceEndpoint ?? '/mcp/resources/read',
      routing: options?.routing ?? {},
    }
    this.circuit = new CircuitBreaker({
      failureThreshold: this.options.routing.circuitBreaker?.failureThreshold ?? 5,
      successThreshold: this.options.routing.circuitBreaker?.successThreshold ?? 2,
      recoveryTimeoutMs: this.options.routing.circuitBreaker?.recoveryTimeoutMs ?? 30_000,
      name: 'request-router',
    })
    this.retry = new RetryHandler({
      maxRetries: this.options.routing.retry?.maxRetries ?? 2,
      baseDelayMs: this.options.routing.retry?.baseDelayMs ?? 250,
      maxDelayMs: this.options.routing.retry?.maxDelayMs ?? 4_000,
      backoffFactor: this.options.routing.retry?.backoffFactor ?? 2,
      jitter: this.options.routing.retry?.jitter ?? 'full',
      retryOn: this.options.routing.retry?.retryOn ?? { networkErrors: true, httpStatusClasses: [5], httpStatuses: [408, 429] },
    })
    this.lb = new LoadBalancer({ strategy: this.options.routing.loadBalancer?.strategy ?? 'round_robin' })
    this.registry = new RouteRegistry(this.servers, this.circuit, this.lb)
  }

  getServers(): Map<string, LoadedServer> {
    return this.servers
  }

  async routeListTools(_req: ListToolsRequest): Promise<ListToolsResult> {
    const tools = this.aggregator.getAllTools(this.servers)
    return { tools }
  }

  async routeCallTool(req: CallToolRequest, clientToken?: string): Promise<CallToolResult> {
    // Resolve mapping via aggregator if available
    const map = this.aggregator.getMappingForTool(req.name)
    const serverId = map?.serverId ?? req.name.split('.')[0]
    const toolName = map?.originalName ?? (req.name.includes('.') ? req.name.split('.').slice(1).join('.') : req.name)

    const server = this.servers.get(serverId)
    if (!server) {
      return { content: { error: `Server ${serverId} not found` }, isError: true }
    }

    // Handle STDIO servers differently
    if (server.type === 'stdio') {
      try {
        Logger.info('Routing call to STDIO server', { serverId, toolName })
        const { StdioCapabilityDiscovery } = await import('./stdio-capability-discovery.js')
        const stdioDiscovery = new StdioCapabilityDiscovery()
        const result = await stdioDiscovery.callTool(serverId, toolName, req.arguments ?? {})
        return result.result || result
      } catch (error) {
        Logger.error('STDIO tool call failed', { serverId, toolName, error })
        return { content: { error: `STDIO tool call failed: ${error}` }, isError: true }
      }
    }

    const resolution = this.registry.resolve(serverId)
    if (!resolution) {
      return { content: { error: `Route not found for tool ${req.name}` }, isError: true }
    }

    const headers: AuthHeaders = { 'content-type': 'application/json' }
    const auth = await this.getAuthHeaders?.(serverId, clientToken)
    if (auth && (auth as OAuthDelegation).type === 'oauth_delegation') {
      return { content: { error: 'OAuth delegation required', details: auth }, isError: true }
    }
    const extra = (auth as AuthHeaders) ?? (clientToken ? { Authorization: `Bearer ${clientToken}` } : {})
    Object.assign(headers, extra)

    const url = new URL(this.options.callToolEndpoint, this.ensureTrailingSlash(resolution.instance.url)).toString()
    const key = `${serverId}::${resolution.instance.id}`

    try {
      const json = await this.circuit.execute(key, async () => {
        const res = await this.fetchWithRetry(url, {
          method: 'POST',
          headers,
          body: JSON.stringify({ name: toolName, arguments: req.arguments ?? {} }),
        })
        return (await res.json()) as CallToolResult
      })
      this.registry.markSuccess(serverId, resolution.instance.id)
      return json
    } catch (err) {
      this.registry.markFailure(serverId, resolution.instance.id)
      Logger.warn('routeCallTool failed', err)
      return { content: { error: String(err) }, isError: true }
    }
  }

  async routeListResources(_req: ListResourcesRequest): Promise<ListResourcesResult> {
    const resources = this.aggregator.getAllResources(this.servers)
    return { resources }
  }

  async routeReadResource(req: ReadResourceRequest, clientToken?: string): Promise<ReadResourceResult> {
    const map = this.aggregator.getMappingForResource(req.uri)
    const serverId = map?.serverId ?? req.uri.split('.')[0]
    const resourceUri = map?.originalName ?? (req.uri.includes('.') ? req.uri.split('.').slice(1).join('.') : req.uri)

    const server = this.servers.get(serverId)
    if (!server) {
      return { contents: `Server ${serverId} not found`, mimeType: 'text/plain' }
    }

    // Handle STDIO servers differently
    if (server.type === 'stdio') {
      try {
        Logger.info('Routing read to STDIO server', { serverId, resourceUri })
        const { StdioCapabilityDiscovery } = await import('./stdio-capability-discovery.js')
        const stdioDiscovery = new StdioCapabilityDiscovery()
        const result = await stdioDiscovery.readResource(serverId, resourceUri)
        return result.result || result
      } catch (error) {
        Logger.error('STDIO resource read failed', { serverId, resourceUri, error })
        return { contents: `STDIO resource read failed: ${error}`, mimeType: 'text/plain' }
      }
    }

    const resolution = this.registry.resolve(serverId)
    if (!resolution) {
      return { contents: `Route not found for resource ${req.uri}`, mimeType: 'text/plain' }
    }

    const headers: AuthHeaders = { 'content-type': 'application/json' }
    const auth = await this.getAuthHeaders?.(serverId, clientToken)
    if (auth && (auth as OAuthDelegation).type === 'oauth_delegation') {
      return { contents: JSON.stringify({ error: 'OAuth delegation required', details: auth }), mimeType: 'application/json' }
    }
    const extra = (auth as AuthHeaders) ?? (clientToken ? { Authorization: `Bearer ${clientToken}` } : {})
    Object.assign(headers, extra)

    const url = new URL(this.options.readResourceEndpoint, this.ensureTrailingSlash(resolution.instance.url)).toString()
    const key = `${serverId}::${resolution.instance.id}`
    try {
      const json = await this.circuit.execute(key, async () => {
        const res = await this.fetchWithRetry(url, { method: 'POST', headers, body: JSON.stringify({ uri: resourceUri }) })
        return (await res.json()) as ReadResourceResult
      })
      this.registry.markSuccess(serverId, resolution.instance.id)
      return json
    } catch (err) {
      this.registry.markFailure(serverId, resolution.instance.id)
      Logger.warn('routeReadResource failed', err)
      return { contents: String(err), mimeType: 'text/plain' }
    }
  }

  async routeSubscribe(_req: SubscribeRequest): Promise<SubscribeResult> {
    // Not implemented yet; aggregation events out of scope here
    return { ok: true }
  }

  private ensureTrailingSlash(endpoint: string): string {
    return endpoint.endsWith('/') ? endpoint : `${endpoint}/`
  }

  private async fetchWithRetry(input: string, init: RequestInit): Promise<Response> {
    return this.retry.execute(async () => {
      const res = await fetch(input, init)
      if (!res.ok) {
        // For retry logic, throw an error carrying status to trigger retry policy
        const err = new Error(`HTTP ${res.status}`) as Error & { status?: number }
        ;(err as any).status = res.status
        throw err
      }
      return res
    }, (ctx) => {
      Logger.debug('Retrying upstream request', ctx)
    })
  }
}

```

--------------------------------------------------------------------------------
/docs/testing/phase-9-testing-architecture.md:
--------------------------------------------------------------------------------

```markdown
# Phase 9 — Comprehensive Testing Architecture

This document specifies the end-to-end testing architecture for the Master MCP Server across Node.js and Cloudflare Workers. It is tailored to this codebase (TypeScript, ESM, strict mode) and builds on Phases 1–8 (auth, module loading, routing, config, OAuth, utils).

## Goals
- Cross-platform: Node 18+ and Cloudflare Workers.
- ESM + TypeScript strict compatibility.
- Leverage existing utilities (logging, validation, monitoring).
- Clear test layering: unit, integration, E2E, plus security and performance.
- Deterministic, isolated, and parallel-friendly test runs.
- CI/CD automation with quality gates and coverage thresholds.

---

## Framework Selection

- Unit/Integration (Node): Vitest
  - Fast, ESM-native, TS-friendly. Built-in mock timers and coverage via V8.
  - Supertest for Express-based HTTP integration of Node runtime (`src/index.ts`).
  - Undici MockAgent (optional) for fetch interception on Node 18, when not spinning stub servers.

- Unit/Integration (Workers): Vitest + Miniflare 3
  - `miniflare` test environment for `Request`/`Response` compatibility.
  - Target `OAuthFlowController.handleRequest` and any worker entrypoints (`src/runtime/worker.ts`).

- E2E (HTTP-level): Vitest test suite using real HTTP listeners
  - Node: start Express via `createServer(true)` and use Supertest/HTTP.
  - Workers: run Miniflare instance or `wrangler dev` in CI as needed.

- Performance: Artillery
  - Simple YAML scenarios to stress authentication and routing endpoints.
  - Separate CI job; can be run locally against dev servers.

- Security: Vitest + fast-check (property-based) + static assertions
  - Fuzz inputs for token parsing, state/PKCE validation, and router input validation.
  - Optional OWASP ZAP baseline in CI (nightly) if desired.

---

## Directory Structure

```
tests/
  unit/
    routing/              # circuit-breaker, retry, load-balancer
    modules/              # aggregator, router (with fetch mocked)
    oauth/                # pkce/state/validator
    utils/                # logger, validation helpers
  integration/
    node/                 # express endpoints, config manager wiring
    workers/              # flow-controller.handleRequest via Miniflare
    oauth/                # callback flow with mock OIDC provider
  e2e/
    node/                 # start full HTTP server and hit /mcp/*, /oauth/*
    workers/              # worker entrypoint end-to-end
  mocks/
    oauth/                # mock OIDC provider (Node + Worker variants)
    mcp/                  # fake MCP backends (capabilities/tools/resources)
    http/                 # undici MockAgent helpers (Node)
  factories/
    configFactory.ts      # MasterConfig, ServerConfig builders
    oauthFactory.ts       # tokens/states/JWKS
    mcpFactory.ts         # tool/resource definitions
  fixtures/
    capabilities.json
    tools.json
    resources.json
  perf/
    artillery/
      auth-routing.yaml   # load scenarios for auth + routing
  security/
    oauth.spec.ts         # PKCE/state/nonce fuzz tests (fast-check)
  _setup/
    vitest.setup.ts       # global hooks, silent logs, fake timers config
    miniflare.setup.ts    # worker env config for tests
  _utils/
    test-server.ts        # ephemeral HTTP servers
    mock-fetch.ts         # Node fetch interception (Undici)
    log-capture.ts        # capture + assert logs
```

Notes:
- The unit layer uses pure module-level tests with fetch mocked or local HTTP stubs.
- Integration tests are black-box at module boundaries (e.g., ProtocolHandler + RequestRouter + mocks).
- E2E spins the real Node server and/or Miniflare worker with realistic mocks for upstream MCP servers and OIDC.

---

## Test Environment Management

- Node vs Workers selection
  - Node-specific Vitest config: `vitest.config.ts` (environment: node)
  - Workers-specific Vitest config: `vitest.worker.config.ts` (environment: miniflare)
  - Test files can explicitly opt into Miniflare with `// @vitest-environment miniflare`.

- Isolation and determinism
  - Use Vitest’s fake timers for retry/circuit tests.
  - Ephemeral HTTP servers: `tests/_utils/test-server.ts` binds to port 0 and auto-closes in `afterEach`.
  - Log capture: disable or capture logs via `Logger.configure({ json: true })` for stable assertions.

- Cross-platform resources
  - OAuth flows invoke real HTTP endpoints; a mock OIDC provider runs as an in-process HTTP server in both Node and Miniflare scenarios (Miniflare tests remain in a Node host context, so spinning a Node HTTP stub is acceptable).
  - Upstream MCP backends emulated by `fake-backend` servers exposing `/capabilities`, `/mcp/tools/*`, `/mcp/resources/*`.

---

## Mock and Stub Architecture

### MCP Protocol Backends
- Fake backend servers respond with deterministic JSON:
  - `GET /capabilities`: lists tools/resources/prompts
  - `POST /mcp/tools/list` and `/mcp/resources/list`: optional fallbacks
  - `POST /mcp/tools/call`: echoes arguments or returns canned results
  - `POST /mcp/resources/read`: returns fixture content
- Supports Bearer tokens to simulate auth propagation from Master.

### OAuth Provider (OIDC) Mock
- Node HTTP server (Express or http module) serving:
  - `/.well-known/openid-configuration`
  - `/authorize`: simulates auth code issuance by redirecting with `code` + `state`
  - `/token`: returns JSON access token, optional refresh, scopes, `expires_in`
  - `/jwks.json`: JWKS for completeness if JOSE validation is later added upstream
- Uses `jose` to generate ephemeral key material and produce signed tokens if needed.
- Configurable via factory helpers to tailor provider metadata per test.

### HTTP Mocking for Node (optional)
- `undici` MockAgent helper when spinning HTTP servers is overkill.
- Route by URL patterns and methods; fallback to network disallowed.

---

## Test Data Management

- Fixtures: JSON payloads for tools/resources/capabilities. Keep small and readable.
- Factories:
  - `configFactory`: produce `MasterConfig` + `ServerConfig` with sensible defaults (ports, endpoints, auth strategies).
  - `oauthFactory`: generate PKCE/state payloads and basic OAuth token shapes.
  - `mcpFactory`: create tool/resource definitions and common requests.
- State/Token Stores:
  - In-memory only; reset across tests.
  - If a persistent DB is introduced later:
    - Node: use SQLite in-memory or Testcontainers in CI; provide cleanup hooks.
    - Workers: use Miniflare KV/D1 bindings with per-test namespaces.

---

## Performance Testing Strategy (Artillery)

- Scenarios:
  - OAuth authorize/token (mock provider) happy-path latency and error rates.
  - Routing: `POST /mcp/tools/call` with mixed success/failure from backends to exercise retry/circuit logic.
  - Server lifecycle: parallel discovery calls to `/capabilities` against multiple backends.
- Metrics:
  - p50/p90/p99 latency, RPS, error rates per route.
  - Custom logs via `Logger.time` around critical paths; scrape from structured logs when running locally.
- CI:
  - Run on a separate “performance” workflow or on nightly schedules to avoid slowing PRs.

---

## Security Testing Architecture

- Property-based testing with `fast-check` for:
  - `FlowValidator.validateReturnTo`: ensure only safe origins/paths pass.
  - `StateManager` and `PKCEManager`: state integrity, one-time consumption, and PKCE verifier binding.
  - Input validation for router requests (e.g., tool/resource names) using `utils/validation` helpers.
- OAuth flow protections:
  - Ensure `state` is required and consumed exactly once.
  - Enforce PKCE method presence, verify rejection on mismatch.
  - Token exchange failure handling and error surface is sanitized.
- Optional dynamic scans:
  - OWASP ZAP baseline against local server in CI (nightly) to catch obvious misconfigurations.

---

## CI/CD Integration

### Jobs
- Lint + Typecheck: ESLint and `tsc -p tsconfig.node.json --noEmit`.
- Unit + Integration (Node): Vitest with coverage.
- Unit + Integration (Workers): Vitest (Miniflare) with coverage.
- E2E: start local server; run black-box tests.
- Security: property tests; optional ZAP baseline (nightly).
- Performance: Artillery (nightly or gated by label).

### Coverage and Quality Gates
- Coverage via Vitest v8 provider; thresholds:
  - Global: `branches: 80%`, `functions: 85%`, `lines: 85%`, `statements: 85%`.
  - Critical modules (routing, oauth): per-file `lines: 90%` target in follow-up.
- Fail PR job if thresholds not met.
- Upload `lcov` or `cobertura` to CI artifacts (or Codecov if desired).

---

## Test Utilities Integration (Phase 8)

- Logger: `Logger.configure({ json: true, level: 'error' })` in test setup; use `log-capture` to assert important events.
- Validation: assert guards (`assertString`, `sanitizeObject`) across boundary tests; fuzz via fast-check.
- Monitoring: wrap performance-critical test paths with `Logger.time` and assert upper bounds during perf runs.

---

## Local and CI Usage

- Local:
  - `vitest -c vitest.config.ts` for Node suites.
  - `vitest -c vitest.worker.config.ts` for Worker suites.
  - `artillery run tests/perf/artillery/auth-routing.yaml` for load tests.

- CI:
  - Run jobs in parallel; collect coverage and artifacts.
  - Gate merges on lint, typecheck, unit/integration, and coverage.

---

## Next Steps (Implementation Guide)
1. Install dev deps: vitest, @vitest/coverage-v8, supertest, miniflare, artillery, fast-check, @types/supertest.
2. Add scripts: `test`, `test:node`, `test:workers`, `test:coverage`, `test:e2e`.
3. Fill factories and mocks with minimal working endpoints.
4. Seed initial critical tests:
   - routing: circuit-breaker/retry/load-balancer
   - oauth: state, pkce, callback error handling
   - aggregator: discovery + mapping
   - protocol: call tool/resource happy path and error path


```

--------------------------------------------------------------------------------
/docs/.vitepress/cache/deps/vitepress___@vueuse_core.js:
--------------------------------------------------------------------------------

```javascript
import {
  DefaultMagicKeysAliasMap,
  StorageSerializers,
  TransitionPresets,
  assert,
  breakpointsAntDesign,
  breakpointsBootstrapV5,
  breakpointsElement,
  breakpointsMasterCss,
  breakpointsPrimeFlex,
  breakpointsQuasar,
  breakpointsSematic,
  breakpointsTailwind,
  breakpointsVuetify,
  breakpointsVuetifyV2,
  breakpointsVuetifyV3,
  bypassFilter,
  camelize,
  clamp,
  cloneFnJSON,
  computedAsync,
  computedEager,
  computedInject,
  computedWithControl,
  containsProp,
  controlledRef,
  createEventHook,
  createFetch,
  createFilterWrapper,
  createGlobalState,
  createInjectionState,
  createRef,
  createReusableTemplate,
  createSharedComposable,
  createSingletonPromise,
  createTemplatePromise,
  createUnrefFn,
  customStorageEventName,
  debounceFilter,
  defaultDocument,
  defaultLocation,
  defaultNavigator,
  defaultWindow,
  executeTransition,
  extendRef,
  formatDate,
  formatTimeAgo,
  get,
  getLifeCycleTarget,
  getSSRHandler,
  hasOwn,
  hyphenate,
  identity,
  increaseWithUnit,
  injectLocal,
  invoke,
  isClient,
  isDef,
  isDefined,
  isIOS,
  isObject,
  isWorker,
  makeDestructurable,
  mapGamepadToXbox360Controller,
  noop,
  normalizeDate,
  notNullish,
  now,
  objectEntries,
  objectOmit,
  objectPick,
  onClickOutside,
  onElementRemoval,
  onKeyDown,
  onKeyPressed,
  onKeyStroke,
  onKeyUp,
  onLongPress,
  onStartTyping,
  pausableFilter,
  promiseTimeout,
  provideLocal,
  provideSSRWidth,
  pxValue,
  rand,
  reactify,
  reactifyObject,
  reactiveComputed,
  reactiveOmit,
  reactivePick,
  refAutoReset,
  refDebounced,
  refDefault,
  refThrottled,
  refWithControl,
  resolveRef,
  resolveUnref,
  set,
  setSSRHandler,
  syncRef,
  syncRefs,
  templateRef,
  throttleFilter,
  timestamp,
  toArray,
  toReactive,
  toRef,
  toRefs,
  toValue,
  tryOnBeforeMount,
  tryOnBeforeUnmount,
  tryOnMounted,
  tryOnScopeDispose,
  tryOnUnmounted,
  unrefElement,
  until,
  useActiveElement,
  useAnimate,
  useArrayDifference,
  useArrayEvery,
  useArrayFilter,
  useArrayFind,
  useArrayFindIndex,
  useArrayFindLast,
  useArrayIncludes,
  useArrayJoin,
  useArrayMap,
  useArrayReduce,
  useArraySome,
  useArrayUnique,
  useAsyncQueue,
  useAsyncState,
  useBase64,
  useBattery,
  useBluetooth,
  useBreakpoints,
  useBroadcastChannel,
  useBrowserLocation,
  useCached,
  useClipboard,
  useClipboardItems,
  useCloned,
  useColorMode,
  useConfirmDialog,
  useCountdown,
  useCounter,
  useCssVar,
  useCurrentElement,
  useCycleList,
  useDark,
  useDateFormat,
  useDebounceFn,
  useDebouncedRefHistory,
  useDeviceMotion,
  useDeviceOrientation,
  useDevicePixelRatio,
  useDevicesList,
  useDisplayMedia,
  useDocumentVisibility,
  useDraggable,
  useDropZone,
  useElementBounding,
  useElementByPoint,
  useElementHover,
  useElementSize,
  useElementVisibility,
  useEventBus,
  useEventListener,
  useEventSource,
  useEyeDropper,
  useFavicon,
  useFetch,
  useFileDialog,
  useFileSystemAccess,
  useFocus,
  useFocusWithin,
  useFps,
  useFullscreen,
  useGamepad,
  useGeolocation,
  useIdle,
  useImage,
  useInfiniteScroll,
  useIntersectionObserver,
  useInterval,
  useIntervalFn,
  useKeyModifier,
  useLastChanged,
  useLocalStorage,
  useMagicKeys,
  useManualRefHistory,
  useMediaControls,
  useMediaQuery,
  useMemoize,
  useMemory,
  useMounted,
  useMouse,
  useMouseInElement,
  useMousePressed,
  useMutationObserver,
  useNavigatorLanguage,
  useNetwork,
  useNow,
  useObjectUrl,
  useOffsetPagination,
  useOnline,
  usePageLeave,
  useParallax,
  useParentElement,
  usePerformanceObserver,
  usePermission,
  usePointer,
  usePointerLock,
  usePointerSwipe,
  usePreferredColorScheme,
  usePreferredContrast,
  usePreferredDark,
  usePreferredLanguages,
  usePreferredReducedMotion,
  usePreferredReducedTransparency,
  usePrevious,
  useRafFn,
  useRefHistory,
  useResizeObserver,
  useSSRWidth,
  useScreenOrientation,
  useScreenSafeArea,
  useScriptTag,
  useScroll,
  useScrollLock,
  useSessionStorage,
  useShare,
  useSorted,
  useSpeechRecognition,
  useSpeechSynthesis,
  useStepper,
  useStorage,
  useStorageAsync,
  useStyleTag,
  useSupported,
  useSwipe,
  useTemplateRefsList,
  useTextDirection,
  useTextSelection,
  useTextareaAutosize,
  useThrottleFn,
  useThrottledRefHistory,
  useTimeAgo,
  useTimeout,
  useTimeoutFn,
  useTimeoutPoll,
  useTimestamp,
  useTitle,
  useToNumber,
  useToString,
  useToggle,
  useTransition,
  useUrlSearchParams,
  useUserMedia,
  useVModel,
  useVModels,
  useVibrate,
  useVirtualList,
  useWakeLock,
  useWebNotification,
  useWebSocket,
  useWebWorker,
  useWebWorkerFn,
  useWindowFocus,
  useWindowScroll,
  useWindowSize,
  watchArray,
  watchAtMost,
  watchDebounced,
  watchDeep,
  watchIgnorable,
  watchImmediate,
  watchOnce,
  watchPausable,
  watchThrottled,
  watchTriggerable,
  watchWithFilter,
  whenever
} from "./chunk-P2XGSYO7.js";
import "./chunk-HVR2FF6M.js";
export {
  DefaultMagicKeysAliasMap,
  StorageSerializers,
  TransitionPresets,
  assert,
  computedAsync as asyncComputed,
  refAutoReset as autoResetRef,
  breakpointsAntDesign,
  breakpointsBootstrapV5,
  breakpointsElement,
  breakpointsMasterCss,
  breakpointsPrimeFlex,
  breakpointsQuasar,
  breakpointsSematic,
  breakpointsTailwind,
  breakpointsVuetify,
  breakpointsVuetifyV2,
  breakpointsVuetifyV3,
  bypassFilter,
  camelize,
  clamp,
  cloneFnJSON,
  computedAsync,
  computedEager,
  computedInject,
  computedWithControl,
  containsProp,
  computedWithControl as controlledComputed,
  controlledRef,
  createEventHook,
  createFetch,
  createFilterWrapper,
  createGlobalState,
  createInjectionState,
  reactify as createReactiveFn,
  createRef,
  createReusableTemplate,
  createSharedComposable,
  createSingletonPromise,
  createTemplatePromise,
  createUnrefFn,
  customStorageEventName,
  debounceFilter,
  refDebounced as debouncedRef,
  watchDebounced as debouncedWatch,
  defaultDocument,
  defaultLocation,
  defaultNavigator,
  defaultWindow,
  computedEager as eagerComputed,
  executeTransition,
  extendRef,
  formatDate,
  formatTimeAgo,
  get,
  getLifeCycleTarget,
  getSSRHandler,
  hasOwn,
  hyphenate,
  identity,
  watchIgnorable as ignorableWatch,
  increaseWithUnit,
  injectLocal,
  invoke,
  isClient,
  isDef,
  isDefined,
  isIOS,
  isObject,
  isWorker,
  makeDestructurable,
  mapGamepadToXbox360Controller,
  noop,
  normalizeDate,
  notNullish,
  now,
  objectEntries,
  objectOmit,
  objectPick,
  onClickOutside,
  onElementRemoval,
  onKeyDown,
  onKeyPressed,
  onKeyStroke,
  onKeyUp,
  onLongPress,
  onStartTyping,
  pausableFilter,
  watchPausable as pausableWatch,
  promiseTimeout,
  provideLocal,
  provideSSRWidth,
  pxValue,
  rand,
  reactify,
  reactifyObject,
  reactiveComputed,
  reactiveOmit,
  reactivePick,
  refAutoReset,
  refDebounced,
  refDefault,
  refThrottled,
  refWithControl,
  resolveRef,
  resolveUnref,
  set,
  setSSRHandler,
  syncRef,
  syncRefs,
  templateRef,
  throttleFilter,
  refThrottled as throttledRef,
  watchThrottled as throttledWatch,
  timestamp,
  toArray,
  toReactive,
  toRef,
  toRefs,
  toValue,
  tryOnBeforeMount,
  tryOnBeforeUnmount,
  tryOnMounted,
  tryOnScopeDispose,
  tryOnUnmounted,
  unrefElement,
  until,
  useActiveElement,
  useAnimate,
  useArrayDifference,
  useArrayEvery,
  useArrayFilter,
  useArrayFind,
  useArrayFindIndex,
  useArrayFindLast,
  useArrayIncludes,
  useArrayJoin,
  useArrayMap,
  useArrayReduce,
  useArraySome,
  useArrayUnique,
  useAsyncQueue,
  useAsyncState,
  useBase64,
  useBattery,
  useBluetooth,
  useBreakpoints,
  useBroadcastChannel,
  useBrowserLocation,
  useCached,
  useClipboard,
  useClipboardItems,
  useCloned,
  useColorMode,
  useConfirmDialog,
  useCountdown,
  useCounter,
  useCssVar,
  useCurrentElement,
  useCycleList,
  useDark,
  useDateFormat,
  refDebounced as useDebounce,
  useDebounceFn,
  useDebouncedRefHistory,
  useDeviceMotion,
  useDeviceOrientation,
  useDevicePixelRatio,
  useDevicesList,
  useDisplayMedia,
  useDocumentVisibility,
  useDraggable,
  useDropZone,
  useElementBounding,
  useElementByPoint,
  useElementHover,
  useElementSize,
  useElementVisibility,
  useEventBus,
  useEventListener,
  useEventSource,
  useEyeDropper,
  useFavicon,
  useFetch,
  useFileDialog,
  useFileSystemAccess,
  useFocus,
  useFocusWithin,
  useFps,
  useFullscreen,
  useGamepad,
  useGeolocation,
  useIdle,
  useImage,
  useInfiniteScroll,
  useIntersectionObserver,
  useInterval,
  useIntervalFn,
  useKeyModifier,
  useLastChanged,
  useLocalStorage,
  useMagicKeys,
  useManualRefHistory,
  useMediaControls,
  useMediaQuery,
  useMemoize,
  useMemory,
  useMounted,
  useMouse,
  useMouseInElement,
  useMousePressed,
  useMutationObserver,
  useNavigatorLanguage,
  useNetwork,
  useNow,
  useObjectUrl,
  useOffsetPagination,
  useOnline,
  usePageLeave,
  useParallax,
  useParentElement,
  usePerformanceObserver,
  usePermission,
  usePointer,
  usePointerLock,
  usePointerSwipe,
  usePreferredColorScheme,
  usePreferredContrast,
  usePreferredDark,
  usePreferredLanguages,
  usePreferredReducedMotion,
  usePreferredReducedTransparency,
  usePrevious,
  useRafFn,
  useRefHistory,
  useResizeObserver,
  useSSRWidth,
  useScreenOrientation,
  useScreenSafeArea,
  useScriptTag,
  useScroll,
  useScrollLock,
  useSessionStorage,
  useShare,
  useSorted,
  useSpeechRecognition,
  useSpeechSynthesis,
  useStepper,
  useStorage,
  useStorageAsync,
  useStyleTag,
  useSupported,
  useSwipe,
  useTemplateRefsList,
  useTextDirection,
  useTextSelection,
  useTextareaAutosize,
  refThrottled as useThrottle,
  useThrottleFn,
  useThrottledRefHistory,
  useTimeAgo,
  useTimeout,
  useTimeoutFn,
  useTimeoutPoll,
  useTimestamp,
  useTitle,
  useToNumber,
  useToString,
  useToggle,
  useTransition,
  useUrlSearchParams,
  useUserMedia,
  useVModel,
  useVModels,
  useVibrate,
  useVirtualList,
  useWakeLock,
  useWebNotification,
  useWebSocket,
  useWebWorker,
  useWebWorkerFn,
  useWindowFocus,
  useWindowScroll,
  useWindowSize,
  watchArray,
  watchAtMost,
  watchDebounced,
  watchDeep,
  watchIgnorable,
  watchImmediate,
  watchOnce,
  watchPausable,
  watchThrottled,
  watchTriggerable,
  watchWithFilter,
  whenever
};
//# sourceMappingURL=vitepress___@vueuse_core.js.map

```

--------------------------------------------------------------------------------
/src/modules/module-loader.ts:
--------------------------------------------------------------------------------

```typescript
import type { LoadedServer, ServerProcess, ServerType } from '../types/server.js'
import type { ServerConfig } from '../types/config.js'
import type { AuthHeaders } from '../types/auth.js'
import { Logger } from '../utils/logger.js'

export interface ModuleLoaderOptions {
  healthEndpoint?: string // path appended to endpoint, defaults to '/health'
  capabilitiesEndpoint?: string // path appended to endpoint, defaults to '/capabilities'
  defaultHostname?: string // defaults to 'localhost'
}

export interface ModuleLoader {
  loadServers(configs: ServerConfig[], clientToken?: string): Promise<Map<string, LoadedServer>>
  load(config: ServerConfig, clientToken?: string): Promise<LoadedServer>
  unload(id: string): Promise<void>
  performHealthCheck(server: LoadedServer, clientToken?: string): Promise<boolean>
  restartServer(serverId: string): Promise<void>
}

/**
 * DefaultModuleLoader implements multi-source loading with cross-platform process placeholders.
 * It avoids Node-specific APIs so it can compile for both Node and Workers builds.
 * Actual spawning should be implemented by a platform-specific adapter in later phases.
 */
export class DefaultModuleLoader implements ModuleLoader {
  private servers = new Map<string, LoadedServer>()
  private options: Required<ModuleLoaderOptions>

  constructor(options?: ModuleLoaderOptions) {
    this.options = {
      healthEndpoint: options?.healthEndpoint ?? '/health',
      capabilitiesEndpoint: options?.capabilitiesEndpoint ?? '/capabilities',
      defaultHostname: options?.defaultHostname ?? 'localhost',
    }
  }

  async loadServers(configs: ServerConfig[], clientToken?: string): Promise<Map<string, LoadedServer>> {
    const results = await Promise.all(
      configs.map(async (cfg) => {
        try {
          const server = await this.load(cfg, clientToken)
          return [cfg.id, server] as const
        } catch (err) {
          Logger.error(`Failed to load server ${cfg.id}`, err)
          const server: LoadedServer = {
            id: cfg.id,
            type: 'unknown',
            endpoint: 'unknown',
            config: cfg,
            status: 'error',
            lastHealthCheck: Date.now(),
          }
          return [cfg.id, server] as const
        }
      })
    )
    for (const [id, s] of results) this.servers.set(id, s)
    return new Map(this.servers)
  }

  async load(config: ServerConfig, clientToken?: string): Promise<LoadedServer> {
    const type = this.detectServerType(config)
    const base: LoadedServer = {
      id: config.id,
      type,
      endpoint: this.deriveEndpoint(config),
      config,
      status: 'starting',
      lastHealthCheck: 0,
    }

    let loaded: LoadedServer
    switch (config.type) {
      case 'git':
        loaded = await this.loadFromGit(config, base)
        break
      case 'npm':
        loaded = await this.loadFromNpm(config, base)
        break
      case 'pypi':
        loaded = await this.loadFromPypi(config, base)
        break
      case 'docker':
        loaded = await this.loadFromDocker(config, base)
        break
      case 'local':
        loaded = await this.loadFromLocal(config, base)
        break
      default:
        loaded = base
    }

    // Immediate health check to set running/error
    try {
      const ok = await this.performHealthCheck(loaded, clientToken)
      loaded.status = ok ? 'running' : 'error'
    } catch (err) {
      Logger.warn(`Health check failed for ${config.id}`, err)
      loaded.status = 'error'
    }

    this.servers.set(loaded.id, loaded)
    return loaded
  }

  async unload(id: string): Promise<void> {
    const server = this.servers.get(id)
    if (!server) return
    try {
      await server.process?.stop()
    } catch (err) {
      Logger.warn(`Error stopping server ${id}`, err)
    } finally {
      this.servers.delete(id)
      Logger.logServerEvent('unloaded', id)
    }
  }

  async restartServer(serverId: string): Promise<void> {
    const server = this.servers.get(serverId)
    if (!server) throw new Error(`Server not found: ${serverId}`)
    Logger.logServerEvent('restarting', serverId)
    try {
      await server.process?.stop()
    } catch (err) {
      Logger.warn(`Error stopping server ${serverId} during restart`, err)
    }
    // Re-load using the same config
    const reloaded = await this.load(server.config)
    this.servers.set(serverId, reloaded)
  }

  async performHealthCheck(server: LoadedServer, clientToken?: string): Promise<boolean> {
    if (!server.endpoint || server.endpoint === 'unknown') {
      server.lastHealthCheck = Date.now()
      server.status = 'error'
      return false
    }
    const url = new URL(this.options.healthEndpoint, this.ensureTrailingSlash(server.endpoint)).toString()
    const headers: AuthHeaders = {}
    // In Phase 3, auth integration is handled at higher layers; here we only accept a caller-provided token.
    if (clientToken) headers['Authorization'] = `Bearer ${clientToken}`
    try {
      const res = await fetch(url, { headers })
      server.lastHealthCheck = Date.now()
      if (!res.ok) return false
      const ct = res.headers.get('content-type') || ''
      if (ct.includes('application/json')) {
        const json = (await res.json()) as any
        return Boolean(json?.ok ?? true)
      }
      return true
    } catch (err) {
      server.lastHealthCheck = Date.now()
      Logger.warn(`Health check request failed for ${server.id}`, err)
      return false
    }
  }

  // --- Multi-source loading stubs (network/process performed outside this module in later phases) ---
  private async loadFromGit(config: ServerConfig, base: LoadedServer): Promise<LoadedServer> {
    // In a full implementation, we'd clone and install. Here we assume it's pre-built and start it.
    Logger.logServerEvent('loadFromGit', config.id, { url: config.url, branch: config.branch })
    return this.startRuntime(config, base)
  }

  private async loadFromNpm(config: ServerConfig, base: LoadedServer): Promise<LoadedServer> {
    Logger.logServerEvent('loadFromNpm', config.id, { pkg: config.package, version: config.version })
    return this.startRuntime(config, base)
  }

  private async loadFromPypi(config: ServerConfig, base: LoadedServer): Promise<LoadedServer> {
    Logger.logServerEvent('loadFromPypi', config.id, { pkg: config.package, version: config.version })
    return this.startRuntime(config, base)
  }

  private async loadFromDocker(config: ServerConfig, base: LoadedServer): Promise<LoadedServer> {
    Logger.logServerEvent('loadFromDocker', config.id, { image: config.package, tag: config.version })
    // Docker orchestration would run the container exposing a port; we only resolve endpoint here
    return { ...base, status: 'starting' }
  }

  private async loadFromLocal(config: ServerConfig, base: LoadedServer): Promise<LoadedServer> {
    Logger.logServerEvent('loadFromLocal', config.id, { path: config.url })
    Logger.info('Loading local server', { config })
    const result = await this.startRuntime(config, base)
    Logger.info('Loaded local server', { result })
    return result
  }

  // --- Runtime orchestration and type detection ---
  private detectServerType(config: ServerConfig): ServerType {
    // Heuristics: look at package name/url/args for hints
    const name = (config.package ?? config.url ?? '').toLowerCase()
    Logger.info('Detecting server type', { name, config })
    
    // For file URLs, we should detect as stdio
    if (config.url && config.url.startsWith('file://')) {
      Logger.info('Assuming stdio server for file URL', { url: config.url })
      return 'stdio'
    }
    
    // For URLs, we can't determine the type from the URL itself
    // Let's assume it's a node server for HTTP URLs
    if (config.url && /^https?:\/\//i.test(config.url)) {
      Logger.info('Assuming node server for HTTP URL', { url: config.url })
      return 'node'
    }
    
    if (name.endsWith('.py') || /py|pypi|python/.test(name)) return 'python'
    if (/ts|typescript/.test(name)) return 'typescript'
    if (/node|js|npm/.test(name)) return 'node'
    return 'unknown'
  }

  private deriveEndpoint(config: ServerConfig): string {
    const port = config.config.port
    if (port) return `http://${this.options.defaultHostname}:${port}`
    // If URL looks like http(s):// use as-is
    const url = config.url ?? ''
    Logger.info('Deriving endpoint', { url, config })
    if (/^https?:\/\//i.test(url)) return url
    // For file URLs (STDIO servers), we can't derive an HTTP endpoint
    if (url.startsWith('file://')) return 'unknown'
    return 'unknown'
  }

  private async startRuntime(config: ServerConfig, base: LoadedServer): Promise<LoadedServer> {
    const type = this.detectServerType(config)
    Logger.info('Detected server type', { type, config })
    let proc: ServerProcess | undefined
    try {
      // For remote servers (HTTP URLs), we don't need to start a process
      if (config.url && /^https?:\/\//i.test(config.url)) {
        Logger.info('Remote server, no process to start', { url: config.url })
        proc = undefined
      } else if (type === 'stdio') {
        // For STDIO servers, start as a child process
        Logger.info('Starting STDIO server as child process', { url: config.url })
        const { StdioManager } = await import('./stdio-manager.js')
        const stdioManager = new StdioManager()
        const filePath = config.url!.replace('file://', '')
        proc = await stdioManager.startServer(config.id, filePath, config.config.environment)
      } else if (type === 'python') {
        proc = await this.startPythonServer(config)
      } else if (type === 'typescript' || type === 'node') {
        proc = await this.startTypeScriptServer(config)
      } else {
        // Unknown: assume externally managed endpoint
        proc = undefined
      }
    } catch (err) {
      Logger.error(`Failed to start runtime for ${config.id}`, err)
      return { ...base, status: 'error' }
    }
    Logger.info('Started runtime', { proc, base })
    return { ...base, process: proc, status: 'starting' }
  }

  // Cross-platform placeholders. Real implementation should manage child processes per-OS.
  private async startPythonServer(_config: ServerConfig): Promise<ServerProcess> {
    // Placeholder: assume an external process is started via orchestrator. Provide a no-op stop.
    return { stop: async () => void 0 }
  }

  private async startTypeScriptServer(config: ServerConfig): Promise<ServerProcess> {
    // Check if this is a file URL that should be started as a child process
    if (config.url && config.url.startsWith('file://')) {
      Logger.info('Starting TypeScript server as child process', { url: config.url })
      
      // Import child_process dynamically
      const { spawn } = await import('node:child_process')
      
      // Convert file:// URL to path
      const filePath = config.url.replace('file://', '')
      
      // Spawn the process
      const proc = spawn('node', [filePath], {
        stdio: ['pipe', 'pipe', 'pipe'], // stdin, stdout, stderr
        env: { ...process.env, ...config.config.environment }
      })
      
      return {
        pid: proc.pid,
        stop: async () => {
          return new Promise((resolve) => {
            proc.kill()
            setTimeout(() => resolve(), 1000) // Wait 1 second for graceful shutdown
          })
        }
      }
    }
    
    // Placeholder: assume an external process is started via orchestrator. Provide a no-op stop.
    return { stop: async () => void 0 }
  }

  private ensureTrailingSlash(endpoint: string): string {
    if (!endpoint.endsWith('/')) return `${endpoint}/`
    return endpoint
  }
}

```

--------------------------------------------------------------------------------
/src/oauth/flow-controller.ts:
--------------------------------------------------------------------------------

```typescript
import type { MasterConfig } from '../types/config.js'
import type { OAuthToken } from '../types/auth.js'
import { PKCEManager } from './pkce-manager.js'
import { StateManager } from './state-manager.js'
import { FlowValidator } from './flow-validator.js'
import { CallbackHandler } from './callback-handler.js'
import { WebInterface } from './web-interface.js'
import { Logger } from '../utils/logger.js'

export interface FlowControllerDeps {
  getConfig: () => MasterConfig
  // Called to store a delegated token when server + client context is known
  storeDelegatedToken?: (clientToken: string, serverId: string, token: OAuthToken) => Promise<void>
}

export class OAuthFlowController {
  private readonly pkce = new PKCEManager()
  private readonly state = new StateManager()
  private readonly validator: FlowValidator
  private readonly web = new WebInterface()
  private readonly deps: FlowControllerDeps
  private readonly basePath: string

  constructor(deps: FlowControllerDeps, basePath = '/oauth') {
    this.validator = new FlowValidator(deps.getConfig)
    this.deps = deps
    this.basePath = basePath
  }

  // Compute baseUrl from request context
  private getBaseUrlFromExpress(req: any): string {
    const cfg = this.deps.getConfig()
    if (cfg.hosting?.base_url) return cfg.hosting.base_url
    const proto = (req.protocol as string) || 'http'
    const host = (req.get?.('host') as string) || req.headers?.host
    return `${proto}://${host}`
  }

  private getBaseUrlFromRequest(req: Request): string {
    const cfg = this.deps.getConfig()
    if (cfg.hosting?.base_url) return cfg.hosting.base_url
    try {
      const u = new URL(req.url)
      return `${u.protocol}//${u.host}`
    } catch {
      return 'http://localhost'
    }
  }

  // Express registration (no direct dependency on express types)
  registerExpress(app: any): void {
    const base = this.basePath
    // GET /oauth/authorize
    app.get(`${base}/authorize`, async (req: any, res: any) => {
      try {
        const query = req.query || {}
        const providerParam = typeof query.provider === 'string' ? query.provider : undefined
        const serverId = typeof query.server_id === 'string' ? query.server_id : undefined
        const scopesParam = typeof query.scope === 'string' ? query.scope : undefined
        const returnTo = this.validator.validateReturnTo(
          typeof query.return_to === 'string' ? query.return_to : undefined,
          this.getBaseUrlFromExpress(req)
        )
        const { config, providerId } = this.validator.resolveProvider({ provider: providerParam, serverId })
        const state = this.state.create({ provider: providerId, serverId, clientToken: this.getClientTokenFromExpress(req), returnTo })
        const { challenge, method } = await this.pkce.generate(state)
        const baseUrl = this.getBaseUrlFromExpress(req)
        const redirectUri = new URL(`${this.basePath}/callback`, baseUrl).toString()
        const authUrl = new URL(config.authorization_endpoint)
        authUrl.searchParams.set('response_type', 'code')
        authUrl.searchParams.set('client_id', config.client_id)
        authUrl.searchParams.set('redirect_uri', redirectUri)
        const scope = scopesParam ?? (config.scopes ? config.scopes.join(' ') : '')
        if (scope) authUrl.searchParams.set('scope', scope)
        authUrl.searchParams.set('state', state)
        authUrl.searchParams.set('code_challenge', challenge)
        authUrl.searchParams.set('code_challenge_method', method)

        // Render a small redirect page to avoid exposing long URLs in Location header logs
        res.set('content-type', 'text/html; charset=utf-8')
        res.status(200).send(this.web.renderRedirectPage(providerId, authUrl.toString()))
      } catch (err) {
        Logger.warn('OAuth authorize failed', err)
        res.redirect(`${this.basePath}/error`)
      }
    })

    // GET /oauth/callback
    app.get(`${base}/callback`, async (req: any, res: any) => {
      try {
        const params = new URLSearchParams(req.query as Record<string, string>)
        const providerParam = typeof req.query?.provider === 'string' ? (req.query.provider as string) : undefined
        const serverId = typeof req.query?.server_id === 'string' ? (req.query.server_id as string) : undefined
        const { config } = this.validator.resolveProvider({ provider: providerParam, serverId })
        const cb = new CallbackHandler({
          config: this.deps.getConfig(),
          pkceManager: this.pkce,
          stateManager: this.state,
          baseUrl: this.getBaseUrlFromExpress(req),
          storeDelegatedToken: this.deps.storeDelegatedToken,
        })
        const result = await cb.handleCallback(params, config)
        if (result.error) {
          res.redirect(`${this.basePath}/error?msg=${encodeURIComponent(result.error)}`)
          return
        }
        const returnTo = result.state?.returnTo
        if (returnTo) {
          res.redirect(returnTo)
        } else {
          res.set('content-type', 'text/html; charset=utf-8')
          res.status(200).send(this.web.renderSuccessPage('Authorization complete. You may close this window.'))
        }
      } catch (err) {
        Logger.warn('OAuth callback failed', err)
        res.redirect(`${this.basePath}/error`)
      }
    })

    // POST /oauth/token
    app.post(`${base}/token`, async (req: any, res: any) => {
      try {
        const body = req.body || {}
        const state = typeof body.state === 'string' ? body.state : undefined
        const code = typeof body.code === 'string' ? body.code : undefined
        const providerParam = typeof body.provider === 'string' ? body.provider : undefined
        const serverId = typeof body.server_id === 'string' ? body.server_id : undefined
        if (!state || !code) {
          res.status(400).json({ error: 'Missing state or code' })
          return
        }
        const { config } = this.validator.resolveProvider({ provider: providerParam, serverId })
        const cb = new CallbackHandler({
          config: this.deps.getConfig(),
          pkceManager: this.pkce,
          stateManager: this.state,
          baseUrl: this.getBaseUrlFromExpress(req),
          storeDelegatedToken: this.deps.storeDelegatedToken,
        })
        const result = await cb.handleCallback(new URLSearchParams({ state, code }), config)
        if (result.error) {
          res.status(400).json({ error: result.error })
          return
        }
        // For security, do not return tokens to the browser; we store server-side
        res.json({ ok: true })
      } catch (err) {
        Logger.warn('OAuth token exchange failed', err)
        res.status(500).json({ error: 'Token exchange failed' })
      }
    })

    // Success and error pages
    app.get(`${base}/success`, (_req: any, res: any) => {
      res.set('content-type', 'text/html; charset=utf-8')
      res.status(200).send(this.web.renderSuccessPage())
    })

    app.get(`${base}/error`, (req: any, res: any) => {
      const msg = typeof req.query?.msg === 'string' ? (req.query.msg as string) : undefined
      res.set('content-type', 'text/html; charset=utf-8')
      res.status(200).send(this.web.renderErrorPage(msg))
    })
  }

  private getClientTokenFromExpress(req: any): string | undefined {
    const h = (req.headers?.authorization as string) || (req.headers?.Authorization as string)
    if (typeof h === 'string' && h.toLowerCase().startsWith('bearer ')) return h.slice(7)
    return undefined
  }

  // Cross-platform Worker-style request handler
  async handleRequest(req: Request): Promise<Response> {
    const url = new URL(req.url)
    const path = url.pathname
    if (req.method === 'GET' && path.endsWith(`${this.basePath}/authorize`)) {
      try {
        const providerParam = (url.searchParams.get('provider') || undefined) as string | undefined
        const serverId = (url.searchParams.get('server_id') || undefined) as string | undefined
        const scopesParam = (url.searchParams.get('scope') || undefined) as string | undefined
        const returnTo = this.validator.validateReturnTo(url.searchParams.get('return_to'), this.getBaseUrlFromRequest(req))
        const { config, providerId } = this.validator.resolveProvider({ provider: providerParam, serverId })
        // Cannot reliably get Authorization header in some browser flows; ignore client token in Workers
        const state = this.state.create({ provider: providerId, serverId, clientToken: undefined, returnTo })
        const { challenge, method } = await this.pkce.generate(state)
        const redirectUri = new URL(`${this.basePath}/callback`, this.getBaseUrlFromRequest(req)).toString()
        const authUrl = new URL(config.authorization_endpoint)
        authUrl.searchParams.set('response_type', 'code')
        authUrl.searchParams.set('client_id', config.client_id)
        authUrl.searchParams.set('redirect_uri', redirectUri)
        const scope = scopesParam ?? (config.scopes ? config.scopes.join(' ') : '')
        if (scope) authUrl.searchParams.set('scope', scope)
        authUrl.searchParams.set('state', state)
        authUrl.searchParams.set('code_challenge', challenge)
        authUrl.searchParams.set('code_challenge_method', method)
        return new Response(this.web.renderRedirectPage(providerId, authUrl.toString()), {
          headers: { 'content-type': 'text/html; charset=utf-8' },
          status: 200,
        })
      } catch (err) {
        Logger.warn('OAuth authorize (worker) failed', err)
        return new Response(this.web.renderErrorPage('Failed to start authorization'), {
          headers: { 'content-type': 'text/html; charset=utf-8' },
          status: 500,
        })
      }
    }

    if (req.method === 'GET' && path.endsWith(`${this.basePath}/callback`)) {
      try {
        const params = new URLSearchParams(url.search)
        const providerParam = url.searchParams.get('provider')
        const serverId = url.searchParams.get('server_id')
        const { config } = this.validator.resolveProvider({ provider: providerParam, serverId })
        const cb = new CallbackHandler({
          config: this.deps.getConfig(),
          pkceManager: this.pkce,
          stateManager: this.state,
          baseUrl: this.getBaseUrlFromRequest(req),
          storeDelegatedToken: this.deps.storeDelegatedToken,
        })
        const result = await cb.handleCallback(params, config)
        if (result.error) {
          return new Response(this.web.renderErrorPage(result.error), {
            headers: { 'content-type': 'text/html; charset=utf-8' },
            status: 400,
          })
        }
        const returnTo = result.state?.returnTo
        if (returnTo) return Response.redirect(new URL(returnTo, this.getBaseUrlFromRequest(req)).toString(), 302)
        return new Response(this.web.renderSuccessPage('Authorization complete. You may close this window.'), {
          headers: { 'content-type': 'text/html; charset=utf-8' },
          status: 200,
        })
      } catch (err) {
        Logger.warn('OAuth callback (worker) failed', err)
        return new Response(this.web.renderErrorPage('Callback handling failed'), {
          headers: { 'content-type': 'text/html; charset=utf-8' },
          status: 500,
        })
      }
    }

    if (req.method === 'POST' && path.endsWith(`${this.basePath}/token`)) {
      try {
        const ct = req.headers.get('content-type') || ''
        let data: Record<string, string> = {}
        if (ct.includes('application/json')) {
          data = (await req.json()) as any
        } else if (ct.includes('application/x-www-form-urlencoded')) {
          data = Object.fromEntries(new URLSearchParams(await req.text())) as any
        } else {
          return new Response(JSON.stringify({ error: 'Unsupported content type' }), {
            headers: { 'content-type': 'application/json' },
            status: 415,
          })
        }
        const state = typeof data.state === 'string' ? data.state : undefined
        const code = typeof data.code === 'string' ? data.code : undefined
        const providerParam = typeof data.provider === 'string' ? data.provider : undefined
        const serverId = typeof data.server_id === 'string' ? data.server_id : undefined
        if (!state || !code) return new Response(JSON.stringify({ error: 'Missing state or code' }), { headers: { 'content-type': 'application/json' }, status: 400 })
        const { config } = this.validator.resolveProvider({ provider: providerParam, serverId })
        const cb = new CallbackHandler({
          config: this.deps.getConfig(),
          pkceManager: this.pkce,
          stateManager: this.state,
          baseUrl: this.getBaseUrlFromRequest(req),
          storeDelegatedToken: this.deps.storeDelegatedToken,
        })
        const result = await cb.handleCallback(new URLSearchParams({ state, code }), config)
        if (result.error) return new Response(JSON.stringify({ error: result.error }), { headers: { 'content-type': 'application/json' }, status: 400 })
        return new Response(JSON.stringify({ ok: true }), { headers: { 'content-type': 'application/json' } })
      } catch (err) {
        Logger.warn('OAuth token exchange (worker) failed', err)
        return new Response(JSON.stringify({ error: 'Token exchange failed' }), { headers: { 'content-type': 'application/json' }, status: 500 })
      }
    }

    if (req.method === 'GET' && path.endsWith(`${this.basePath}/success`)) {
      return new Response(this.web.renderSuccessPage(), { headers: { 'content-type': 'text/html; charset=utf-8' } })
    }
    if (req.method === 'GET' && path.endsWith(`${this.basePath}/error`)) {
      const msg = new URL(req.url).searchParams.get('msg') ?? undefined
      return new Response(this.web.renderErrorPage(msg || undefined), { headers: { 'content-type': 'text/html; charset=utf-8' } })
    }

    return new Response('Not Found', { status: 404 })
  }
}

```

--------------------------------------------------------------------------------
/docs/architecture/images/mcp_master_architecture.svg:
--------------------------------------------------------------------------------

```
<svg viewBox="0 0 1400 1000" xmlns="http://www.w3.org/2000/svg">
  <defs>
    <marker id="arrowhead" markerWidth="10" markerHeight="7" refX="10" refY="3.5" orient="auto">
      <polygon points="0 0, 10 3.5, 0 7" fill="#333"/>
    </marker>
    <pattern id="diagonalHatch" patternUnits="userSpaceOnUse" width="4" height="4">
      <path d="M-1,1 l2,-2 M0,4 l4,-4 M3,5 l2,-2" style="stroke:#ccc, stroke-width:1"/>
    </pattern>
    <filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
      <feDropShadow dx="3" dy="3" stdDeviation="2" flood-color="#00000020"/>
    </filter>
  </defs>

  <!-- Background -->
  <rect width="1400" height="1000" fill="#f8fafc"/>
  
  <!-- Title -->
  <text x="700" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="24" font-weight="bold" fill="#1e293b">
    Master MCP Server Architecture
  </text>
  <text x="700" y="55" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" fill="#64748b">
    Unified MCP Gateway with Shared OAuth &amp; Multi-Repository Module Loading
  </text>

  <!-- Client Layer -->
  <g id="client-layer">
    <rect x="50" y="100" width="1300" height="80" fill="#e0f2fe" stroke="#0369a1" stroke-width="2" rx="8" filter="url(#shadow)"/>
    <text x="70" y="125" font-family="Arial, sans-serif" font-size="16" font-weight="bold" fill="#0369a1">MCP Clients</text>
    
    <rect x="100" y="135" width="120" height="35" fill="#0284c7" rx="4"/>
    <text x="160" y="157" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" fill="white">Claude Desktop</text>
    
    <rect x="240" y="135" width="120" height="35" fill="#0284c7" rx="4"/>
    <text x="300" y="157" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" fill="white">Cursor IDE</text>
    
    <rect x="380" y="135" width="120" height="35" fill="#0284c7" rx="4"/>
    <text x="440" y="157" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" fill="white">Custom Apps</text>
    
    <text x="600" y="152" font-family="Arial, sans-serif" font-size="12" fill="#64748b">Single MCP Connection</text>
  </g>

  <!-- Master MCP Server -->
  <g id="master-server">
    <rect x="50" y="220" width="1300" height="260" fill="#f1f5f9" stroke="#475569" stroke-width="2" rx="8" filter="url(#shadow)"/>
    <text x="70" y="245" font-family="Arial, sans-serif" font-size="18" font-weight="bold" fill="#1e293b">Master MCP Server Gateway</text>

    <!-- OAuth Manager -->
    <rect x="80" y="260" width="180" height="80" fill="#fef3c7" stroke="#f59e0b" stroke-width="2" rx="6"/>
    <text x="170" y="285" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" fill="#92400e">OAuth Manager</text>
    <text x="90" y="305" font-family="Arial, sans-serif" font-size="11" fill="#92400e">• Token validation</text>
    <text x="90" y="320" font-family="Arial, sans-serif" font-size="11" fill="#92400e">• Shared auth state</text>
    <text x="90" y="335" font-family="Arial, sans-serif" font-size="11" fill="#92400e">• Token forwarding</text>

    <!-- Capability Aggregator -->
    <rect x="280" y="260" width="180" height="80" fill="#ddd6fe" stroke="#8b5cf6" stroke-width="2" rx="6"/>
    <text x="370" y="285" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" fill="#5b21b6">Capability Aggregator</text>
    <text x="290" y="305" font-family="Arial, sans-serif" font-size="11" fill="#5b21b6">• Tool discovery</text>
    <text x="290" y="320" font-family="Arial, sans-serif" font-size="11" fill="#5b21b6">• Schema merging</text>
    <text x="290" y="335" font-family="Arial, sans-serif" font-size="11" fill="#5b21b6">• Resource listing</text>

    <!-- Request Router -->
    <rect x="480" y="260" width="180" height="80" fill="#dcfce7" stroke="#16a34a" stroke-width="2" rx="6"/>
    <text x="570" y="285" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" fill="#166534">Request Router</text>
    <text x="490" y="305" font-family="Arial, sans-serif" font-size="11" fill="#166534">• Tool call routing</text>
    <text x="490" y="320" font-family="Arial, sans-serif" font-size="11" fill="#166534">• Load balancing</text>
    <text x="490" y="335" font-family="Arial, sans-serif" font-size="11" fill="#166534">• Error handling</text>

    <!-- Module Loader -->
    <rect x="680" y="260" width="180" height="80" fill="#fee2e2" stroke="#dc2626" stroke-width="2" rx="6"/>
    <text x="770" y="285" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" fill="#991b1b">Module Loader</text>
    <text x="690" y="305" font-family="Arial, sans-serif" font-size="11" fill="#991b1b">• Git repo cloning</text>
    <text x="690" y="320" font-family="Arial, sans-serif" font-size="11" fill="#991b1b">• Package installation</text>
    <text x="690" y="335" font-family="Arial, sans-serif" font-size="11" fill="#991b1b">• Hot reloading</text>

    <!-- Server Manager -->
    <rect x="880" y="260" width="180" height="80" fill="#f3e8ff" stroke="#a855f7" stroke-width="2" rx="6"/>
    <text x="970" y="285" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" fill="#7c2d12">Server Manager</text>
    <text x="890" y="305" font-family="Arial, sans-serif" font-size="11" fill="#7c2d12">• Process lifecycle</text>
    <text x="890" y="320" font-family="Arial, sans-serif" font-size="11" fill="#7c2d12">• Health monitoring</text>
    <text x="890" y="335" font-family="Arial, sans-serif" font-size="11" fill="#7c2d12">• Auto-restart</text>

    <!-- Config Manager -->
    <rect x="1080" y="260" width="180" height="80" fill="#ecfdf5" stroke="#10b981" stroke-width="2" rx="6"/>
    <text x="1170" y="285" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" fill="#065f46">Config Manager</text>
    <text x="1090" y="305" font-family="Arial, sans-serif" font-size="11" fill="#065f46">• Server configs</text>
    <text x="1090" y="320" font-family="Arial, sans-serif" font-size="11" fill="#065f46">• Environment vars</text>
    <text x="1090" y="335" font-family="Arial, sans-serif" font-size="11" fill="#065f46">• Secret management</text>

    <!-- MCP Protocol Handler -->
    <rect x="280" y="360" width="700" height="60" fill="#e2e8f0" stroke="#64748b" stroke-width="2" rx="6"/>
    <text x="630" y="385" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" fill="#334155">MCP Protocol Handler (Streamable HTTP)</text>
    <text x="290" y="405" font-family="Arial, sans-serif" font-size="11" fill="#475569">Handles: list_tools, call_tool, list_resources, read_resource, subscribe, notifications</text>
  </g>

  <!-- Repository Sources -->
  <g id="repositories">
    <rect x="50" y="520" width="400" height="120" fill="#fffbeb" stroke="#f59e0b" stroke-width="2" rx="8" filter="url(#shadow)"/>
    <text x="70" y="545" font-family="Arial, sans-serif" font-size="16" font-weight="bold" fill="#92400e">Repository Sources</text>
    
    <rect x="80" y="560" width="100" height="30" fill="#f59e0b" rx="4"/>
    <text x="130" y="580" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" fill="white">GitHub Repos</text>
    
    <rect x="200" y="560" width="100" height="30" fill="#f59e0b" rx="4"/>
    <text x="250" y="580" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" fill="white">GitLab Repos</text>
    
    <rect x="320" y="560" width="100" height="30" fill="#f59e0b" rx="4"/>
    <text x="370" y="580" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" fill="white">Private Git</text>

    <rect x="80" y="600" width="100" height="30" fill="#ea580c" rx="4"/>
    <text x="130" y="620" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" fill="white">NPM Registry</text>
    
    <rect x="200" y="600" width="100" height="30" fill="#ea580c" rx="4"/>
    <text x="250" y="620" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" fill="white">PyPI Registry</text>
    
    <rect x="320" y="600" width="100" height="30" fill="#ea580c" rx="4"/>
    <text x="370" y="620" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" fill="white">Docker Hub</text>
  </g>

  <!-- Backend MCP Servers -->
  <g id="backend-servers">
    <rect x="500" y="520" width="850" height="180" fill="#f0fdf4" stroke="#16a34a" stroke-width="2" rx="8" filter="url(#shadow)"/>
    <text x="520" y="545" font-family="Arial, sans-serif" font-size="16" font-weight="bold" fill="#166534">Backend MCP Servers (Auto-loaded)</text>

    <!-- Python Servers -->
    <g id="python-servers">
      <text x="530" y="570" font-family="Arial, sans-serif" font-size="14" font-weight="bold" fill="#1e40af">Python Servers</text>
      
      <rect x="530" y="580" width="140" height="50" fill="#dbeafe" stroke="#3b82f6" stroke-width="1" rx="4"/>
      <text x="600" y="600" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#1e40af">File System MCP</text>
      <text x="600" y="615" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#1e40af">Tools: read, write, list</text>

      <rect x="690" y="580" width="140" height="50" fill="#dbeafe" stroke="#3b82f6" stroke-width="1" rx="4"/>
      <text x="760" y="600" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#1e40af">Database MCP</text>
      <text x="760" y="615" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#1e40af">Tools: query, insert, update</text>

      <rect x="850" y="580" width="140" height="50" fill="#dbeafe" stroke="#3b82f6" stroke-width="1" rx="4"/>
      <text x="920" y="600" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#1e40af">Email MCP</text>
      <text x="920" y="615" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#1e40af">Tools: send, search, read</text>
    </g>

    <!-- TypeScript Servers -->
    <g id="typescript-servers">
      <text x="530" y="660" font-family="Arial, sans-serif" font-size="14" font-weight="bold" fill="#059669">TypeScript Servers</text>
      
      <rect x="530" y="670" width="140" height="50" fill="#d1fae5" stroke="#10b981" stroke-width="1" rx="4"/>
      <text x="600" y="690" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#059669">GitHub MCP</text>
      <text x="600" y="705" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#059669">Tools: create_issue, pr</text>

      <rect x="690" y="670" width="140" height="50" fill="#d1fae5" stroke="#10b981" stroke-width="1" rx="4"/>
      <text x="760" y="690" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#059669">Slack MCP</text>
      <text x="760" y="705" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#059669">Tools: post, search, dm</text>

      <rect x="850" y="670" width="140" height="50" fill="#d1fae5" stroke="#10b981" stroke-width="1" rx="4"/>
      <text x="920" y="690" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#059669">Calendar MCP</text>
      <text x="920" y="705" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#059669">Tools: schedule, list</text>

      <!-- More servers indicator -->
      <rect x="1010" y="625" width="80" height="50" fill="#f3f4f6" stroke="#9ca3af" stroke-width="1" rx="4" stroke-dasharray="5,5"/>
      <text x="1050" y="645" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" fill="#6b7280">More</text>
      <text x="1050" y="660" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" fill="#6b7280">Servers...</text>
    </g>

    <!-- Server Runtime Info -->
    <rect x="1120" y="580" width="200" height="110" fill="#fef7ff" stroke="#c084fc" stroke-width="1" rx="4"/>
    <text x="1220" y="600" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#7c3aed">Runtime Environment</text>
    <text x="1130" y="620" font-family="Arial, sans-serif" font-size="10" fill="#7c3aed">• Python 3.9+ processes</text>
    <text x="1130" y="635" font-family="Arial, sans-serif" font-size="10" fill="#7c3aed">• Node.js 18+ processes</text>
    <text x="1130" y="650" font-family="Arial, sans-serif" font-size="10" fill="#7c3aed">• Docker containers</text>
    <text x="1130" y="665" font-family="Arial, sans-serif" font-size="10" fill="#7c3aed">• Health checks</text>
    <text x="1130" y="680" font-family="Arial, sans-serif" font-size="10" fill="#7c3aed">• Auto-scaling</text>
  </g>

  <!-- Configuration -->
  <g id="configuration">
    <rect x="50" y="740" width="500" height="120" fill="#fef2f2" stroke="#ef4444" stroke-width="2" rx="8" filter="url(#shadow)"/>
    <text x="70" y="765" font-family="Arial, sans-serif" font-size="16" font-weight="bold" fill="#991b1b">Configuration (master-mcp-config.yaml)</text>
    
    <rect x="80" y="780" width="440" height="70" fill="#fee2e2" rx="4"/>
    <text x="90" y="800" font-family="Monaco, monospace" font-size="10" fill="#991b1b">servers:</text>
    <text x="90" y="815" font-family="Monaco, monospace" font-size="10" fill="#991b1b">  - repo: github.com/user/filesystem-mcp</text>
    <text x="90" y="830" font-family="Monaco, monospace" font-size="10" fill="#991b1b">  - repo: github.com/user/slack-mcp</text>
    <text x="90" y="845" font-family="Monaco, monospace" font-size="10" fill="#991b1b">  - package: "@mcp/github-server@latest"</text>
  </g>

  <!-- Hosting Options -->
  <g id="hosting">
    <rect x="600" y="740" width="750" height="120" fill="#f0f9ff" stroke="#0ea5e9" stroke-width="2" rx="8" filter="url(#shadow)"/>
    <text x="620" y="765" font-family="Arial, sans-serif" font-size="16" font-weight="bold" fill="#0c4a6e">Hosting Options</text>
    
    <rect x="640" y="780" width="120" height="30" fill="#0ea5e9" rx="4"/>
    <text x="700" y="800" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" fill="white">Cloudflare Workers</text>
    
    <rect x="780" y="780" width="80" height="30" fill="#0ea5e9" rx="4"/>
    <text x="820" y="800" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" fill="white">Koyeb</text>
    
    <rect x="880" y="780" width="80" height="30" fill="#0ea5e9" rx="4"/>
    <text x="920" y="800" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" fill="white">Render</text>
    
    <rect x="980" y="780" width="120" height="30" fill="#0ea5e9" rx="4"/>
    <text x="1040" y="800" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" fill="white">Docker Compose</text>
    
    <rect x="1120" y="780" width="120" height="30" fill="#0ea5e9" rx="4"/>
    <text x="1180" y="800" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" fill="white">Kubernetes</text>

    <text x="640" y="835" font-family="Arial, sans-serif" font-size="11" fill="#0c4a6e">Scale-to-zero, OAuth, global edge distribution</text>
  </g>

  <!-- Data Flow Arrows -->
  <g id="arrows" marker-end="url(#arrowhead)">
    <!-- Client to Master -->
    <line x1="400" y1="180" x2="400" y2="220" stroke="#374151" stroke-width="2"/>
    <text x="420" y="200" font-family="Arial, sans-serif" font-size="11" fill="#374151">MCP Requests</text>

    <!-- OAuth Flow -->
    <line x1="170" y1="180" x2="170" y2="260" stroke="#f59e0b" stroke-width="2"/>
    
    <!-- Module Loading -->
    <line x1="250" y1="520" x2="680" y2="340" stroke="#dc2626" stroke-width="2"/>
    
    <!-- Server Management -->
    <line x1="970" y1="340" x2="970" y2="520" stroke="#a855f7" stroke-width="2"/>
    
    <!-- Capability Discovery -->
    <line x1="370" y1="340" x2="700" y2="520" stroke="#8b5cf6" stroke-width="2"/>
    
    <!-- Request Routing -->
    <line x1="570" y1="340" x2="700" y2="520" stroke="#16a34a" stroke-width="2"/>
  </g>

  <!-- Legend -->
  <g id="legend">
    <rect x="50" y="890" width="1300" height="80" fill="#ffffff" stroke="#e2e8f0" stroke-width="1" rx="6" filter="url(#shadow)"/>
    <text x="70" y="915" font-family="Arial, sans-serif" font-size="14" font-weight="bold" fill="#1e293b">Key Features</text>
    
    <text x="80" y="935" font-family="Arial, sans-serif" font-size="12" fill="#475569">• <tspan font-weight="bold">Zero-config server integration:</tspan> Existing MCP servers work without modification</text>
    <text x="80" y="950" font-family="Arial, sans-serif" font-size="12" fill="#475569">• <tspan font-weight="bold">Shared OAuth:</tspan> Single authentication for all backend servers</text>
    <text x="80" y="965" font-family="Arial, sans-serif" font-size="12" fill="#475569">• <tspan font-weight="bold">Dynamic capability discovery:</tspan> Tools and resources auto-aggregated from all servers</text>
    
    <text x="600" y="935" font-family="Arial, sans-serif" font-size="12" fill="#475569">• <tspan font-weight="bold">Multi-repository support:</tspan> Load from Git, NPM, PyPI, Docker</text>
    <text x="600" y="950" font-family="Arial, sans-serif" font-size="12" fill="#475569">• <tspan font-weight="bold">Hot reloading:</tspan> Update servers without client reconnection</text>
    <text x="600" y="965" font-family="Arial, sans-serif" font-size="12" fill="#475569">• <tspan font-weight="bold">Language agnostic:</tspan> Python, TypeScript, and future language support</text>
  </g>
</svg>
```
Page 3/8FirstPrevNextLast