Skip to content

Architecture Deepening Opportunities

This note records the architecture review from 2026-05-08. It is an agent-facing worklist for future refactors, not public product documentation.

Current Codebase Status

Last checked against the working tree on 2026-05-27.

  • Milestone 1 is implemented: templateIdentity.ts and requestContextPreparation.ts exist, with targeted inspect and REST tool routes using the shared preparation path.
  • Milestone 2 first slice is implemented: filterSelection.ts and capabilityCatalog.ts exist, MetaToolProvider, /api/tools, and /api/tool-invocations use the catalog/selection seams, and serve consumes yargs/env-parsed preset input. The catalog's refresh facts are still placeholder never facts, so direct MCP handler, notification, and broader refresh-policy migration remain second-slice work.
  • Milestone 3 first slice is implemented: config-change/configChange.ts and installation/serverInstallationWorkflow.ts exist, mcp uninstall and internal remove/uninstall use Config Change, and mcp install plus mcp_install use Server Installation Workflow.
  • Milestone 4 is implemented: streamableSessionLifecycle.ts owns Streamable HTTP creation, lookup, restoration, initialize recovery, and cleanup. The deprecated sessionService.ts compatibility wrapper has been removed.
  • Milestone 5 is implemented for run, inspect, instructions, and proxy: clientSurfaceAttachment.ts owns shared attach-only runtime discovery, auth-profile lookup, Request Context construction, context hash, reusable-session cache behavior for run/inspect/instructions, REST/MCP fallback classification, stale-session retry, and fresh-session attachment for proxy.
  • Milestone 6 is complete: oauthAuthorizationFlow.ts owns consent approval/denial, localhost CLI-token creation, backend OAuth dashboard fact shaping, backend OAuth start/restart, and backend OAuth callback completion/loading-ready notification. HTTP routes delegate OAuth consent, authorization, restart, callback, and CLI-token decisions through structured flow operations while retaining consent/dashboard HTML rendering and HTTP response mapping. instructionsDistribution.ts owns eager-inspection policy, instruction detail collection/assembly, managed startup-doc content, startup-reference rendering, and legacy managed-reference cleanup. instructions uses its own reusable Client Surface Attachment identity while delegating distribution policy; cli-setup delegates managed-doc and startup-reference policy while retaining hooks, path resolution, JSON merging, and file writes.

Next Execution Target

Milestone 6 is complete. Next execution target: work through the remaining todo list below, starting with a completion audit of Milestones 1-6 before opening new architecture milestones.

Remaining Todo List

These items are the known follow-ups after the Milestone 1-6 roadmap. They should be resolved before declaring the architecture deepening pass fully closed.

  • [x] Audit Milestones 1-6 against the current working tree and record whether each milestone is fully closed, first-slice complete, or still carrying compatibility debt.
  • [x] Complete the Milestone 2 second slice for Capability Catalog refresh ownership: replace placeholder never refresh facts with real refresh intent handling where callers still sequence refresh manually.
  • [x] Move direct MCP protocol list/call handlers onto Capability Catalog where practical, preserving existing wire behavior.
  • [x] Migrate async server-loaded notifications and config-change notifications to use the catalog/visibility contract instead of bespoke capability routing.
  • [x] Decide whether src/transport/http/utils/sessionService.ts should remain as a long-term deprecated compatibility wrapper or be removed after all Streamable HTTP callers consume streamableSessionLifecycle.ts directly.
  • [x] Review compatibility facades left after Milestone 3, especially install/config-change paths, and shrink them if all migrated callers now depend on Config Change and Server Installation Workflow directly.
  • [x] Re-run the focused milestone verification set, then update this status block with exact commands and results.

Completion Audit Findings

  • Milestone 1 is closed for the planned slice: runtime identity and request-context preparation have dedicated modules and migrated REST/inspect callers.
  • Milestone 2 is first-slice complete, but remains open for second-slice Capability Catalog ownership of refresh facts, direct MCP protocol handlers, and capability-change notifications.
  • Milestone 3 is first-slice complete, with compatibility facade cleanup still pending around older install/config surfaces.
  • Milestone 4 is closed: production Streamable HTTP callers use streamableSessionLifecycle.ts directly, and the deprecated sessionService.ts wrapper has been removed.
  • Milestone 5 is closed for the planned run, inspect, instructions, and proxy attachment surfaces.
  • Milestone 6 is closed for OAuth Authorization Flow and Instructions Distribution.

Progress Notes

  • 2026-05-27: Capability Catalog now accepts an explicit refresh port and per-query refresh intent. HTTP /api/tools catalog listing requests catalog-owned ifStale refresh facts instead of relying only on downstream fallback behavior. Verified with rtk pnpm exec vitest run src/core/capabilities/capabilityCatalog.test.ts src/core/capabilities/metaToolProvider.test.ts src/transport/http/routes/apiRoutes.test.ts and rtk pnpm exec tsc --noEmit --pretty false.
  • 2026-05-27: Direct MCP ListToolsRequest handling now builds a Capability Catalog from filtered outbound connections, so disabled-tool visibility, template route resolution, and public URI shaping flow through the catalog path. Direct CallToolRequest already preserves catalog-equivalent disabled-tool enforcement and session-aware connection resolution; no broader call-path migration was practical without changing MCP timeout/error behavior. Verified with rtk pnpm exec vitest run src/core/protocol/requestHandlers-disabledTools.test.ts src/core/protocol/requestHandlers.test.ts and rtk pnpm exec tsc --noEmit --pretty false.
  • 2026-05-27: Async refresh now returns catalog-compatible notification facts, and config-change listChanged notification decisions use createCapabilityNotificationFacts(...) so tool notifications flow from catalog refresh/list visibility semantics while resource and prompt notifications stay on aggregator diffs. Verified with rtk pnpm exec vitest run src/core/capabilities/capabilityNotificationFacts.test.ts src/core/capabilities/asyncLoadingOrchestrator.test.ts src/core/configChangeHandler.test.ts src/transport/http/routes/apiRoutes.test.ts and rtk pnpm exec tsc --noEmit --pretty false.
  • 2026-05-27: Removed the deprecated src/transport/http/utils/sessionService.ts compatibility wrapper and its wrapper-only tests after confirming production Streamable HTTP routes already depend on streamableSessionLifecycle.ts directly. Verified with rtk pnpm exec vitest run src/transport/http/streamableSessionLifecycle.test.ts src/transport/http/routes/streamableHttpRoutes.test.ts.
  • 2026-05-27: Shrank the Milestone 3 compatibility facade by moving internal uninstallServer(..., { removeAll: true }) directly to Config Change. ServerInstallationService remains only for list/update/check and non-remove-all uninstall compatibility that was outside the Milestone 3 install/config-change slice. Verified with rtk pnpm exec vitest run src/core/tools/internal/adapters/installationAdapter.test.ts and rtk pnpm exec tsc --noEmit --pretty false.
  • 2026-05-27: Final focused verification passed: rtk pnpm exec vitest run src/core/capabilities/capabilityCatalog.test.ts src/core/capabilities/metaToolProvider.test.ts src/transport/http/routes/apiRoutes.test.ts src/core/protocol/requestHandlers-disabledTools.test.ts src/core/protocol/requestHandlers.test.ts src/core/capabilities/capabilityNotificationFacts.test.ts src/core/capabilities/asyncLoadingOrchestrator.test.ts src/core/configChangeHandler.test.ts src/transport/http/streamableSessionLifecycle.test.ts src/transport/http/routes/streamableHttpRoutes.test.ts src/core/tools/internal/adapters/installationAdapter.test.ts passed 11 files / 238 tests, rtk pnpm exec tsc --noEmit --pretty false passed, and rtk pnpm lint passed.

Milestone Plan

This plan sequences the deepening work so each milestone leaves the codebase in a better state, even if later milestones are delayed. Each milestone should land with focused unit tests around the new Module Interface plus caller tests for migrated Adapters.

Milestone 1: Runtime Identity And Context Foundations

Goal: remove duplicated runtime-key and request-preparation rules before changing broader capability or command paths.

Scope:

  • Complete No.1 Template Server Identity Module.
  • Complete No.2 Request Context Preparation Module for targeted inspect and REST tool listing/invocation.
  • Add any missing CONTEXT.md terms discovered while implementing these Modules.

Done when:

  • Template identity construction, serialization, parsing, lookup candidate order, and rendered-hash creation live behind src/core/server/templateIdentity.ts.
  • Targeted inspect and REST tool routes use one Request Context Preparation path for session precedence, template registration, pending instance creation, and lazy capability refresh.
  • Bare /api/inspect remains non-instantiating.
  • Tests cover static, rendered, session-bound, routing-only, and malformed identity/context cases.

Milestone 2: Capability And Filter Visibility

Goal: make visibility and routing one query surface instead of a caller responsibility.

Scope:

  • Complete the first slice of No.8 Filter Selection Module: selector resolution, preset lookup ports, compatibility locals, and requested-tag extraction.
  • Complete the first slice of No.5 Capability Snapshot And Catalog Module.
  • Migrate MetaToolProvider, /api/tools, and /api/tool-invocations to the Capability Catalog.

Done when:

  • CLI and HTTP filter inputs produce the same validated filtering intent for preset, tag-filter, filter, and tags.
  • One request or command input cannot supply multiple filter selectors silently; mutual-exclusion errors are structured, while legacy filter remains compatible when it is the sole selector.
  • Scope validation uses Filter Selection facts, including requested tags derived from simple filters, advanced filters, negative clauses, and preset tagQuery.
  • Runtime filtering remains origin-agnostic: callers build the session's Server Candidate Set from static servers and session-available Template Server Instances, then apply the selected filter intent.
  • Capability Catalog owns Capability Visibility, Capability Refresh intent, and internal Capability Routes for the first migrated callers.
  • Disabled-tool checks, tag/preset filtering, and schema/invoke access checks follow the same visibility path for migrated callers.
  • Tests cover visible listing, schema lookup, invocation rejection, template routes, static routes, selector validation, preset scope checks, and origin-agnostic filtering.

Milestone 3: Config Change And Installation

Goal: centralize persisted configuration mutation before migrating install workflows.

Scope:

  • Complete No.4 Config Mutation And Reload Module first.
  • Then complete No.3 Server Installation Workflow Module on top of the public Config Change Interface.

Done when:

  • Destructive or forced config mutations can create Config Backups, validate before writing, serialize writers, preserve unknown fields, and report reload outcomes.
  • mcp uninstall and the internal remove/uninstall path use Config Change as the first vertical slice.
  • mcp install and mcp_install use Server Installation Workflow for registry/direct preview and apply modes.
  • Tests cover backup defaults, retention, lock timeout, reload outcomes, install conflict policy, dry-run preview, endpoint priority, and structured workflow statuses.

Milestone 4: Streamable Transport Lifecycle

Goal: move Streamable HTTP continuity out of Express route control flow.

Scope:

  • Complete No.6 HTTP Streamable Transport Session Lifecycle Module.
  • Preserve existing HTTP behavior while exposing structured lifecycle results internally.
  • Reconcile the long-lived Streamable HTTP/proxy path with Request Context Preparation decisions from Milestone 1.

Done when:

  • POST, GET, and DELETE routes delegate creation, active lookup, restoration, initialize recovery, bookkeeping, disconnect cleanup, and explicit delete cleanup to streamableSessionLifecycle.
  • SessionService is removed or reduced to a temporary compatibility wrapper.
  • Tests cover active lookup, restore success/failure, initialize recovery, missing sessions, persistence warnings, abnormal disconnect, explicit delete, and Template Server Instance membership cleanup through ServerManager.disconnectTransport().

Milestone 5: Attach-Only Client Surfaces

Goal: make run, inspect, instructions, and proxy use one attachment model for an existing Aggregated Runtime.

Scope:

  • Complete No.7 Client Surface Attachment Module.
  • Migrate run and inspect first through the reusable-session attachment intent.
  • Add the fresh-session attachment path for proxy after the reusable-session contract is stable.
  • Defer instructions attachment migration until No.10 settles instruction-distribution policy, then migrate it only if its runtime-inspection behavior matches the shared state machine.
  • Keep proxy, inspect, and run attach-only; do not reopen the Background Aggregated Runtime ADR.

Done when:

  • Runtime discovery, auth-profile lookup, Request Context building, attachment intent selection, context hash, session cache, REST support cache, and stale-session retry decisions live behind one Module Interface.
  • run and inspect command bodies mostly format input/output and map structured attachment outcomes.
  • proxy uses the same attachment module for fresh-session runtime discovery, Request Context construction, filter attachment, and generated Request Session.
  • Tests cover reusable-session cache matching, legacy cache rejection, auth-required errors, REST endpoint support probing, endpoint-missing fallback, transient fallback without disabling REST, stale cached-session retry, fresh-session no-cache behavior, and filter/query propagation.

Milestone 6: OAuth And Instructions Distribution

Goal: deepen the two remaining user-facing seams whose behavior spans runtime, HTTP routes, and agent setup.

Scope:

  • Complete No.9 OAuth Authorization Flow Module.
  • Complete No.10 Instructions Distribution Module.

Done when:

  • OAuth routes no longer mutate oauthProvider.oauthStorage directly for consent or CLI-token flows.
  • Consent, denial, localhost CLI-token creation, backend OAuth start/restart/callback, and dashboard facts are structured operations behind the OAuth Module Interface.
  • Route rendering remains an adapter responsibility where recorded in No.9; consent CSP and dashboard HTML are not silently moved into lower-level storage or runtime code.
  • instructions and cli-setup use one Instructions Distribution policy for eager inspection, disconnected server instructions, managed startup-doc content, startup-reference rendering, and legacy managed-reference cleanup.
  • Tests cover invalid consent, selected scopes, localhost CLI token creation, backend OAuth restart/callback, unavailable Template Server instructions, disconnected cached instructions, global Codex absolute startup references, repo/Claude references, and managed startup-doc updates.

Cross-Milestone Rules

  • Do not skip the Interface tests for a deepened Module; the Interface is the test surface.
  • Do not migrate every caller in the first slice unless the milestone explicitly says so.
  • Preserve public command and HTTP behavior unless a milestone explicitly records a behavior change.
  • Update CONTEXT.md when a milestone needs a new domain term.
  • Add an ADR only when a milestone resolves a decision future architecture reviews should not re-litigate.

1. Template Server Identity Module

Files: src/core/server/templateServerManager.ts, src/core/server/connectionResolver.ts, src/core/server/adapters/TemplateServerAdapter.ts, src/core/capabilities/lazyLoadingOrchestrator.ts

Problem: template identity leaks across the runtime as name, name:sessionId, name:renderedHash, clean names, and outbound keys. The Seam is Shallow because callers must know key formats.

Solution: deepen one Module that owns template identity, lookup order, and routing.

Accepted boundaries:

  • Treat Template Server Identity as the canonical owner for all template identity forms, including clean template names, rendered identities, session identities, outbound routing identities, pool identities, and cleanup/tracker identities.
  • Preserve the current lookup priority as intentional behavior: session identity first, rendered identity second, and clean static name last.
  • Model identity mode explicitly. Both perClient: true and shareable: false produce a session-bound identity; default shareable templates produce a shareable rendered identity.
  • Start the module in src/core/server/templateIdentity.ts because template identity is runtime core plumbing, not a CLI or user-facing domain module.
  • Use typed identity objects internally and convert to strings only at map, adapter, and transport storage boundaries.
  • Let the identity module own rendered-hash creation from an already-rendered template config, but leave template rendering itself in the existing pool/preparation lifecycle.
  • Keep ClientTemplateTracker internals on their current templateName plus instanceId relationship model in the first slice; use Template Server Identity only at the boundaries where tracker data becomes routing, pooling, or cleanup keys.
  • Keep ConnectionResolver as the lookup facade for OutboundConnections; it should consume Template Server Identity candidate order instead of rebuilding key strings itself.
  • Make TemplateServerAdapter delegate connection lookup to ConnectionResolver instead of building its own candidate connection keys.
  • Reject : inside serialized identity components for runtime map keys. Treat no-colon keys as clean/static identities, exactly-one-colon keys as template routing identities, and more-than-one-colon routing keys as invalid. Pool identity serialization should be explicit rather than relying on ad hoc split(':') parsing.
  • Fail fast when constructing or serializing identities with invalid components, but parse existing runtime map keys defensively so malformed keys can be logged and skipped during filtering.

Implementation plan:

  1. Add src/core/server/templateIdentity.ts with typed identity objects, rendered-hash creation from already-rendered configs, key serialization, identity-mode resolution, and canonical lookup candidate order.
  2. Add focused tests for shareable rendered identity, session-bound identity from perClient: true, session-bound identity from shareable: false, pool identity serialization, static clean identity, session-first lookup candidates, invalid component rejection, and defensive parsing of malformed runtime keys.
  3. Refactor ClientInstancePool to use identity-owned rendered hashes and pool keys while leaving template rendering in the existing lifecycle.
  4. Refactor TemplateServerManager to use identity mode for outbound keys and cleanup-boundary conversions.
  5. Refactor ConnectionResolver to use canonical identity candidates while preserving it as the OutboundConnections lookup facade.
  6. Refactor TemplateServerAdapter to delegate lookup and connection-key reporting through ConnectionResolver.
  7. Keep ClientTemplateTracker internals unchanged for the first slice.
  8. Verify resolver, adapter, pool, manager, and route direct-invocation coverage.

Benefits: better Locality for template bugs and more Leverage for server, lazy loading, and tool routing tests. Tests can cover shareable, per-client, and missing-session cases once.

2. Request Context Preparation Module

Files: src/transport/http/routes/inspectRoutes.ts, src/transport/http/routes/toolRoutes.ts, src/core/server/serverManager.ts

Problem: inspect and tool routes duplicate "extract context, derive session, load templates, register templates, create pending instances, refresh capabilities," with a subtle header-only session difference.

Solution: deepen a Module whose Interface is "prepare this request/session for template-aware routing."

Accepted boundaries:

  • Treat Request Context Preparation as runtime core behavior, not an Express route helper. HTTP routes should keep transport-specific extraction and response-header writing, while the preparation module accepts normalized request context/session inputs and returns the resolved Request Session.
  • Preserve header-only mcp-session-id as a routing-only Request Session. Tool list and invocation routes may use it to route to already-prepared template instances, but it must not render or register new Template Server Instances without an actual Request Context.
  • Preserve the existing Request Session precedence: transport-provided session identity first, Request Context.sessionId second, and derived context identity last.
  • Let Request Context Preparation own the lazy capability refresh trigger after it creates new Template Server Instances. Keep the actual capability rebuild behind the existing lazy-loading dependency, and skip refresh for routing-only sessions or requests that only touch already-prepared template instances.
  • Return a structured preparation result rather than only sessionId | undefined, so callers and tests can distinguish no context, routing-only session, already-prepared context, and newly-created template instances without inferring behavior from a nullable string.
  • When a real Request Context is present, register missing Template Server adapters for the rendered template set even if every Template Server Instance for the resolved Request Session already exists. Registration is part of template-aware routing and inspection; instance creation remains limited to pending templates.
  • Keep bare /api/inspect listings non-instantiating. They should not run Request Context Preparation even when request context data is present; they may list declared Template Servers without creating Template Server Instances.
  • Let targeted /api/inspect?target=... use a header-only mcp-session-id as a routing-only Request Session, matching /api/tools. This may resolve already-prepared session-specific template connections, but still must not create Template Server Instances without a real Request Context.
  • Include the stdio proxy path in the design: proxy is a Client Surface that carries Request Context in MCP _meta over Streamable HTTP, and Streamable HTTP session creation currently prepares contextual templates through ServerManager.connectTransport.
  • Migrate both REST template-aware surfaces in the first implementation slice: targeted inspect and tool list/invocation should call the same Request Context Preparation module, while bare inspect listings keep their non-instantiating path. Treat Streamable HTTP/proxy integration as a second slice unless the first slice exposes a clear compatibility wrapper for ServerManager.connectTransport.
  • Give the module a narrow runtime dependency object rather than the full ServerManager. The route layer may adapt from ServerManager initially, but the preparation module should depend only on rendered-template loading, template manager operations, template adapter registration, outbound client/transport access, and lazy capability refresh.
  • Keep unexpected preparation failures as thrown errors in the first slice, preserving current route-level 500 handling. The structured preparation result should describe successful preparation/routing states, not replace route input validation or introduce a new error transport contract.

Implementation plan:

  1. Add src/core/server/requestContextPreparation.ts with normalized inputs, structured successful result states, session precedence resolution, template adapter registration, pending-template creation, ephemeral-session touch, and lazy capability refresh ownership.
  2. Add focused module tests for no context, routing-only header session, derived session, header-over-context precedence, pending template creation, already-prepared template touch, adapter registration without duplicate instance creation, and refresh-only-after-new-instances.
  3. Migrate targeted inspect plus /api/tools list/invocation to the shared module, preserving bare /api/inspect as non-instantiating and adding route tests for targeted inspect header-only routing.
  4. Audit ServerManager.connectTransport and Streamable HTTP session creation for the proxy path. Either adapt it to the shared preparation module in a second slice or document why long-lived MCP transport initialization keeps a separate preparation path.

Benefits: the ordering facts move behind one Seam. Tests become focused on context present or absent, header-only session, pending templates, lazy refresh, targeted inspect routing, REST/proxy consistency, and long-lived Streamable HTTP session compatibility.

3. Server Installation Workflow Module

Files: src/commands/mcp/install.ts, src/core/tools/internal/installationHandlers.ts, src/core/tools/internal/adapters/installationAdapter.ts, src/domains/server-management/serverInstallationService.ts

Problem: CLI and internal MCP tools repeat install, persist, reload, validation, and result-shaping behavior. The Adapter is Shallow by the deletion test.

Solution: deepen one Server Installation Workflow Module; CLI and internal tools become formatting Adapters.

Accepted boundaries:

  • Treat Server Installation Workflow as the canonical owner for resolving registry or direct-install input into an installable Configured Server Target plus structured installation facts. It owns validation, registry/package resolution, local-name derivation, conflict policy, and result shaping, but delegates persisted config writes, backups, and reload observation to Config Change.
  • Keep this opportunity install-only. Registry install, direct package/config install, force/conflict policy, dry-run preview, structured install result, and handoff to Config Change are in scope; update, uninstall, and list workflows remain separate concerns.
  • Expose one normalized workflow input with explicit source variants. Use a registry source for registry-id/version/local-name inputs and a direct source for package, command, URL, transport, environment, and argument inputs. Do not let name ambiguously mean both local configured-server name and registry server id.
  • Return registry prerequisite metadata as first-class non-blocking workflow output. Registry lookup and endpoint selection failures fail the workflow, but prerequisite extraction failures should become warnings when a valid Configured Server Target can still be produced.
  • Resolve install conflicts against Configured Server Target names, not only static mcpServers names. Installs create or replace static mcpServers entries in the first slice. If the requested local name collides with a Template Server in mcpTemplates, return a clear conflict even when force is set; the caller should choose another local name or use a future template-edit workflow. If the local name collides only with a static mcpServers entry, force may replace that static target through Config Change.
  • Make dry-run a real workflow preview, not a CLI-only string formatter. Dry-run should perform validation, registry lookup, endpoint selection, local-name derivation, conflict detection, and Configured Server Target generation, then return a structured preview without invoking Config Change.
  • Expose one workflow entrypoint with mode: 'preview' | 'apply' rather than separate planning and execution APIs. Both modes share validation, resolution, target generation, and conflict handling; only apply mode invokes Config Change and includes its result.
  • Land the workflow in src/domains/installation/serverInstallationWorkflow.ts, alongside the existing install-specific validators, metadata extraction, defaults, and configurators. Keep src/domains/server-management/serverInstallationService.ts as a compatibility facade for old update, uninstall, list, and migrated install callers during the transition.
  • Give the workflow narrow dependency ports instead of letting it create singletons internally. The core workflow should receive ports for registry lookup/search, configured-target conflict lookup, Config Change apply, and optional progress events; CLI and internal-tool factory helpers may wire those ports from ConfigContext, createRegistryClient(), and progress tracking.
  • Wait for the real Config Change module before migrating install persistence. Do not introduce a temporary install-only adapter over existing config helpers; apply mode should use the public Config Change API once No.4 exists.
  • Treat No.3 implementation as blocked on No.4. Do not implement a preview-only workflow before Config Change exists, because preview conflict lookup and apply persistence should share the same configured-target resolution model.
  • Return expected install outcomes as structured statuses rather than thrown exceptions. Use statuses such as preview, applied, exists, template_conflict, invalid_input, not_found, and registry_unavailable; reserve thrown exceptions for programmer errors and unexpected port failures. CLI adapters may turn non-success statuses into exit-code failures, while internal MCP tools should format the same statuses as JSON results.
  • Extend the mcp_install output schema to expose the workflow status vocabulary directly when No.3 is implemented. Do not collapse statuses such as template_conflict, invalid_input, or registry_unavailable into a generic failed result.
  • Validate direct sources strictly by transport shape. A direct static stdio target requires command and may include args, env, cwd, and restart fields; direct http and sse targets require url and may include headers. Treat package as metadata or an explicit convenience for building an npx command, not as the discriminator for whether an install is direct. Invalid combinations should return invalid_input with field-level messages.
  • Preserve the current registry endpoint priority as the default: packages before remotes, npm package before other packages, streamable-http remote before other remotes, then first available endpoint. Workflow inputs may express a preferred endpoint or installation method, but only among endpoints present in registry metadata. Unsatisfied required preferences should return invalid_input; optional preferences may warn and fall back to the default priority.
  • Return installation metadata in the workflow result for CLI and internal-tool formatting, but defer persisted metadata shape. Do not write _metadata, _registry, or a new metadata field into generated Configured Server Targets in the first slice; reconcile the persisted metadata contract separately before storing it in config.

Implementation plan:

  1. Land No.4 first so the public Config Change API owns configured-target mutation, backup, validation, and reload observation.
  2. Add src/domains/installation/serverInstallationWorkflow.ts with normalized source inputs, preview/apply modes, narrow ports, endpoint selection, prerequisite extraction, direct-config generation, static-only conflict policy, transport-shaped direct validation, result-only installation metadata, and structured statuses.
  3. Add focused workflow tests for registry success, endpoint priority and preference handling, direct stdio/http/sse success, invalid direct source combinations, result-only metadata, real dry-run preview, static conflict without force, static replacement with force, template collision refusal, registry not found, registry unavailable, prerequisite extraction warning, and unexpected port failure.
  4. Migrate mcp install direct and interactive execution paths to the workflow result while keeping terminal progress and display formatting in the CLI layer. Do not redesign wizard search, selection, or configuration prompts in this slice; the wizard should hand its final selection to the shared workflow.
  5. Migrate mcp_install and ServerInstallationAdapter.installServer to the workflow while leaving update, uninstall, and list methods on the existing compatibility adapter. Extend McpInstallOutputSchema to expose workflow statuses directly.
  6. Keep src/domains/server-management/serverInstallationService.ts as a compatibility facade during the first slice; remove or shrink its install-specific logic only after both CLI and internal install callers use the workflow.

Benefits: stronger Locality for install semantics and better Leverage across human CLI and MCP tool callers. Tests can target force, dry-run, registry failure, direct package, and reload once.

4. Config Mutation And Reload Module

Files: src/commands/shared/baseConfigUtils.ts, src/core/tools/internal/adapters/management/managementAdapter.ts, src/core/tools/internal/adapters/installation/directInstallation.ts, src/commands/mcp/uninstall.ts

Problem: config read, write, backup, and reload are scattered through globals and file helpers. Reload has weak Locality because callers cannot observe what actually happened.

Solution: deepen a Module around add, update, remove, backup, and reload results.

Accepted boundaries:

  • Treat a config change as the persisted mutation plus the observable reload outcome for the affected runtime scope.
  • Target both static servers and template server definitions; resolve existing duplicate names with template-first precedence.
  • Make backups caller-controlled, defaulting to backups for destructive operations and forced overwrites.
  • After a backup is created, apply backup retention opportunistically: keep the latest 10 matching backups per config file and also support a configurable maximum age, defaulting to 30 days, from config.toml.
  • When both backup retention rules are active, delete a backup if either rule expires it: outside the latest 10 for that config file, or older than the configured maximum age.
  • Do not automatically roll back a persisted config change when reload fails. Report persistence and reload as separate outcomes; restore remains an explicit backup-driven action.
  • Validate the requested configured-server target before mutation, then validate the full resulting config document before writing. Reload failures are reported after a successful write, not treated as write failures.
  • Preserve unknown top-level and server-level config fields when writing. A config change is not a config migration unless explicitly requested.
  • Expose explicit high-level methods for add, update, remove, backup, and reload-facing operations, backed by a shared internal pipeline. Avoid a generic public config-operation DSL.
  • Land the module under a domain area such as src/domains/config-change/. Keep src/commands/shared/baseConfigUtils.ts as a compatibility wrapper during migration rather than the long-term owner.
  • Observe runtime reload through a port boundary. Internal MCP tools can use in-process reload; CLI commands should discover the aggregated runtime for the same runtime scope and request reload when available. Report outcomes such as observed, runtime not running, reload disabled, and reload failed.
  • Serialize writers with a per-config-file lock around read, backup, mutate, validate, write, and backup-retention cleanup. Release the lock before reload observation, and fail with a clear timeout result rather than waiting indefinitely.
  • Treat backup creation failure as a hard failure when backup is required or requested. Treat backup-retention cleanup failure as a non-blocking warning on an otherwise successful config change.
  • Migrate callers incrementally. Use mcp uninstall and the internal remove/uninstall path as the first vertical slice before expanding to install, update, enable, disable, and tool-specific config edits.
  • Return stable structured results from high-level methods, with fields for status, operation, config path, target, changed state, backup, retention cleanup, reload outcome, and warnings. CLI and internal tools should format from this result instead of inferring facts independently.

Implementation plan:

  1. Add src/domains/config-change/ with the public result types, explicit high-level operations, shared mutation pipeline, per-config-file lock, backup creation, backup-retention cleanup, validation hooks, and reload-observation port.
  2. Implement backup retention as an opportunistic post-backup cleanup using latest-10-per-config-file plus configurable max-age rules from config.toml.
  3. Migrate mcp uninstall and the internal remove/uninstall path as the first vertical slice, leaving existing shared config helpers as compatibility wrappers during migration.
  4. Add focused tests for destructive backup defaults, forced-overwrite backup behavior, retention OR semantics, lock timeout behavior, validation-before-write, preservation of unknown fields, reload outcomes, and non-blocking retention cleanup warnings.
  5. Expand the module to install, update, enable, disable, and tool-specific config edits once the uninstall/remove slice is stable.

Benefits: one Seam for config mutation, structured reload facts, less singleton mocking, and cleaner filesystem tests.

5. Capability Snapshot And Catalog Module

Files: src/core/capabilities/capabilityAggregator.ts, src/core/capabilities/asyncLoadingOrchestrator.ts, src/core/capabilities/metaToolProvider.ts, src/core/capabilities/toolRegistry.ts

Problem: callers must know to refresh capabilities, rebuild registries, apply session filters, and suppress notifications in the right order.

Solution: deepen snapshot and catalog behavior around "refresh and return visible capabilities plus routing facts."

Accepted boundaries:

  • Treat Capability Snapshot as the raw point-in-time aggregate of tools, resources, prompts, and ready servers from backend servers before request-specific visibility rules are applied.
  • Treat Capability Catalog as the visibility-aware query surface for tools, resources, prompts, server lists, and routing facts that are visible to a Client Surface for a Request Session and filter context.
  • Let callers ask the Capability Catalog for visible results instead of separately refreshing capabilities, rebuilding the tool registry, applying tag/preset/session filters, applying disabled-tool filtering, normalizing template server names, and deciding whether list-changed notifications should be suppressed.
  • Let Capability Catalog own Capability Refresh policy through explicit per-call intent such as never, ifStale, or force. Callers should not manually sequence refresh followed by a separate registry/catalog query.
  • Return refresh/change facts alongside visible query results so request-context preparation, async server readiness, config changes, and REST/MCP callers can tell whether the underlying Capability Snapshot changed and whether list-changed notifications are appropriate for the current Client Surface.
  • Return internal Capability Routes with visible capability entries so REST routes, meta-tools, and MCP handlers do not separately rediscover clean-name, rendered-hash, session-key, and static-server resolution rules.
  • Keep public capability output on clean names. Capability Routes are runtime facts for invocation and schema lookup, not client-visible server identifiers.
  • Treat disabled-tool rules, tag/preset filters, and session/template availability as inputs into Capability Visibility. The Capability Catalog should both omit invisible tools from listings and reject schema lookup or invocation through the same visibility path.
  • Remove caller-specific splits between filterDisabledTools() for list paths and getDisabledToolError() for invocation paths as callers migrate to the Capability Catalog.
  • Start with a narrow vertical slice. Add src/core/capabilities/capabilityCatalog.ts, keep CapabilityAggregator, ToolRegistry, and MetaToolProvider as internal collaborators, and migrate MetaToolProvider, /api/tools, and /api/tool-invocations before broadening to MCP protocol handlers or notification flows.
  • Leave direct MCP protocol list/call handlers, async server-loaded notifications, and config-change notification migration for a second slice once the Capability Catalog contract has proven its visible-query, routing, refresh, and access-check shape.
  • Expose explicit workflow methods instead of a generic catalog query DSL. First-slice methods should be shaped around real caller needs such as listing visible tools, describing a visible tool, invoking a visible tool, returning MCP-style visible capabilities, and refreshing/querying with change facts where needed.
  • Keep meta-tool output schemas on public clean server/tool fields in the first slice. Internal Capability Routes should not leak into tool_list, tool_schema, or tool_invoke responses.

Implementation plan:

  1. Add src/core/capabilities/capabilityCatalog.ts with public result types for visible capability entries, internal Capability Routes, Capability Refresh intent, refresh/change facts, and visibility/access errors.
  2. Add focused catalog tests before caller migration: visible tool listing, server/tag/session filtering, disabled-tool omission, disabled schema/invoke rejection, static-server route handles, template rendered-hash route handles, per-client session route handles, refresh never/force behavior, and public clean-name output.
  3. Implement the first catalog slice around existing collaborators. Reuse CapabilityAggregator for Capability Snapshot rebuilds, ToolRegistry for lightweight metadata where it still helps, ConnectionResolver for route lookup during the transition, and SchemaCache/schema loading for describe flows.
  4. Migrate MetaToolProvider to call the Capability Catalog for tool_list, tool_schema, and tool_invoke while preserving the current meta-tool input/output schemas and structured error shape.
  5. Migrate REST /api/tools and /api/tool-invocations to the Capability Catalog, removing route-local disabled-tool checks, direct ToolRegistry.fromToolsMap(...) fallback behavior where catalog output can cover it, and duplicate clean-name/template-key resolution.
  6. Keep direct MCP protocol handlers, AsyncLoadingOrchestrator notification handling, ConfigChangeHandler notifications, and broad LazyLoadingOrchestrator cleanup for a second slice. Add adapter shims in the first slice only when needed to keep existing behavior stable.
  7. Verify with targeted tests for capabilityCatalog, metaToolProvider, lazyLoadingOrchestrator, and apiRoutes, plus template-server clean-name coverage that exercises list, describe, and invoke paths through the catalog.

Benefits: higher Leverage for lazy loading and async loading tests; better Locality for clean-name/hash-key and filtering bugs.

6. HTTP Streamable Transport Session Lifecycle Module

Files: src/transport/http/routes/streamableHttpRoutes.ts, src/transport/http/utils/sessionService.ts, src/transport/http/restorableStreamableTransport.ts

Problem: route code owns Streamable Transport Session creation, initialize-only recovery, persistence warnings, restoration, response storage, and error rules. The route Module is Shallow.

Solution: deepen Streamable Transport Session lifecycle resolution so routes mostly delegate.

Accepted boundaries:

  • Treat Streamable Transport Session Lifecycle as the owner of the incoming-request decision tree for Streamable HTTP transport continuity. It resolves whether a request should create a new Streamable Transport Session, use an active transport, restore a persisted session, recover an unknown session through an initialize request, return a missing-session result, or fail.
  • Keep Express route handlers as HTTP adapters. Routes should extract HTTP method, mcp-session-id, request body, validated tag/filter inputs, custom template, and _meta Request Context, then map structured lifecycle results to HTTP responses and SDK handleRequest calls.
  • Return structured lifecycle outcomes such as created, found, restored, initialize_recovered, missing, invalid_request, and failed instead of making routes infer lifecycle state from nullable transports or thrown errors.
  • Land the first slice in src/transport/http/streamableSessionLifecycle.ts because Streamable Transport Session is a transport concept, not a general runtime domain module.
  • Give the module narrow dependency ports rather than the full route environment. It should receive ports for active transport lookup/connect/disconnect, session repository access, optional async notification initialization, transport construction, and lifecycle diagnostics.
  • Keep the current SDK-internal restoration mechanism in the first slice, but isolate it behind a narrow compatibility helper or port. The first slice should not redesign the MCP SDK restoration handshake; it should make success, missing initialize-response data, connection failure, and SDK-internal access failure directly testable.
  • Keep transport.handleRequest(req, res, body) in the route adapter because Express response streaming belongs at the HTTP boundary. Move post-handle initialize bookkeeping into Streamable Transport Session Lifecycle through an explicit method such as recordHandledRequest, so initialize-response persistence stays with the restoration prerequisites.
  • Own cleanup entrypoints while preserving the current semantic split. Abnormal stream disconnect and transport onclose should disconnect only the active transport and preserve persisted Streamable Transport Session state for reconnect; explicit DELETE should delete persisted lifecycle state after the SDK DELETE request is handled.
  • Preserve the relationship between Streamable Transport Session cleanup and pooled Template Server Instances. Disconnecting or deleting a Streamable Transport Session must remove that session from any associated Template Server Instances; instances with no remaining sessions become idle and are released later by the existing template instance idle-cleanup path.
  • Back the lifecycle active-transport cleanup port with ServerManager.disconnectTransport() in the first slice. Do not make Streamable Transport Session Lifecycle depend directly on TemplateServerManager or ClientInstancePool; the template-pool release invariant should stay centralized behind the existing server-manager cleanup path.
  • Preserve first-slice HTTP error mapping while exposing richer internal lifecycle results. Missing sessions and restore-unavailable states should keep the current route-level 404 behavior for GET/DELETE, unknown non-initialize POST should keep the initialize-first 404, missing mcp-session-id on GET/DELETE should remain 400, and unexpected port/programmer failures should remain 500. Internal results should still distinguish reasons such as not_found, missing_initialize_response, connection_failed, and sdk_restore_failed for unit tests and diagnostics.
  • Absorb the current SessionService responsibilities into Streamable Transport Session Lifecycle instead of keeping two overlapping lifecycle abstractions. Delete src/transport/http/utils/sessionService.ts in the first slice if migration stays small; keep only a temporary compatibility wrapper if route/test wiring needs an intermediate step.

Benefits: direct tests for unknown session, initialize recovery, restoration, and persistence without Express-heavy setup.

Implementation plan:

  1. Add src/transport/http/streamableSessionLifecycle.ts with structured lifecycle results, narrow dependency ports, normal/restorable transport construction, active lookup, creation, restoration, initialize recovery, post-handle initialize bookkeeping, and cleanup/delete entrypoints.
  2. Add focused lifecycle unit tests for generated-session POST creation, provided-session initialize recovery, unknown non-initialize POST, active transport lookup, persisted restoration success, missing initialize-response data, connection failure, SDK-internal restore failure, GET/DELETE missing session ID, persistence warnings, record-handled initialize storage, abnormal disconnect preserving persisted state, explicit delete removing persisted state, and active cleanup calling the ServerManager.disconnectTransport() port.
  3. Migrate POST, GET, and DELETE in streamableHttpRoutes.ts together so routes become HTTP adapters that extract inputs, map lifecycle results to existing HTTP responses, call transport.handleRequest, wire disconnect listeners, and call recordHandledRequest or delete cleanup at the right time.
  4. Delete src/transport/http/utils/sessionService.ts and migrate its tests to streamableSessionLifecycle.test.ts if the route migration remains small; otherwise keep it only as a deprecated compatibility wrapper over StreamableSessionLifecycle.
  5. Preserve existing public HTTP behavior and verify with streamableHttpRoutes, streamableSessionLifecycle, streamableSessionRepository, restorableStreamableTransport, and targeted session-restoration e2e coverage.

7. Client Surface Attachment Module

Files: src/commands/run/run.ts, src/commands/inspect/inspect.ts, src/commands/proxy/proxy.ts, src/commands/shared/serveTargetResolver.ts, src/commands/shared/apiClient.ts, src/commands/shared/serveClient.ts

Problem: run and inspect each own a large part of Client Surface attachment behavior: runtime discovery, project-context resolution, Request Context construction, auth-profile loading, REST-vs-MCP selection, cached Streamable Transport Session reuse, stale-session recovery, and session-cache updates. proxy uses some of the same discovery and Request Context setup, but has its own filtering and session identity path. The current command Modules are Shallow because callers must know protocol fallback, cache, auth, and context-ordering details.

Solution: deepen a Client Surface Attachment Module whose Interface prepares a command to communicate with an Aggregated Runtime and returns structured attachment facts. Commands should declare their Client Surface, desired protocol behavior, filtering inputs, and whether they need a fresh or reusable Streamable Transport Session; the module should own discovery, auth profile lookup, context hashing, cache read/write/delete, REST support facts, and stale-session retry decisions.

Accepted boundaries:

  • Treat Client Surface Attachment as the owner of shared attach-only setup for both short-lived command surfaces and long-lived proxy-style surfaces. Do not scope it only to cached run and inspect behavior.
  • Model attachment intent explicitly. Use a reusable-session intent for run, inspect, and later instructions; it owns context hashing, CLI session-cache read/write/delete, REST support caching, stale-session retry, and MCP fallback. Use a fresh-session intent for proxy; it owns runtime discovery, Request Context construction, filter attachment, and generated Request Session without reusing the CLI session cache.
  • Let Client Surface Attachment own the attachment state machine, not command semantics. Commands should still parse their own user intent and format their own output: run keeps tool-reference parsing, stdin/argument validation, and result formatting; inspect keeps target parsing and inspect-result normalization; proxy keeps stdio transport lifecycle and shutdown formatting.
  • Keep REST/MCP fallback decisions, cache mutation, and retry policy inside Client Surface Attachment. Commands should provide narrow protocol adapters such as "try REST inspect", "try MCP inspect", "try REST tool invocation", or "try MCP tool invocation" rather than directly owning fallback order and stale-session cleanup.
  • Land the first slice in src/commands/shared/clientSurfaceAttachment.ts. This module is command-side attachment infrastructure because it depends on serve-target discovery, auth profiles, CLI session-cache paths, REST/MCP command fallback, and CLI Request Context construction; do not put these CLI concerns in runtime core or a domain module.
  • Preserve the current REST-support cache semantics for reusable-session attachments. A successful REST call records REST support; endpoint-missing failures such as missing REST routes or 405 fall back to MCP and persist REST as unsupported; transient or upstream failures such as 503 or network status 0 may fall back to MCP but must not mark REST unsupported; auth failures such as 401 or 403 should surface as authentication errors rather than falling back.
  • Let Client Surface Attachment own reusable-session context hashing. Preserve the current CLI session hash behavior: include meaningful Request Context facts such as client surface transport type, version, project-config context, selected environment variables, and explicit session data, but omit working-directory-only churn such as cwd and PWD.
  • Extract the outer orchestration before extracting low-level protocol helpers. In the first slice, runCommand and getInspectResult should call Client Surface Attachment with command-supplied protocol adapters while existing helpers such as invokeTool and inspectTools remain command-local. Move those helpers only after the first slice proves a stable common result shape.
  • Make src/commands/shared/clientSurfaceAttachment.test.ts the primary first-slice test surface for attachment state-machine behavior. Keep command tests as adapter and user-facing regression coverage instead of duplicating every cache, fallback, and stale-session branch in run and inspect tests.
  • Migrate in slices. Start with run and inspect because they share the deepest cache, REST, and fallback behavior; bring proxy in through the fresh-session path once the shared attachment contract is stable.

Current codebase note (2026-05-25): src/commands/shared/clientSurfaceAttachment.ts and src/commands/shared/clientSurfaceAttachment.test.ts exist, and runCommand plus getInspectResult now call the reusable-session attachment path with command-supplied REST/MCP adapters. proxyCommand now calls the fresh-session attachment path and keeps stdio transport lifecycle/shutdown local. The first slices keep invokeTool, inspectTools, and StdioProxyTransport command/transport-local. instructions remains deferred until No.10 settles instruction-distribution policy.

Implementation plan:

  1. Add src/commands/shared/clientSurfaceAttachment.ts with typed attachment intents for reusable-session and fresh-session surfaces, structured attachment facts, command-supplied protocol adapter contracts, and narrow ports for serve-target discovery, auth-profile lookup, context building, and session-cache persistence.
  2. Add focused clientSurfaceAttachment.test.ts coverage for matching and mismatched cache reads, legacy cache rejection, context hashing that omits working-directory-only churn, REST success support caching, endpoint-missing REST fallback with REST-disabled persistence, transient REST fallback without REST-disabled persistence, auth failure without fallback, stale cached-session retry, and fresh-session intent avoiding CLI cache reads/writes.
  3. Migrate runCommand to the reusable-session attachment path while keeping tool parsing, stdin/argument validation, existing invokeTool behavior, output formatting, and exit-code mapping in the run command adapter.
  4. Migrate getInspectResult to the reusable-session attachment path while keeping target parsing, inspect-result normalization, instruction extraction, and existing inspectTools behavior in the inspect command adapter.
  5. Trim duplicated cache/fallback tests from run and inspect only after equivalent shared-module tests exist; retain command-level regression tests for user-facing behavior, adapter wiring, auth messaging, and output shape.
  6. Add the fresh-session attachment path for proxy after the reusable-session slice is stable, preserving generated session identity, stdio transport lifecycle, filter forwarding, and shutdown behavior.
  7. Revisit instructions once No.10 settles instruction-distribution policy; migrate it to reusable-session attachment only if its runtime-inspection behavior matches the shared state machine.

Benefits: better Locality for attach-only command bugs and more Leverage across run, inspect, instructions, and proxy. Tests can cover runtime discovery, auth, Request Context hashing, REST support cache, MCP fallback, and stale-session recovery once instead of repeating command-heavy mocks.

8. Filter Selection Module

Files: src/commands/serve/serve.ts, src/transport/http/middlewares/tagsExtractor.ts, src/transport/http/middlewares/scopeAuthMiddleware.ts, src/core/filtering/filteringService.ts, src/core/filtering/templateFilteringService.ts, src/domains/preset/manager/presetManager.ts, src/commands/mcp/tokens.ts

Problem: preset, tag-filter, filter, and tags are parsed, normalized, prioritized, and validated in several places. HTTP middleware writes partial facts into res.locals, scope validation re-extracts tags from expressions, serve handles preset environment variables separately, and token/listing paths evaluate filters again. The Interface leaks filtering priority, legacy compatibility rules, validated tags, preset JSON queries, and advanced-expression conversion.

Solution: deepen one Filter Selection Module whose Interface is "turn command or HTTP query inputs into a validated filtering intent." The module should own selector precedence, preset resolution, tag sanitization, legacy filter compatibility, requested-tag extraction for scope checks, and conversion into the InboundConnectionConfig fields consumed by runtime filtering. HTTP middleware and CLI commands become Adapters that map inputs and errors.

Accepted boundaries:

  • Preserve strict mutual exclusion for a single filter input source. If one HTTP request or command input supplies more than one selector among preset, tag-filter, legacy filter, and tags, return a structured validation error instead of silently applying precedence.
  • Preserve legacy filter compatibility only when it is the sole selector. Parse it as an advanced expression first, then fall back to simple comma-separated tags.
  • Preserve existing layering only where the inputs come from separate sources before final validation, such as ONE_MCP_PRESET overriding CLI --filter in stdio serve startup or config/default merging producing one final selector.
  • Return exactly one final Filter Selection intent: preset, advanced, simple-or, or none.
  • Return scope-validation facts with the selected filter intent instead of making scopeAuthMiddleware re-parse downstream state. The result should include runtime filtering payload fields such as tags, tagExpression, tagQuery, presetName, and tagFilterMode, plus requestedTags for OAuth scope checks.
  • Correct preset scope validation as part of the migration. For advanced, derive requestedTags from the parsed expression; for simple-or, use the sanitized tag list; for preset, derive requestedTags from the preset tagQuery rather than only backward-compatible tags, so preset-selected tagged servers still require matching tag scopes.
  • Keep scope validation fail-closed for negative filters. requestedTags should include every referenced tag, including tags that appear only under not / ! clauses, so a token cannot use negative predicates to learn about out-of-scope tags.
  • Keep user filter intent separate from authorization narrowing. Filter Selection should return none when the user supplies no selector; auth middleware may still narrow the runtime Server Candidate Set to granted tag scopes so scoped tokens cannot enumerate out-of-scope servers.
  • Keep Filter Selection type-agnostic about server origin. Filtering should not need to know whether a candidate is a static MCP server or a Template Server Instance; callers should build the Server Candidate Set for the relevant Request Session by combining static servers and session-available template instances, then apply the selected filtering intent to that combined set.
  • Do not make Filter Selection construct the Server Candidate Set in the first slice. Candidate-set construction depends on runtime state, prepared template instances, and later Capability Catalog routing; Filter Selection should produce the intent, while runtime callers pass the session-scoped candidate set to existing filtering services or future Capability Visibility.
  • Land Filter Selection in src/core/filtering/filterSelection.ts. It is shared filtering infrastructure for HTTP middleware, serve, SSE/Streamable session config, token/listing tools, and future Capability Visibility; do not put selector resolution in command-only helpers or in the preset domain.
  • Use narrow preset lookup ports instead of calling PresetManager.getInstance() inside Filter Selection. HTTP and CLI adapters should wire existing preset-manager behavior while the module owns selector validation, preset-result normalization, requested-tag extraction, and InboundConnectionConfig conversion.
  • Migrate HTTP filtering through a compatibility shape. tagsExtractor should write one canonical res.locals.filterSelection plus existing compatibility locals such as tags, tagExpression, tagQuery, tagFilterMode, presetName, and tagWarnings; scopeAuthMiddleware should move first to filterSelection.requestedTags, while route helpers can migrate from compatibility locals in later slices.

Implementation plan:

  1. Add src/core/filtering/filterSelection.ts with typed selector inputs, result variants for preset, advanced, simple-or, and none, structured validation errors, compatibility-locals output, requested-tag extraction, and InboundConnectionConfig conversion.
  2. Add narrow preset lookup ports that can return preset name, strategy, tagQuery, expression text when available, and not-found or invalid-preset facts without letting Filter Selection create a PresetManager singleton.
  3. Add focused filterSelection.test.ts coverage for strict selector mutual exclusion, legacy filter advanced-first/simple-fallback parsing, invalid selector types, tag sanitization errors and warnings, preset not found, preset invalid query, requested tags from simple, advanced, negative advanced, preset JSON queries, and none intent.
  4. Migrate tagsExtractor to call Filter Selection, map structured validation errors to existing HTTP 400/500 responses, write res.locals.filterSelection, and keep compatibility locals for existing routes.
  5. Migrate scopeAuthMiddleware to validate filterSelection.requestedTags, including preset and negative-clause tags, while preserving the existing auth-granted-tags narrowing behavior when no explicit filter was selected.
  6. Migrate serve startup filtering to the same module, preserving ONE_MCP_PRESET as the separate-source override before final selector validation and keeping current logging/error behavior.
  7. Migrate mcp tokens and other command surfaces that parse preset or tag-filter to use Filter Selection for selector validation while leaving their candidate-set construction and output formatting local.
  8. Leave Server Candidate Set construction and filter evaluation in existing runtime services for the first slice; migrate route helpers and future Capability Visibility to consume the canonical filter-selection result after compatibility locals have settled.

Benefits: stronger Locality for tag and preset behavior, plus more Leverage for Capability Visibility, Request Context Preparation, and Streamable Transport Session persistence tests. It also reduces the chance that serve, REST, SSE, token tooling, and template filtering disagree about the same selector.

9. OAuth Authorization Flow Module

Files: src/auth/sdkOAuthServerProvider.ts, src/auth/storage/oauthStorageService.ts, src/transport/http/routes/oauthRoutes.ts, src/transport/http/routes/cliTokenRoute.ts, src/transport/http/middlewares/scopeAuthMiddleware.ts

Problem: SDKOAuthServerProvider owns SDK OAuth hooks, authorization-code exchange, token verification, token revocation, consent rendering, and storage construction, but HTTP routes still reach into oauthProvider.oauthStorage for consent approval/denial and CLI-token creation. The route layer also owns parts of OAuth dashboard status, server reconnect initiation, and HTML rendering. The seam is real, but under-deepened: callers still know repository keys, token creation details, and consent-processing order.

Solution: deepen the existing OAuth Module so routes ask it for structured authorization-flow operations: submit consent, deny consent, create localhost CLI tokens, summarize backend OAuth status, start or restart backend OAuth, and render or return consent/dashboard view models. Keep Express routes as HTTP Adapters and keep SDK-specific provider methods behind the OAuth Module's public Interface.

Accepted boundaries:

  • Treat OAuth Authorization Flow as the owner of consent and token-creation storage mutations. Routes must not read or write oauthProvider.oauthStorage directly for consent approval, consent denial, authorization-request lookup, client lookup, or localhost CLI token creation.
  • Keep routes as HTTP adapters. They may validate transport-specific input shape, enforce localhost-only access for CLI token creation, apply rate limiting, call structured OAuth operations, and map outcomes to redirects, HTTP status codes, or JSON responses.
  • Keep SDK-required OAuth hooks on SDKOAuthServerProvider, including authorize, exchangeAuthorizationCode, verifyAccessToken, and revokeToken.
  • Expose structured operations such as submitConsent, denyConsent, createLocalhostCliToken, and consent-request view lookup through the OAuth Module interface instead of exposing storage repositories.
  • Split rendering responsibilities by risk. Keep consent-page rendering and CSP in SDKOAuthServerProvider.authorize for the first slice because the SDK hook already owns the response boundary, but move consent submission into structured OAuth operations. For the OAuth dashboard, let the OAuth Module return a dashboard view model while oauthRoutes keeps initial HTML rendering.
  • Return structured consent submission outcomes such as approved_redirect, denied_redirect, invalid_request, invalid_client, invalid_scope, and expired_request; routes should map those outcomes to redirects or HTTP responses.
  • Include backend OAuth management in the OAuth Module interface through runtime ports. Expose operations such as summarizeBackendOAuthStatus, startBackendOAuth, restartBackendOAuth, and completeBackendOAuthCallback, but keep transport recreation, outbound connection updates, and loading-state mutation behind ServerManager, ClientManager, and loading-manager ports.
  • Return structured backend OAuth outcomes such as service_not_found, authorization_url_ready, authorization_url_missing, restarted, callback_completed, and callback_failed; routes should map those outcomes instead of directly mutating client status, authorizationUrl, or oauthStartTime.
  • Land the first slice in src/auth/oauthAuthorizationFlow.ts. This is auth-domain orchestration, not HTTP route code or client runtime code.
  • Make src/auth/oauthAuthorizationFlow.test.ts the primary test surface for consent submission, localhost CLI token creation, backend OAuth status summaries, start/restart outcomes, and callback completion mapping. Keep oauthRoutes.test.ts focused on adapter behavior, while storage, provider, and client OAuth tests keep their lower-level responsibilities.
  • Migrate with a temporary compatibility path if needed. oauthStorage may remain public during the first slice only for unmigrated internals, but the target state is private storage behind the OAuth Module interface once oauthRoutes and cliTokenRoute no longer touch it.

Implementation plan:

  1. Add src/auth/oauthAuthorizationFlow.ts with structured result types, narrow ports for OAuth storage, auth config, available tags, runtime client lookup/start/restart/callback completion, and optional loading-state updates.
  2. Add focused oauthAuthorizationFlow.test.ts coverage for missing consent fields, expired authorization requests, missing clients, approve with selected scopes, deny, invalid scopes, audit facts, localhost CLI token creation, disabled-auth CLI-token response, backend dashboard summary, authorize with existing URL, authorize by initiating backend OAuth, restart, callback success, callback OAuth error, callback missing code, and callback failure.
  3. Move consent submission logic out of oauthRoutes.ts into the new flow module while preserving existing redirects, error payloads, rate limiting, and consent-page rendering/CSP in SDKOAuthServerProvider.authorize.
  4. Move localhost CLI token creation out of cliTokenRoute.ts into the new flow module. Keep localhost detection and HTTP response mapping in the route, but move token id generation, TTL selection, available-scope calculation, session creation, and token response facts behind the flow.
  5. Move OAuth dashboard data gathering plus authorize/restart/callback decision trees into flow operations backed by runtime ports. Keep dashboard HTML rendering in oauthRoutes.ts for the first slice.
  6. Update oauthRoutes.test.ts to focus on adapter mappings from structured flow outcomes to redirects, status codes, JSON, and dashboard HTML; avoid asserting repository details through route tests.
  7. Make SDKOAuthServerProvider.oauthStorage private after oauthRoutes and cliTokenRoute stop using it directly, or leave it as a deprecated compatibility escape hatch only until all direct repository access has been migrated.

Benefits: better Locality for security-sensitive token and consent behavior, with more Leverage for tests around invalid consent requests, selected scopes, localhost CLI token generation, token TTL, audit events, backend OAuth restart, and route error mapping. It narrows the Interface that can mutate OAuth storage directly.

10. Instructions Distribution Module

Files: src/commands/instructions/instructions.ts, src/commands/cliSetup/setupFiles.ts, src/core/instructions/instructionAggregator.ts, src/commands/inspect/inspect.ts

Problem: instruction behavior is split between runtime aggregation, instructions command formatting, inspect fallback behavior, and startup-doc/hook setup. The tricky behavior is distribution, not only template rendering: which Client Surface should receive which instructions, when to eager-inspect unavailable Template Servers, how disconnected server instructions are carried from server listings, and how managed startup references stay current.

Solution: deepen an Instructions Distribution Module that owns instruction collection policy and managed startup-doc content for agent clients. Runtime inspection should provide server facts; the module should decide which server details require eager inspection, how unavailable or disconnected server instructions are represented, and what managed bootstrap content gets written for each supported client. instructions and cli-setup remain formatting/file-writing Adapters.

Accepted boundaries:

  • Treat Instructions Distribution as policy for delivering server instructions and managed bootstrap content to a Client Surface, not as the owner of final CLI display formatting.
  • Let Instructions Distribution decide which servers need eager inspection, how disconnected, unavailable, or template instruction states are represented, how cached server-list instructions are used, and what managed bootstrap content is generated for Codex and Claude.
  • Keep instructionsCommand as a CLI adapter. It should obtain inspection facts, call Instructions Distribution, and then format output through existing instruction-output formatting.
  • Keep cli-setup as a file-writing adapter. It should ask Instructions Distribution for managed document content and startup-reference policy, then write hooks, startup docs, and managed docs.
  • Keep InstructionAggregator as runtime aggregation and template rendering for connected server instructions; Instructions Distribution consumes inspection/runtime facts rather than replacing runtime aggregation.
  • Preserve the existing managed startup-doc reference policy. Global Codex AGENTS.md should reference the managed doc with an absolute @.../.codex/1MCP.md path; repo Codex can use the repo-local managed doc reference; Claude startup docs can keep local/relative managed doc references.
  • Keep legacy managed-reference cleanup as part of the policy. When the canonical managed reference is absolute, old @1MCP.md references should be removed or replaced rather than duplicated.
  • Leave file writes, JSON hook merging, and idempotent disk updates in cli-setup; Instructions Distribution should own managed content and startup-reference decisions, not filesystem mutation.
  • Preserve the current eager-inspection policy. Template Servers should be eagerly inspected because their instructions may be contextual to the current Request Context; connected non-template servers may also be eagerly inspected for accurate details; disconnected non-template servers should not be eagerly inspected.
  • Preserve instruction fallback behavior. If template eager inspection succeeds, use contextual server-detail instructions; if it fails, emit the template-specific note (unavailable: template server could not be initialized with the current context). For disconnected non-template servers, use cached serverInstructions from the all-server listing when present and include (unavailable: server is not currently connected).
  • Do not change the public instructions command output shape in the first slice.
  • Land Instructions Distribution in src/core/instructions/instructionsDistribution.ts, beside InstructionAggregator but separate from runtime aggregation and template rendering.
  • Make src/core/instructions/instructionsDistribution.test.ts the primary policy test surface for eager-inspection decisions, detail assembly from inspect results, cached disconnected instructions, template unavailable notes, managed doc content, startup reference rendering for Codex and Claude global/repo scopes, and legacy reference cleanup. Keep command and setup tests focused on adapter wiring, output formatting, hooks, and file writes.

Implementation plan:

  1. Add src/core/instructions/instructionsDistribution.ts with typed server-summary/detail inputs, eager-inspection decisions, detail assembly results, managed doc content rendering, startup-reference rendering, and legacy-reference cleanup helpers.
  2. Add focused instructionsDistribution.test.ts coverage for template eager inspection, connected static eager inspection, disconnected static skip, cached disconnected instructions, no-instructions notes, template initialization failure notes, unchanged public detail shape, global Codex absolute managed references, repo Codex references, Claude references, and legacy @1MCP.md cleanup.
  3. Refactor instructionsCommand so it keeps inspect calls and final formatInstructionsOutput formatting, but delegates eager-inspection decisions and detail assembly to Instructions Distribution.
  4. Refactor setupFiles.ts so managed document content, startup managed block rendering, and managed-reference cleanup come from Instructions Distribution, while path resolution, hook JSON merging, and file writes remain in cli-setup.
  5. Keep InstructionAggregator unchanged in the first slice except for importing shared content/policy helpers if needed; do not move runtime template rendering into Instructions Distribution.
  6. Preserve existing instructions, cli-setup, and setup-file tests as adapter/regression coverage while moving duplicated policy assertions into instructionsDistribution.test.ts.
  7. Revisit integration with No.7 Client Surface Attachment after the distribution policy is stable, so instructions can share runtime attachment/cache behavior without changing instruction-distribution semantics.

Benefits: better Locality for agent bootstrap regressions and more Leverage for tests covering unavailable template instructions, disconnected server cached instructions, global vs repo-scoped startup docs, managed block updates, and Client Surface-specific playbooks.

Released under the Apache 2.0 License.