Context
Filed 2026-05-08 as M6 sub-issue 3 (neural-link) — third per-server SDK migration in the M6 epic #10986 sequence (KB → GH-WF → NL → MC). Sequenced after #10991 (KB) and #10993 (GH-WF) per parent epic linear-cadence rule: each migration touches ai/services.mjs import paths, so concurrent PRs would merge-conflict at the SDK aggregator.
Per parent M6 epic #10986 §The Fix and learn/agentos/v13-path.md §3 D4: services move from ai/mcp/server/neural-link/services/*.mjs → ai/services/neural-link/*.mjs. ai/services.mjs becomes a re-export aggregator with stable <Server>_<ServiceName> alias contracts. M2 BaseServer substrate (#10965) provides the extension contract — but neural-link is the sub-issue with the LARGEST per-server complexity gap from the canonical pattern (see Architectural Reality below).
The Problem
Neural-link services currently live under ai/mcp/server/neural-link/services/ — 9 service files. 7 are exposed via ai/services.mjs's aliasing layer (NeuralLink_ComponentService, NeuralLink_ConnectionService, NeuralLink_DataService, NeuralLink_HealthService, NeuralLink_InstanceService, NeuralLink_InteractionService, NeuralLink_RuntimeService). 2 services (RecorderService, toolService) are internal — not currently SDK-exported.
The friction (per parent epic #10986 framing): same as KB and GH-WF — the on-disk shape conflates "service implementation" with "MCP transport endpoint". This sub-issue normalizes neural-link to the existing ai/services/ConceptService.mjs flat-shape precedent.
Distinct from KB and GH-WF migrations: neural-link's Server.mjs uses a custom boot() override (per #10975 M2 migration — transport-before-services bootstrap order rationale) rather than the canonical getDependentServices() extension hook. Migration must update import paths inside the boot() override AND any imports of services like ConnectionService that NL bootstraps explicitly — broader Server.mjs surface than KB/GH-WF.
The Architectural Reality
Current state (verified 2026-05-08 via ls ai/mcp/server/neural-link/services/ + grep on ai/services.mjs):
| File |
SDK alias (ai/services.mjs) |
SDK-exported |
ComponentService.mjs |
NeuralLink_ComponentService |
✓ |
ConnectionService.mjs |
NeuralLink_ConnectionService |
✓ |
DataService.mjs |
NeuralLink_DataService |
✓ |
HealthService.mjs |
NeuralLink_HealthService |
✓ |
InstanceService.mjs |
NeuralLink_InstanceService |
✓ |
InteractionService.mjs |
NeuralLink_InteractionService |
✓ |
RuntimeService.mjs |
NeuralLink_RuntimeService |
✓ |
RecorderService.mjs |
(not exported — internal) |
— |
toolService.mjs |
(not exported — MCP tool router) |
— |
M2 BaseServer substrate (#10965 / PR #10975):
- NL Server.mjs reduced 212 → 149 LOC during M2 migration
- Custom
boot() override preserves transport-before-services order (rationale: connect MCP transport early so client handshake succeeds even when Bridge is down)
- Hooks used:
getServerMetadata, getToolService, custom boot() override that sequences loadCustomConfig → mcpServer → connectTransport → ConnectionService.ready (non-fatal) → healthcheck
- Member preserved:
bridgeCwd (for ConnectionService.cwd assignment)
- Module-level:
_turnId counter + getCurrentTurnId() export (per-dispatch increment)
makeSafe() Zod-wrapping (ai/services.mjs:231-237): wraps all 7 SDK-exported NL services. Migration preserves wrapping; only import paths change above the wrap.
NeuralLink_Config import at ai/services.mjs:69 — handled separately from service imports; migration doesn't touch config (out of scope per parent epic).
Existing flat-shape precedent: ai/services/ConceptService.mjs + sibling sub-issues #10991 (KB) and #10993 (GH-WF) establish the per-server pattern.
The Fix
Per-file moves (9 service files):
| Source path |
Destination path |
ai/mcp/server/neural-link/services/ComponentService.mjs |
ai/services/neural-link/ComponentService.mjs |
ai/mcp/server/neural-link/services/ConnectionService.mjs |
ai/services/neural-link/ConnectionService.mjs |
ai/mcp/server/neural-link/services/DataService.mjs |
ai/services/neural-link/DataService.mjs |
ai/mcp/server/neural-link/services/HealthService.mjs |
ai/services/neural-link/HealthService.mjs |
ai/mcp/server/neural-link/services/InstanceService.mjs |
ai/services/neural-link/InstanceService.mjs |
ai/mcp/server/neural-link/services/InteractionService.mjs |
ai/services/neural-link/InteractionService.mjs |
ai/mcp/server/neural-link/services/RecorderService.mjs |
ai/services/neural-link/RecorderService.mjs |
ai/mcp/server/neural-link/services/RuntimeService.mjs |
ai/services/neural-link/RuntimeService.mjs |
ai/mcp/server/neural-link/services/toolService.mjs |
ai/services/neural-link/toolService.mjs |
Then:
- Update
ai/services.mjs import paths (lines 62-68) — alias names (NeuralLink_*) preserved; only import paths change.
- Update
ai/mcp/server/neural-link/Server.mjs custom boot() override — import paths for any services referenced inside boot() (e.g., ConnectionService.ready call); also update any other service imports the NL Server uses.
- Update NL
config.mjs / openapi.yaml if they reference service paths.
- Update internal cross-references between NL services.
- Verify
bridgeCwd member assignment + _turnId counter + getCurrentTurnId() export still work post-migration.
- Remove the now-empty
ai/mcp/server/neural-link/services/ directory.
- Verify operator scripts work unchanged via the preserved
NeuralLink_* aliases.
Class names UNCHANGED. Service file names UNCHANGED. Only physical locations change.
Neo class-system compatibility note: Same as KB/GH-WF — verify during implementation whether any NL service uses Neo.setupClass() with className matching old path. NL services likely follow factory-singleton pattern.
Custom-boot-order preservation: NL's boot() override exists to preserve transport-before-services semantic (rationale captured in PR #10975). Migration must NOT change the boot order — only the import paths inside the override.
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
ai/services/neural-link/<ServiceName>.mjs paths |
This sub-issue + parent epic #10986 |
Flat SDK location for NL services |
None — physical move |
Brief note in learn/agentos/ post-M6-epic |
ls ai/services/neural-link/ confirms move; old ai/mcp/server/neural-link/services/ removed |
ai/services.mjs NeuralLink_* re-export aliases |
This sub-issue |
Continue exposing all 7 currently-aliased services; only import paths change |
Aliases preserved → consumer code unchanged |
JSDoc on ai/services.mjs |
Operator scripts + tests run unchanged post-migration |
NL Server.boot() override |
M2 BaseServer extension contract (#10965 / PR #10975) |
Custom boot order preserved; only import paths inside boot() updated |
Migration must not change order — transport-before-services rationale per #10975 |
NL Server.mjs JSDoc |
Per-server unit specs pass; transport-before-services semantic verified |
| NL unit + integration coverage |
Existing test substrate |
Continue working post-migration with no AC delta |
Test fixture imports updated alongside service moves |
n/a |
All 7 existing NL unit specs pass (per #10975 baseline) + integration row green |
bridgeCwd member + _turnId counter + getCurrentTurnId() export |
NL Server.mjs preserved members from PR #10975 |
Preserved post-migration; service moves don't affect Server.mjs members |
If broken, halt + investigate |
NL Server.mjs JSDoc |
NL CallTool dispatch increments turn counter as before |
Acceptance Criteria
Out of Scope
makeSafe() Zod schema reshape — wrapping pattern preserved as-is.
- Renaming NL services or class names — physical move only.
- Reshaping NL's custom
boot() order — preserved as-is per PR #10975 rationale; this sub-issue does NOT revisit transport-before-services architectural decision.
bridgeCwd member or _turnId counter reshape — preserved as-is.
- Touching
ai/mcp/server/shared/services/* — shared cross-cutting services unaffected.
- Other Tier-1 servers — KB (#10991) + GH-WF (#10993) + memory-core (to be filed) migrations are separate sub-issues.
<Server>_<ServiceName> namespace prefix collapse — preserved through M6 per parent epic Out-of-Scope.
- Promoting
RecorderService / toolService to SDK-exported — they're internal for a reason (NL recorder is internal to the harness substrate; toolService is the MCP tool-routing surface).
ai/services.mjs re-organization — this sub-issue updates NL import paths only.
Avoided Traps / Gold Standards Rejected
- Rejected: only move SDK-exported services, leave internal services in old location. Splits NL services across two top-level dirs categorized by export-status.
- Rejected: rename
NeuralLink_* aliases to drop prefix during migration. Breaks consumers.
- Rejected: refactor
makeSafe Zod wrapping pattern alongside the move. Increases PR risk.
- Rejected: convert NL's custom
boot() to canonical getDependentServices() flow. Architectural reshape; rationale for custom order is preserved per PR #10975 — transport-before-services is load-bearing for client-handshake during Bridge down/spawning. Migration is path-only.
- Rejected: parallel cadence with sibling M6 sub-issues (KB / GH-WF / MC). Per parent epic #10986 Avoided Traps: linear sequencing keeps
ai/services.mjs under single-edit lock.
- Rejected: gradual migration via indirection layer. Clean cut-over simpler for 9-file scope.
- Rejected: split NL migration into "services" + "boot-order-imports" PRs. Both touch same
ai/services.mjs block; combining is cleaner. The boot() override import-path update is small (verify at implementation time).
Related
- Parent epic: #10986 — Migrate Tier-1 MCP services to flat SDK boundary (M6)
- Sibling sub-issues:
- #10991 — KB migration (M6 sub-1; predecessor pattern)
- #10993 — GH-WF migration (M6 sub-2; immediate predecessor in sequence)
- memory-core (M6 sub-4; to be filed; last because most complex)
- Predecessor milestone: #10965 — M2 BaseServer common base class (CLOSED)
- Sibling SDK work: #10103 — SDK-layer config-file migration
- Strategic anchor:
learn/agentos/v13-path.md §3 D4 + §4 M6
- M2 per-server migration precedent: #10975 — neural-link migration to BaseServer + introduced
boot() overridable seam. The same Server.mjs that received the boot() seam now receives M6's import-path update inside that override.
Origin Session ID: 33ed57b5-10ed-491d-8b8f-ce5f1223ec38
Retrieval Hint: query_raw_memories(query="M6 SDK migration neural-link services flat shape ai/services/neural-link NeuralLink_ aliases boot override transport-before-services")
Context
Filed 2026-05-08 as M6 sub-issue 3 (neural-link) — third per-server SDK migration in the M6 epic #10986 sequence (KB → GH-WF → NL → MC). Sequenced after #10991 (KB) and #10993 (GH-WF) per parent epic linear-cadence rule: each migration touches
ai/services.mjsimport paths, so concurrent PRs would merge-conflict at the SDK aggregator.Per parent M6 epic #10986 §The Fix and
learn/agentos/v13-path.md§3 D4: services move fromai/mcp/server/neural-link/services/*.mjs→ai/services/neural-link/*.mjs.ai/services.mjsbecomes a re-export aggregator with stable<Server>_<ServiceName>alias contracts. M2 BaseServer substrate (#10965) provides the extension contract — but neural-link is the sub-issue with the LARGEST per-server complexity gap from the canonical pattern (see Architectural Reality below).The Problem
Neural-link services currently live under
ai/mcp/server/neural-link/services/— 9 service files. 7 are exposed viaai/services.mjs's aliasing layer (NeuralLink_ComponentService,NeuralLink_ConnectionService,NeuralLink_DataService,NeuralLink_HealthService,NeuralLink_InstanceService,NeuralLink_InteractionService,NeuralLink_RuntimeService). 2 services (RecorderService,toolService) are internal — not currently SDK-exported.The friction (per parent epic #10986 framing): same as KB and GH-WF — the on-disk shape conflates "service implementation" with "MCP transport endpoint". This sub-issue normalizes neural-link to the existing
ai/services/ConceptService.mjsflat-shape precedent.Distinct from KB and GH-WF migrations: neural-link's
Server.mjsuses a customboot()override (per #10975 M2 migration — transport-before-services bootstrap order rationale) rather than the canonicalgetDependentServices()extension hook. Migration must update import paths inside theboot()override AND any imports of services likeConnectionServicethat NL bootstraps explicitly — broader Server.mjs surface than KB/GH-WF.The Architectural Reality
Current state (verified 2026-05-08 via
ls ai/mcp/server/neural-link/services/+greponai/services.mjs):ComponentService.mjsNeuralLink_ComponentServiceConnectionService.mjsNeuralLink_ConnectionServiceDataService.mjsNeuralLink_DataServiceHealthService.mjsNeuralLink_HealthServiceInstanceService.mjsNeuralLink_InstanceServiceInteractionService.mjsNeuralLink_InteractionServiceRuntimeService.mjsNeuralLink_RuntimeServiceRecorderService.mjstoolService.mjsM2 BaseServer substrate (#10965 / PR #10975):
boot()override preserves transport-before-services order (rationale: connect MCP transport early so client handshake succeeds even when Bridge is down)getServerMetadata,getToolService, customboot()override that sequencesloadCustomConfig → mcpServer → connectTransport → ConnectionService.ready (non-fatal) → healthcheckbridgeCwd(for ConnectionService.cwd assignment)_turnIdcounter +getCurrentTurnId()export (per-dispatch increment)makeSafe()Zod-wrapping (ai/services.mjs:231-237): wraps all 7 SDK-exported NL services. Migration preserves wrapping; only import paths change above the wrap.NeuralLink_Configimport atai/services.mjs:69— handled separately from service imports; migration doesn't touch config (out of scope per parent epic).Existing flat-shape precedent:
ai/services/ConceptService.mjs+ sibling sub-issues #10991 (KB) and #10993 (GH-WF) establish the per-server pattern.The Fix
Per-file moves (9 service files):
ai/mcp/server/neural-link/services/ComponentService.mjsai/services/neural-link/ComponentService.mjsai/mcp/server/neural-link/services/ConnectionService.mjsai/services/neural-link/ConnectionService.mjsai/mcp/server/neural-link/services/DataService.mjsai/services/neural-link/DataService.mjsai/mcp/server/neural-link/services/HealthService.mjsai/services/neural-link/HealthService.mjsai/mcp/server/neural-link/services/InstanceService.mjsai/services/neural-link/InstanceService.mjsai/mcp/server/neural-link/services/InteractionService.mjsai/services/neural-link/InteractionService.mjsai/mcp/server/neural-link/services/RecorderService.mjsai/services/neural-link/RecorderService.mjsai/mcp/server/neural-link/services/RuntimeService.mjsai/services/neural-link/RuntimeService.mjsai/mcp/server/neural-link/services/toolService.mjsai/services/neural-link/toolService.mjsThen:
ai/services.mjsimport paths (lines 62-68) — alias names (NeuralLink_*) preserved; only import paths change.ai/mcp/server/neural-link/Server.mjscustomboot()override — import paths for any services referenced inside boot() (e.g.,ConnectionService.readycall); also update any other service imports the NL Server uses.config.mjs/openapi.yamlif they reference service paths.bridgeCwdmember assignment +_turnIdcounter +getCurrentTurnId()export still work post-migration.ai/mcp/server/neural-link/services/directory.NeuralLink_*aliases.Class names UNCHANGED. Service file names UNCHANGED. Only physical locations change.
Neo class-system compatibility note: Same as KB/GH-WF — verify during implementation whether any NL service uses
Neo.setupClass()with className matching old path. NL services likely follow factory-singleton pattern.Custom-boot-order preservation: NL's
boot()override exists to preserve transport-before-services semantic (rationale captured in PR #10975). Migration must NOT change the boot order — only the import paths inside the override.Contract Ledger Matrix
ai/services/neural-link/<ServiceName>.mjspathslearn/agentos/post-M6-epicls ai/services/neural-link/confirms move; oldai/mcp/server/neural-link/services/removedai/services.mjsNeuralLink_*re-export aliasesai/services.mjsNL Server.boot()overrideServer.mjsJSDocbridgeCwdmember +_turnIdcounter +getCurrentTurnId()exportAcceptance Criteria
ai/mcp/server/neural-link/services/toai/services/neural-link/. Old directory removed.ai/services.mjscontinues exporting all 7 currently-aliased NL services (NeuralLink_ComponentService,NeuralLink_ConnectionService,NeuralLink_DataService,NeuralLink_HealthService,NeuralLink_InstanceService,NeuralLink_InteractionService,NeuralLink_RuntimeService); only import paths change.ai/mcp/server/neural-link/Server.mjsboot()override updated to import services from new SDK paths. Custom transport-before-services boot order PRESERVED.Neo.setupClass()with className matching old path, className updated.bridgeCwdmember +_turnIdcounter +getCurrentTurnId()export semantics preserved post-migration. CallTool dispatch turn counter increments as before.boot()override + per-spec assertion).Out of Scope
makeSafe()Zod schema reshape — wrapping pattern preserved as-is.boot()order — preserved as-is per PR #10975 rationale; this sub-issue does NOT revisit transport-before-services architectural decision.bridgeCwdmember or_turnIdcounter reshape — preserved as-is.ai/mcp/server/shared/services/*— shared cross-cutting services unaffected.<Server>_<ServiceName>namespace prefix collapse — preserved through M6 per parent epic Out-of-Scope.RecorderService/toolServiceto SDK-exported — they're internal for a reason (NL recorder is internal to the harness substrate; toolService is the MCP tool-routing surface).ai/services.mjsre-organization — this sub-issue updates NL import paths only.Avoided Traps / Gold Standards Rejected
NeuralLink_*aliases to drop prefix during migration. Breaks consumers.makeSafeZod wrapping pattern alongside the move. Increases PR risk.boot()to canonicalgetDependentServices()flow. Architectural reshape; rationale for custom order is preserved per PR #10975 — transport-before-services is load-bearing for client-handshake during Bridge down/spawning. Migration is path-only.ai/services.mjsunder single-edit lock.ai/services.mjsblock; combining is cleaner. Theboot()override import-path update is small (verify at implementation time).Related
learn/agentos/v13-path.md§3 D4 + §4 M6boot()overridable seam. The sameServer.mjsthat received theboot()seam now receives M6's import-path update inside that override.Origin Session ID:
33ed57b5-10ed-491d-8b8f-ce5f1223ec38Retrieval Hint:
query_raw_memories(query="M6 SDK migration neural-link services flat shape ai/services/neural-link NeuralLink_ aliases boot override transport-before-services")