| bc-version |
domain |
keywords |
technologies |
countries |
application-area |
|
|
mcp |
| businesscentral |
| mcp |
| bc-mcp-bridge |
| error-handling |
| sse |
| timeout |
| troubleshooting |
|
|
|
|
bc-mcp-bridge.js Must Check !r.ok Before Any SSE Parsing, Unconditionally
Description
bc-mcp-bridge.js's forward() function decides how to parse the BC MCP
endpoint's response body based on its content-type header. Before
2026-07-31 it only treated a response as an error when !r.ok && !text —
i.e. only when there was no body at all. A non-2xx response WITH a body
(the common case — BC returns structured JSON error objects) fell through
to the content-type branch instead.
Incident (2026-07-31)
BC returned a 400 with a plain JSON error body ({"Error": {"Message": "..."}}) while still labelling the response content-type: text/event-stream. parseSSE() only extracts lines starting with data: —
a plain JSON body has none, so it returned [] silently. The stdin loop's
for (const out of responses) process.stdout.write(...) then had nothing to
iterate, so the bridge produced zero output — not even to stderr — for a
request that BC had already answered in under 200ms. Claude Code had no
signal to work with and waited out its own 30-second client-side timeout,
which was the only thing the developer actually saw.
Rule
Check !r.ok before considering content-type at all, and throw
unconditionally (with the body text included) when it's true. Never let a
non-2xx response reach parseSSE — a server is free to mislabel an error
body's content-type, and the client must not depend on that label being
honest.
Anti-Pattern
const ct = r.headers.get("content-type") || "";
const text = await r.text();
if (!r.ok && !text) throw new Error(`HTTP ${r.status}`);
return ct.includes("text/event-stream") ? parseSSE(text) : [text.trim()];
// A 400 WITH a body silently falls through to parseSSE and returns [].
Compliant
const ct = r.headers.get("content-type") || "";
const text = await r.text();
if (!r.ok) throw new Error(`HTTP ${r.status}: ${text || "(empty body)"}`);
return ct.includes("text/event-stream") ? parseSSE(text) : [text.trim()];
Scope
bc-mcp-bridge.js specifically, but the underlying principle generalizes to
any stdio MCP bridge that branches parsing logic on a server-supplied
content-type header: validate the HTTP status first, independent of what
the header claims the body's shape is.