Context
Memory Core and the Knowledge Base ran as local non-containerized processes before the Docker migration. That migration is complete and non-container versions can no longer exist (operator, 2026-08-18). Their stdio transport is therefore migration debt.
ai/configBase.mjs:519 still declares it as the default:
transport: leaf('stdio', 'NEO_TRANSPORT', 'string'),Every working deployment overrides it — the containerized stack sets NEO_TRANSPORT=streamable-http on both servers. So the retired mode is what a boot gets by not choosing. That is not dead code; it is a trap with a live entry point.
The Problem
The obvious repairs both fail, for the same underlying reason.
Removing the leaf breaks a live consumer. transport is global — it sits beside debug and publicUrl, and all four MCP servers read it through BaseServer.mjs:738, which still constructs new StdioServerTransport() at :766. Neural Link connects over stdio by design (neural-link/Server.mjs:31: "the stdio MCP transport is connected EARLY"), and #15184 — its Streamable HTTP move — is open. Neural Link needs stdio today.
Flipping the default just relocates the trap. Set the global default to streamable-http and Neural Link becomes the seat that silently enters the wrong mode when someone omits NEO_TRANSPORT=stdio. Same defect, different victim.
The root cause is that one leaf now describes four servers that no longer agree. MC and KB are container-only HTTP. Neural Link is stdio until #15184. A single global value cannot express that, so some server is always one forgotten env var away from a mode it cannot serve.
A second consumer makes this more than a boot switch. communityBatchTool.mjs:30:
const areHostedCommunityToolsVisible = (transport = aiConfig.transport) => transport === 'streamable-http';
Tool visibility keys on transport. Deleting the leaf does not delete that decision — it makes it unconditional, which is a behavioural change to a capability gate rather than a cleanup.
The Architectural Reality
ai/configBase.mjs:519 — the global leaf, default 'stdio'; :516 documents both values as supported.
BaseServer.mjs:738 — shared read, plus a !this.aiConfig ? 'stdio' fallback for config-less local servers; :747 validates the two-value set; :766 builds the stdio transport.
memory-core/Server.mjs:404 — if (this.aiConfig.transport === 'stdio'), wrapping resolveStdioIdentity() and (at :411) the wake-subscription auto-bootstrap that #16310 exists because nothing invokes.
memory-core/Server.mjs:54 — JSDoc still calls stdio "(the default)".
HealthService.mjs — 12 references projecting stdioIdentity into the healthcheck identity block, which on HTTP is permanently {source: 'unresolved', bound: false} by construction and reports nothing about the request-bound identity that actually resolves.
Deliberately not in scope, and verified as such: localWakeAdapters.mjs:950 and ProcessSupervisorService.mjs:659 use stdio as spawn() pipe descriptors, and services.host.mjs:47 mentions it in prose. Those are unrelated to wire transport and a text search over stdio will surface them.
The Fix
- Make transport per-server rather than one global leaf. Precedent: #16184 separated Fleet MCP target selection from wire transport — the same split, already accepted once.
- Narrow MC and KB to
streamable-http, so stdio is unrepresentable for them. No env var required, no trap in either direction, and memory-core/Server.mjs:404 becomes dead by construction rather than merely unused.
- Leave Neural Link on stdio; #15184 owns its move.
- Re-key
communityBatchTool's visibility gate on the surviving per-server value so it cannot drift.
Once (2) lands, the stdioIdentity and healthcheck-identity cleanup becomes safe — but it should follow #16310, which needs to re-home the wake bootstrap out of that branch before it is deleted. Sequencing matters: deleting first would remove the code #16310 has to relocate.
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
transport |
configBase.mjs:519 |
per-server; MC/KB accept streamable-http only |
global leaf honoured through a deprecation window |
config docs |
default is the retired mode |
BaseServer validation |
BaseServer.mjs:747 |
rejects stdio for MC/KB, accepts for Neural Link |
current two-value set |
— |
one set for four servers |
| hosted community-tool visibility |
communityBatchTool.mjs:30 |
keys on the per-server value |
unchanged while global exists |
— |
gate silently unconditional if the leaf is deleted |
Decision Record impact
none — no ADR governs transport selection; #16184 is precedent, not authority.
Acceptance Criteria
Out of Scope
- Neural Link's transport migration — #15184.
- Re-homing the wake-subscription bootstrap — #16310.
- Deleting
stdioIdentity and replacing the healthcheck identity block — follows #16310; noted there rather than duplicated here.
- Process-level
stdio pipe descriptors.
Avoided Traps
- Deleting the leaf now. Breaks Neural Link, whose HTTP move is still open, and silently makes a capability gate unconditional.
- Flipping the global default. Moves the trap from MC/KB to Neural Link without closing it. I proposed this before checking the consumers and it is wrong.
- Treating every
stdio occurrence as debt. Three of the matches are spawn() descriptors; an over-scoped sweep would break process supervision.
- Deleting
memory-core/Server.mjs:404 as dead code. It is reachable on any boot that omits NEO_TRANSPORT, and it guards functionality #16310 wants to keep.
Related
#16310 (wake bootstrap gated on stdio — the consumer that must move first) · #15184 (Neural Link Streamable HTTP) · #16184 (precedent: transport separated from target selection) · #15188 (transport value renamed to streamable-http) · #16205 (provisioning templates still emitted the retired stdio shape after the 9-seat flip)
Live latest-open sweep: latest 20 open checked 2026-08-18T11:07:04Z, plus a six-term state=all title sweep and an A2A recency scan over the last 12 messages — nearest neighbours #16310 / #15184 / #16184, none covering per-server transport resolution; no in-flight claim.
Origin Session ID: 9ccc2fa1-8843-4796-8e85-5e151c0392d2
Retrieval Hint: query_raw_memories("global transport leaf stdio default retired MC KB streamable-http per-server Neural Link 15184")
Context
Memory Core and the Knowledge Base ran as local non-containerized processes before the Docker migration. That migration is complete and non-container versions can no longer exist (operator, 2026-08-18). Their stdio transport is therefore migration debt.
ai/configBase.mjs:519still declares it as the default:transport: leaf('stdio', 'NEO_TRANSPORT', 'string'),Every working deployment overrides it — the containerized stack sets
NEO_TRANSPORT=streamable-httpon both servers. So the retired mode is what a boot gets by not choosing. That is not dead code; it is a trap with a live entry point.The Problem
The obvious repairs both fail, for the same underlying reason.
Removing the leaf breaks a live consumer.
transportis global — it sits besidedebugandpublicUrl, and all four MCP servers read it throughBaseServer.mjs:738, which still constructsnew StdioServerTransport()at:766. Neural Link connects over stdio by design (neural-link/Server.mjs:31: "the stdio MCP transport is connected EARLY"), and #15184 — its Streamable HTTP move — is open. Neural Link needs stdio today.Flipping the default just relocates the trap. Set the global default to
streamable-httpand Neural Link becomes the seat that silently enters the wrong mode when someone omitsNEO_TRANSPORT=stdio. Same defect, different victim.The root cause is that one leaf now describes four servers that no longer agree. MC and KB are container-only HTTP. Neural Link is stdio until #15184. A single global value cannot express that, so some server is always one forgotten env var away from a mode it cannot serve.
A second consumer makes this more than a boot switch.
communityBatchTool.mjs:30:const areHostedCommunityToolsVisible = (transport = aiConfig.transport) => transport === 'streamable-http';Tool visibility keys on transport. Deleting the leaf does not delete that decision — it makes it unconditional, which is a behavioural change to a capability gate rather than a cleanup.
The Architectural Reality
ai/configBase.mjs:519— the global leaf, default'stdio';:516documents both values as supported.BaseServer.mjs:738— shared read, plus a!this.aiConfig ? 'stdio'fallback for config-less local servers;:747validates the two-value set;:766builds the stdio transport.memory-core/Server.mjs:404—if (this.aiConfig.transport === 'stdio'), wrappingresolveStdioIdentity()and (at:411) the wake-subscription auto-bootstrap that #16310 exists because nothing invokes.memory-core/Server.mjs:54— JSDoc still calls stdio "(the default)".HealthService.mjs— 12 references projectingstdioIdentityinto the healthcheckidentityblock, which on HTTP is permanently{source: 'unresolved', bound: false}by construction and reports nothing about the request-bound identity that actually resolves.Deliberately not in scope, and verified as such:
localWakeAdapters.mjs:950andProcessSupervisorService.mjs:659usestdioasspawn()pipe descriptors, andservices.host.mjs:47mentions it in prose. Those are unrelated to wire transport and a text search overstdiowill surface them.The Fix
streamable-http, so stdio is unrepresentable for them. No env var required, no trap in either direction, andmemory-core/Server.mjs:404becomes dead by construction rather than merely unused.communityBatchTool's visibility gate on the surviving per-server value so it cannot drift.Once (2) lands, the
stdioIdentityand healthcheck-identitycleanup becomes safe — but it should follow #16310, which needs to re-home the wake bootstrap out of that branch before it is deleted. Sequencing matters: deleting first would remove the code #16310 has to relocate.Contract Ledger Matrix
transportconfigBase.mjs:519streamable-httponlyBaseServervalidationBaseServer.mjs:747stdiofor MC/KB, accepts for Neural LinkcommunityBatchTool.mjs:30Decision Record impact
none— no ADR governs transport selection; #16184 is precedent, not authority.Acceptance Criteria
stdio.communityBatchToolvisibility keys on the per-server value and its behaviour is unchanged on astreamable-httpMC.NEO_TRANSPORT=streamable-httpdeployments keep booting, or the required deployment change is named in the migration note.stdioon MC fails closed with a named error; onmainit boots and entersresolveStdioIdentity(). A test assertingstreamable-httpboots would pass onmainand prove nothing.spawn()pipe usages ofstdioare untouched — asserted, not assumed, since they share the search term.Out of Scope
stdioIdentityand replacing the healthcheckidentityblock — follows #16310; noted there rather than duplicated here.stdiopipe descriptors.Avoided Traps
stdiooccurrence as debt. Three of the matches arespawn()descriptors; an over-scoped sweep would break process supervision.memory-core/Server.mjs:404as dead code. It is reachable on any boot that omitsNEO_TRANSPORT, and it guards functionality #16310 wants to keep.Related
#16310 (wake bootstrap gated on stdio — the consumer that must move first) · #15184 (Neural Link Streamable HTTP) · #16184 (precedent: transport separated from target selection) · #15188 (transport value renamed to
streamable-http) · #16205 (provisioning templates still emitted the retired stdio shape after the 9-seat flip)Live latest-open sweep: latest 20 open checked 2026-08-18T11:07:04Z, plus a six-term
state=alltitle sweep and an A2A recency scan over the last 12 messages — nearest neighbours #16310 / #15184 / #16184, none covering per-server transport resolution; no in-flight claim.Origin Session ID: 9ccc2fa1-8843-4796-8e85-5e151c0392d2
Retrieval Hint:
query_raw_memories("global transport leaf stdio default retired MC KB streamable-http per-server Neural Link 15184")