LearnNewsExamplesServices
Frontmatter
id10986
titleMigrate Tier-1 MCP services to flat SDK boundary
stateClosed
labels
enhancementairefactoringarchitecture
assigneesneo-opus-4-7
createdAtMay 8, 2026, 9:38 PM
updatedAtMay 12, 2026, 4:09 AM
githubUrlhttps://github.com/neomjs/neo/issues/10986
authorneo-opus-4-7
commentsCount1
parentIssue10960
subIssues
10991 Migrate knowledge-base services to flat SDK boundary
10993 Migrate github-workflow services to flat SDK boundary
10994 Migrate neural-link services to flat SDK boundary
10996 Migrate memory-core services + managers to flat SDK boundary
11005 Align Neo classNames with flat SDK locations (M6 follow-up sweep)
subIssuesCompleted5
subIssuesTotal5
blockedBy[]
blocking[]
closedAtMay 9, 2026, 2:04 PM

Migrate Tier-1 MCP services to flat SDK boundary

Closedenhancementairefactoringarchitecture
neo-opus-4-7
neo-opus-4-7 commented on May 8, 2026, 9:38 PM

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:

  1. 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.
  2. 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.
  3. 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:

  1. Sub-issue 1: knowledge-base — Move ai/mcp/server/knowledge-base/services/*.mjsai/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.
  2. Sub-issue 2: github-workflow — Same pattern. 10 services. Stable scope (gh-CLI dependency surface unchanged).
  3. 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.
  4. 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

  • AC1: 4 Tier-1 sub-issues filed under this epic (KB, GH-WF, NL, MC), each with its own AC list and challenge chain.
  • AC2: 4 sub-issue PRs sequenced (KB → GH-WF → NL → MC) so review surface stays bounded.
  • AC3: All 4 Tier-1 service directories migrated to ai/services/<server>/; old ai/mcp/server/<server>/services/ directories removed for the 4 Tier-1 servers.
  • AC4: ai/services.mjs continues exposing the same <Server>_<ServiceName> namespaces (consumer-side contract preserved); only the import paths change.
  • AC5: Operator scripts + daemons + test fixtures consume services unchanged (no code changes required at consumer sites; aliases hold).
  • AC6: Per-server Server.mjs files reduced further post-M6 (target: services-related LOC moves out; Server.mjs retains only server-specific helper logic + extension hooks).
  • AC7: Per-server healthcheck JSON shape unchanged after migration (snapshot diff captured in each sub-PR).
  • AC8: All existing unit specs pass post-migration (per-server suites + cross-cutting BaseServer.spec).
  • AC9: Integration row green post-migration on each sub-PR.
  • AC10: Documentation: short note in learn/agentos/ describing the post-M6 SDK shape (where services live; how external consumers import).
  • AC11: Tier-2 file-system explicitly documented as OUT of M6 scope; stays at original location post-epic.

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")

tobiu referenced in commit dc43731 - "refactor(github-workflow): migrate services to flat SDK boundary (#10993) (#10997) on May 9, 2026, 12:35 AM
tobiu referenced in commit 39e0802 - "feat(docs): ADR-link enrichment for Structural Inventory (#10449 sub-2) (#11012) on May 9, 2026, 4:34 PM