Context
Surfaced 2026-05-07 while implementing #10878 (post-#10877 NEO_-env-var fixture rename) — the env-var rename was applied + verified via node --check + grep, but npm run test-unit -- test/playwright/unit/ai/mcp/Authorization.spec.mjs still timed out. Investigation revealed a pre-existing bug in the test's startup-wait promise resolution logic: it listens for the SSE-startup log message on stdout, but the server emits this message via logger.info which writes to stderr.
The bug existed before #10877; this ticket exists because #10878's Evidence claim documents that the fixture rename does NOT make the test pass end-to-end (timeout is independent of env-var contract).
The Problem
test/playwright/unit/ai/mcp/Authorization.spec.mjs:88-105 (test.beforeAll startup wait):
await new Promise((resolve, reject) => {
mcpServerProcess.stdout.on('data', (data) => {
const msg = data.toString();
if (msg.includes('Server started on SSE transport')) {
resolve();
}
});
mcpServerProcess.stderr.on('data', (data) => {
const msg = data.toString();
console.error(`[MCP Server Error] ${msg}`);
});
setTimeout(() => reject(new Error('MCP Server startup timeout')), 20000);
});
Note the comment "Often SSE server logs to stderr" — the author was aware of the channel mismatch but didn't add resolve() to the stderr listener. The startup-wait therefore times out at 20s regardless of whether the server actually started successfully.
The same shape exists at lines ~199-215 for discoveryProcess.
The Architectural Reality
- Server emits the startup log via:
ai/mcp/server/shared/services/TransportService.mjs:202 — logger.info(\[${resourceName}] Server started on SSE transport (Port: ${port})`);`
logger.info (Memory Core's logger module) writes to stderr by convention; verified by the captured [MCP Server Error] [INFO] [neo-knowledge-base MCP] Available tools loaded from OpenAPI spec line in test output (test wraps stderr with [MCP Server Error] prefix).
- The test's stdout listener pattern would only match if the server's logger emitted to stdout — which it does NOT for
info-level messages.
The Fix
Move the resolve() check into the stderr listener (or include both):
await new Promise((resolve, reject) => {
const checkStarted = (data) => {
const msg = data.toString();
if (msg.includes('Server started on SSE transport')) {
resolve();
}
};
mcpServerProcess.stdout.on('data', checkStarted);
mcpServerProcess.stderr.on('data', (data) => {
checkStarted(data);
console.error(`[MCP Server Error] ${data.toString()}`);
});
setTimeout(() => reject(new Error('MCP Server startup timeout')), 20000);
});
Apply the same fix to the discoveryProcess startup wait (around line ~199-215).
Acceptance Criteria
Out of Scope
- Refactoring the spawned-server-startup pattern into a shared helper (defer until pattern recurs in a third spec file).
- Changing the server's logger output channel (logger.info → stderr is canonical convention).
- The 20-second timeout itself — keep as-is; only the resolve condition needs fixing.
Related
- #10878 / PR #10879 (env-var fixture rename — the parent fix surfacing this bug)
- #10877 (NEO_ env-var prefix institutionalization — adjacent context)
- #9929/#9930 (last commit on this test file, predates the listener-channel bug surfacing)
Origin Session ID: 78a3272e-847b-4799-ad6c-ce334464844c
Retrieval Hint: query_raw_memories(query="Authorization.spec.mjs startup wait stderr stdout listener bug")
Context
Surfaced 2026-05-07 while implementing #10878 (post-#10877 NEO_-env-var fixture rename) — the env-var rename was applied + verified via
node --check+ grep, butnpm run test-unit -- test/playwright/unit/ai/mcp/Authorization.spec.mjsstill timed out. Investigation revealed a pre-existing bug in the test's startup-wait promise resolution logic: it listens for the SSE-startup log message onstdout, but the server emits this message vialogger.infowhich writes tostderr.The bug existed before #10877; this ticket exists because #10878's Evidence claim documents that the fixture rename does NOT make the test pass end-to-end (timeout is independent of env-var contract).
The Problem
test/playwright/unit/ai/mcp/Authorization.spec.mjs:88-105(test.beforeAll startup wait):await new Promise((resolve, reject) => { mcpServerProcess.stdout.on('data', (data) => { const msg = data.toString(); if (msg.includes('Server started on SSE transport')) { resolve(); } }); mcpServerProcess.stderr.on('data', (data) => { const msg = data.toString(); console.error(`[MCP Server Error] ${msg}`); // Often SSE server logs to stderr }); setTimeout(() => reject(new Error('MCP Server startup timeout')), 20000); });Note the comment "Often SSE server logs to stderr" — the author was aware of the channel mismatch but didn't add
resolve()to the stderr listener. The startup-wait therefore times out at 20s regardless of whether the server actually started successfully.The same shape exists at lines ~199-215 for
discoveryProcess.The Architectural Reality
ai/mcp/server/shared/services/TransportService.mjs:202—logger.info(\[${resourceName}] Server started on SSE transport (Port: ${port})`);`logger.info(Memory Core's logger module) writes to stderr by convention; verified by the captured[MCP Server Error] [INFO] [neo-knowledge-base MCP] Available tools loaded from OpenAPI specline in test output (test wraps stderr with[MCP Server Error]prefix).info-level messages.The Fix
Move the
resolve()check into the stderr listener (or include both):await new Promise((resolve, reject) => { const checkStarted = (data) => { const msg = data.toString(); if (msg.includes('Server started on SSE transport')) { resolve(); } }; mcpServerProcess.stdout.on('data', checkStarted); mcpServerProcess.stderr.on('data', (data) => { checkStarted(data); console.error(`[MCP Server Error] ${data.toString()}`); }); setTimeout(() => reject(new Error('MCP Server startup timeout')), 20000); });Apply the same fix to the
discoveryProcessstartup wait (around line ~199-215).Acceptance Criteria
mcpServerProcessanddiscoveryProcessstartup-wait promises resolve when the server emitsServer started on SSE transporton either stdout OR stderr.npm run test-unit -- test/playwright/unit/ai/mcp/Authorization.spec.mjsreaches the actual test bodies (noMCP Server startup timeoutreject in test.beforeAll).console.error([MCP Server Error] ...)echo behavior preserved (operator visibility into stderr stream maintained).Out of Scope
Related
Origin Session ID: 78a3272e-847b-4799-ad6c-ce334464844c
Retrieval Hint:
query_raw_memories(query="Authorization.spec.mjs startup wait stderr stdout listener bug")