Context
Filed 2026-05-08 as the M6 prep epic on the v13 release path. M2 substrate landed earlier today (#10965 closed; all 5 servers extending Neo.ai.mcp.server.BaseServer); M3-M5 sibling milestones also complete (#10956 Orchestrator daemon, #10972 NEO_MC_PRIMARY retirement). M6 was previously blocked on M2's uniform extension surface — that gate is now cleared, and the M2 closeout comment explicitly notes "M6 SDK migration (per-server, Tier-1 only) unblocked — when M6 lane picks up, each server has a uniform extension surface to expose externally."
Per learn/agentos/v13-path.md §3 D4 + §4 M6:
Services move OUT of ai/mcp/server/<server>/services/* into a flatter SDK structure. The MCP server's Server.mjs imports them via the SDK rather than directly. This decouples MCP transport from service implementation — services can be consumed by daemons, operator scripts, integration tests, AND MCP tool dispatch via uniform SDK boundary.
This epic is the parent for per-server M6 migration sub-issues — one per Tier-1 server (knowledge-base, memory-core, github-workflow, neural-link). Tier-2 file-system is OUT of M6 scope per #10965 ("local-only; NOT a deployment target").
The Problem
Today, all MCP server services live under ai/mcp/server/<server>/services/. ai/services.mjs imports them via long paths and re-exports under <Server>_<ServiceName> namespace prefixes (e.g., Memory_SessionService, KB_QueryService, GH_IssueService, NeuralLink_ComponentService). This works — ai/services.mjs is mature SDK substrate consumed by operator scripts, daemons, test fixtures, and the MC server's own processPendingSummarizations path — but the on-disk shape conflates "service implementation" with "MCP transport endpoint."
Three concrete frictions:
- External consumers (operator scripts, the M3 Orchestrator daemon, integration test fixtures, peer MCP servers consuming each other's services) import via deeply-nested per-server paths instead of from a flat SDK root. The existing
ai/services/ConceptService.mjs (Concept Ontology) demonstrates the target shape — flat, owned by SDK boundary, not coupled to any MCP server's directory tree.
- Server.mjs files still own service-instantiation order even after M2's BaseServer migration. Per-server
getDependentServices overrides return service instances directly rather than referencing a flat SDK registry. This couples server bootstrap to service location.
- Daemon and operator-script consumers import from
ai/services.mjs (the prefix-namespaced re-export layer) — works today, but bakes the per-server-prefix convention into the SDK contract. The flat shape on disk lets that contract evolve without requiring physical service relocation later.
A flat SDK shape resolves all three: each Tier-1 server exposes its services at ai/services/<server>/<ServiceName>.mjs (matching the existing Concept Ontology convention), ai/services.mjs becomes a re-export aggregator with stable alias contracts, and per-server Server.mjs files become the thin endpoint wrappers the v13 vision describes.
The Architectural Reality
Current state (verified 2026-05-08 via filesystem + ai/services.mjs inspection):
| Server |
Services count |
Location |
| memory-core |
13+ services + lifecycle/ + managers/ subdirs |
ai/mcp/server/memory-core/services/ |
| knowledge-base |
10 services |
ai/mcp/server/knowledge-base/services/ |
| github-workflow |
10 services |
ai/mcp/server/github-workflow/services/ |
| neural-link |
9 services |
ai/mcp/server/neural-link/services/ |
| file-system (Tier-2, OUT of scope) |
2 services |
ai/mcp/server/file-system/services/ |
| Shared |
DestructiveOperationGuard + auth/transport primitives |
ai/mcp/server/shared/services/ |
ai/services.mjs imports each service via long paths and re-exports as <Server>_<ServiceName>. Already consumed by:
- Operator scripts (
ai:summarize-sessions, ai:run-sandman, ai:backup, ai:defrag-*)
- Daemons (
DreamService, the M3 Orchestrator)
- Test fixtures
- The MC server's own SDK-boundary path (
processPendingSummarizations)
Existing flat SDK precedent:
ai/services/ConceptService.mjs — already at the target shape (non-MCP service, flat root)
- Concept Ontology demonstrates the pattern works empirically
M2 BaseServer substrate (#10965):
- All 5 servers extend
Neo.ai.mcp.server.BaseServer
- Per-server
getDependentServices() override returns service-instance array
Server.mjs files reduced to 61–417 LOC each post-M2 (file-system 61, neural-link 149, github-workflow 110, knowledge-base 139, memory-core 417 — the latter still carries memory-core-specific helper logic)
- M6 brings the next reduction: services move OUT of per-server dirs into the flat SDK root,
getDependentServices references SDK paths
The Fix
Per-Tier-1-server migration tickets, sequenced linearly to keep PR review surface bounded:
- Sub-issue 1: knowledge-base — Move
ai/mcp/server/knowledge-base/services/*.mjs → ai/services/knowledge-base/*.mjs. Update ai/services.mjs imports + KB Server.mjs getDependentServices override + any operator-script consumers. 10 services; smallest cloud-native server; validates the migration shape before tackling memory-core.
- Sub-issue 2: github-workflow — Same pattern. 10 services. Stable scope (gh-CLI dependency surface unchanged).
- Sub-issue 3: neural-link — Same pattern. 9 services. Custom boot order from M2 (
boot() seam: transport-before-services per #10975) preserved — services migrate, boot order does not.
- Sub-issue 4: memory-core — Last. Most complex (13+ services +
lifecycle/ subdir + managers/ subdir). Migration shape proven on the prior 3 servers.
Each sub-issue gets its own Fat Ticket with the per-server file listing, AC list, and challenge chain at filing time. Templated AC shape (per-sub-issue):
- All services moved to
ai/services/<server>/<ServiceName>.mjs
- Old
ai/mcp/server/<server>/services/ directory removed
ai/services.mjs import paths updated (continues re-exporting under <Server>_<ServiceName> aliases for backwards compat)
- Per-server
Server.mjs getDependentServices override updated
- Operator-script + daemon + test consumers updated (none should require code changes; aliases hold)
- All existing per-server unit specs pass post-migration
- Integration row green post-migration
- Healthcheck JSON shape unchanged
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
ai/services/<server>/<ServiceName>.mjs paths |
This epic + per-server sub-issues |
Flat SDK location for Tier-1 server services |
None — physical move |
Short note in learn/agentos/ describing post-M6 SDK shape |
ls ai/services/<server>/ confirms move; old ai/mcp/server/<server>/services/ removed |
ai/services.mjs re-export aliases |
This epic |
Continue exposing <Server>_<ServiceName> alias names; only the imports change paths |
Aliases preserved → consumer code unchanged |
JSDoc on ai/services.mjs |
Operator scripts + daemons run unchanged post-migration |
Per-server Server.mjs getDependentServices |
M2 BaseServer extension contract (#10965) |
Returns service instances imported from new SDK paths |
Migration touches imports only; method signature unchanged |
Per-server JSDoc |
Per-server unit specs pass |
| Tier-2 file-system server |
OUT of M6 scope per #10965 "Out of Scope" framing |
NOT migrated — local-only, deployment-irrelevant |
N/A |
Note in this epic |
Stays at ai/mcp/server/file-system/services/ |
Acceptance Criteria
Out of Scope
- Tier-2 file-system migration — explicitly OUT per #10965 framing ("local-only; NOT a deployment target"). May get its own ticket later if substrate value emerges; not part of v13 release path.
- Removing
<Server>_<ServiceName> namespace prefixes from ai/services.mjs — backwards-compat aliases preserved through M6. A future ticket may reshape the namespace-flatness once external consumers settle, but M6 keeps consumer contracts stable.
- Cross-server service-dependency reshape — moving services to flat root does NOT introduce new cross-server coupling. Each service stays single-server-owned. Cross-server intra-talk patterns (if any emerge) are separate tickets.
- Renaming services during migration — physical move only. Service names + class names stay identical. Renames are separate tickets if needed.
- Touching
ai/daemons/ — daemon services already at the flat-ish ai/daemons/services/ root per M3/M4 work. Not part of M6.
- Touching
ai/mcp/server/shared/services/ — shared cross-cutting services (e.g., DestructiveOperationGuard, AuthService, TransportService) stay at the shared boundary, not migrated to per-server SDK paths.
- #10103 SDK-layer config migration — separate ticket; sibling work (config-file dual of M6's services migration); will reference + coordinate via cross-link, but not subsumed.
Avoided Traps / Gold Standards Rejected
- Rejected: migrate all 5 servers in M6 (including file-system). Tier-2 local-only is NOT a deployment-target SDK surface; the substrate value of moving 2 file-system services is near-zero against the migration cost. Per #10965 framing.
- Rejected: collapse
ai/services.mjs namespace prefixes during M6. The <Server>_<ServiceName> alias contract is consumed by operator scripts, daemons, and tests. Breaking it during M6 doubles the migration cost. Reshape is a future ticket if the prefixed-namespace shape proves wrong.
- Rejected: migrate services + their SDK-layer config (#10103) in one epic. Different surfaces (service modules vs config files); coupling them inflates PR review surface. Sibling tickets, not subsumption.
- Rejected: file all 4 Tier-1 sub-issues in this turn. Each per-server sub-issue needs its own challenge chain + Fat Ticket structure + duplicate sweep. Sequencing them across turns honors
feedback_substrate_scope_restraint.
- Rejected: rename
ai/services.mjs to ai/sdk.mjs or similar to signal the SDK boundary more loudly. Naming reshape is bikeshed-class; the contract matters more than the name. Defer to a future grooming pass if needed.
- Rejected: move services to
ai/<server>/services/ (server-namespaced flat root) instead of ai/services/<server>/ (services-namespaced). Per existing ai/services/ConceptService.mjs precedent + the M3 Orchestrator's own consumption shape, services-namespaced is the established convention. Server-namespaced flat root would split the SDK across two top-level dirs.
- Rejected: aliases-via-default-export-only.
ai/services.mjs re-exports as named exports for type-tracker friendliness + operator-script ergonomics. Default-only would force consumers to rename per import.
- Rejected: parallel sub-PR cadence (KB + GH-WF + NL + MC concurrently). Each migration touches
ai/services.mjs import paths — concurrent PRs would conflict at the merge point. Linear sequencing keeps the file under single-edit lock.
Related
- Parent epic: #10960 — v13 release tracking (canonical M-milestone tracking)
- Grandparent epic: #9999 — Cloud-Native Knowledge & Multi-Tenant Memory Core (v13 main)
- Predecessor milestone (cleared M6's gate): #10965 — M2 BaseServer common base class (CLOSED)
- Sibling milestones:
- #10956 — M3 Orchestrator daemon (CLOSED)
- #10972 — M5 NEO_MC_PRIMARY retirement (CLOSED)
- Adjacent / sibling SDK work:
- #10103 — SDK-layer config-file migration (config-file dual of M6's services migration; sibling, not sub-issue)
- Strategic anchor:
learn/agentos/v13-path.md §3 D4 + §4 M6
- Sub-issues (to be filed): 4 per-server M6 sub-issues — KB / GH-WF / NL / MC
Origin Session ID: 33ed57b5-10ed-491d-8b8f-ce5f1223ec38
Retrieval Hint: query_raw_memories(query="M6 SDK migration per-server Tier-1 services flat shape ai/services namespace")
Context
Filed 2026-05-08 as the M6 prep epic on the v13 release path. M2 substrate landed earlier today (#10965 closed; all 5 servers extending
Neo.ai.mcp.server.BaseServer); M3-M5 sibling milestones also complete (#10956 Orchestrator daemon, #10972 NEO_MC_PRIMARY retirement). M6 was previously blocked on M2's uniform extension surface — that gate is now cleared, and the M2 closeout comment explicitly notes "M6 SDK migration (per-server, Tier-1 only) unblocked — when M6 lane picks up, each server has a uniform extension surface to expose externally."Per
learn/agentos/v13-path.md§3 D4 + §4 M6:This epic is the parent for per-server M6 migration sub-issues — one per Tier-1 server (
knowledge-base,memory-core,github-workflow,neural-link). Tier-2file-systemis OUT of M6 scope per #10965 ("local-only; NOT a deployment target").The Problem
Today, all MCP server services live under
ai/mcp/server/<server>/services/.ai/services.mjsimports them via long paths and re-exports under<Server>_<ServiceName>namespace prefixes (e.g.,Memory_SessionService,KB_QueryService,GH_IssueService,NeuralLink_ComponentService). This works —ai/services.mjsis mature SDK substrate consumed by operator scripts, daemons, test fixtures, and the MC server's ownprocessPendingSummarizationspath — but the on-disk shape conflates "service implementation" with "MCP transport endpoint."Three concrete frictions:
ai/services/ConceptService.mjs(Concept Ontology) demonstrates the target shape — flat, owned by SDK boundary, not coupled to any MCP server's directory tree.getDependentServicesoverrides return service instances directly rather than referencing a flat SDK registry. This couples server bootstrap to service location.ai/services.mjs(the prefix-namespaced re-export layer) — works today, but bakes the per-server-prefix convention into the SDK contract. The flat shape on disk lets that contract evolve without requiring physical service relocation later.A flat SDK shape resolves all three: each Tier-1 server exposes its services at
ai/services/<server>/<ServiceName>.mjs(matching the existing Concept Ontology convention),ai/services.mjsbecomes a re-export aggregator with stable alias contracts, and per-serverServer.mjsfiles become the thin endpoint wrappers the v13 vision describes.The Architectural Reality
Current state (verified 2026-05-08 via filesystem +
ai/services.mjsinspection):lifecycle/+managers/subdirsai/mcp/server/memory-core/services/ai/mcp/server/knowledge-base/services/ai/mcp/server/github-workflow/services/ai/mcp/server/neural-link/services/ai/mcp/server/file-system/services/DestructiveOperationGuard+ auth/transport primitivesai/mcp/server/shared/services/ai/services.mjsimports each service via long paths and re-exports as<Server>_<ServiceName>. Already consumed by:ai:summarize-sessions,ai:run-sandman,ai:backup,ai:defrag-*)DreamService, the M3Orchestrator)processPendingSummarizations)Existing flat SDK precedent:
ai/services/ConceptService.mjs— already at the target shape (non-MCP service, flat root)M2 BaseServer substrate (#10965):
Neo.ai.mcp.server.BaseServergetDependentServices()override returns service-instance arrayServer.mjsfiles reduced to 61–417 LOC each post-M2 (file-system 61, neural-link 149, github-workflow 110, knowledge-base 139, memory-core 417 — the latter still carries memory-core-specific helper logic)getDependentServicesreferences SDK pathsThe Fix
Per-Tier-1-server migration tickets, sequenced linearly to keep PR review surface bounded:
ai/mcp/server/knowledge-base/services/*.mjs→ai/services/knowledge-base/*.mjs. Updateai/services.mjsimports + KBServer.mjsgetDependentServicesoverride + any operator-script consumers. 10 services; smallest cloud-native server; validates the migration shape before tackling memory-core.boot()seam: transport-before-services per #10975) preserved — services migrate, boot order does not.lifecycle/subdir +managers/subdir). Migration shape proven on the prior 3 servers.Each sub-issue gets its own Fat Ticket with the per-server file listing, AC list, and challenge chain at filing time. Templated AC shape (per-sub-issue):
ai/services/<server>/<ServiceName>.mjsai/mcp/server/<server>/services/directory removedai/services.mjsimport paths updated (continues re-exporting under<Server>_<ServiceName>aliases for backwards compat)Server.mjsgetDependentServicesoverride updatedContract Ledger Matrix
ai/services/<server>/<ServiceName>.mjspathslearn/agentos/describing post-M6 SDK shapels ai/services/<server>/confirms move; oldai/mcp/server/<server>/services/removedai/services.mjsre-export aliases<Server>_<ServiceName>alias names; only the imports change pathsai/services.mjsServer.mjsgetDependentServicesai/mcp/server/file-system/services/Acceptance Criteria
ai/services/<server>/; oldai/mcp/server/<server>/services/directories removed for the 4 Tier-1 servers.ai/services.mjscontinues exposing the same<Server>_<ServiceName>namespaces (consumer-side contract preserved); only the import paths change.Server.mjsfiles reduced further post-M6 (target: services-related LOC moves out;Server.mjsretains only server-specific helper logic + extension hooks).BaseServer.spec).learn/agentos/describing the post-M6 SDK shape (where services live; how external consumers import).Out of Scope
<Server>_<ServiceName>namespace prefixes fromai/services.mjs— backwards-compat aliases preserved through M6. A future ticket may reshape the namespace-flatness once external consumers settle, but M6 keeps consumer contracts stable.ai/daemons/— daemon services already at the flat-ishai/daemons/services/root per M3/M4 work. Not part of M6.ai/mcp/server/shared/services/— shared cross-cutting services (e.g.,DestructiveOperationGuard,AuthService,TransportService) stay at the shared boundary, not migrated to per-server SDK paths.Avoided Traps / Gold Standards Rejected
ai/services.mjsnamespace prefixes during M6. The<Server>_<ServiceName>alias contract is consumed by operator scripts, daemons, and tests. Breaking it during M6 doubles the migration cost. Reshape is a future ticket if the prefixed-namespace shape proves wrong.feedback_substrate_scope_restraint.ai/services.mjstoai/sdk.mjsor similar to signal the SDK boundary more loudly. Naming reshape is bikeshed-class; the contract matters more than the name. Defer to a future grooming pass if needed.ai/<server>/services/(server-namespaced flat root) instead ofai/services/<server>/(services-namespaced). Per existingai/services/ConceptService.mjsprecedent + the M3 Orchestrator's own consumption shape, services-namespaced is the established convention. Server-namespaced flat root would split the SDK across two top-level dirs.ai/services.mjsre-exports as named exports for type-tracker friendliness + operator-script ergonomics. Default-only would force consumers to rename per import.ai/services.mjsimport paths — concurrent PRs would conflict at the merge point. Linear sequencing keeps the file under single-edit lock.Related
learn/agentos/v13-path.md§3 D4 + §4 M6Origin Session ID:
33ed57b5-10ed-491d-8b8f-ce5f1223ec38Retrieval Hint:
query_raw_memories(query="M6 SDK migration per-server Tier-1 services flat shape ai/services namespace")