diff --git a/packages/mcp/src/client.ts b/packages/mcp/src/client.ts index 85b754a3..25dd1586 100644 --- a/packages/mcp/src/client.ts +++ b/packages/mcp/src/client.ts @@ -306,8 +306,17 @@ export function formatDocmostAxiosError(error: any): void { } } else { // No response at all (ECONNREFUSED / ETIMEDOUT / ECONNRESET / DNS / timeout). - const reason = error.code ?? error.message ?? "network error"; + // Use ONLY error.code, never the raw error.message: axios network messages + // embed host:port ("connect ECONNREFUSED 127.0.0.1:3000", "getaddrinfo + // ENOTFOUND host") and #437's invariant is that the host never reaches the + // model-visible message. code is set for essentially every real no-response + // error (ECONNREFUSED/ETIMEDOUT/ECONNRESET/ENOTFOUND/ECONNABORTED); the full + // native message still goes to stderr under DEBUG. + const reason = error.code ?? "network error"; message = `${methodPath} failed: ${reason} (no response from server)`; + if (process.env.DEBUG) { + console.error("Docmost request failed; no response:", error.message); + } } if (message.length > ERROR_MESSAGE_CAP) { diff --git a/packages/mcp/test/unit/error-diagnostics.test.mjs b/packages/mcp/test/unit/error-diagnostics.test.mjs index be01f294..ab1bdd56 100644 --- a/packages/mcp/test/unit/error-diagnostics.test.mjs +++ b/packages/mcp/test/unit/error-diagnostics.test.mjs @@ -180,18 +180,22 @@ test("no response uses error.code + path + 'no response from server'", () => { ); }); -test("no response falls back to error.message when code is absent", () => { +test("no response with no code falls back to a neutral reason (raw message not leaked — it may embed host:port)", () => { const err = makeAxiosError({ method: "post", url: "/comments/create", status: undefined, - message: "timeout of 30000ms exceeded", + // A raw axios network message like "connect ECONNREFUSED 127.0.0.1:3000" + // embeds the host; #437's invariant is that it never reaches the message. + message: "connect ECONNREFUSED 10.0.0.5:3000", }); formatDocmostAxiosError(err); assert.equal( err.message, - "POST /comments/create failed: timeout of 30000ms exceeded (no response from server)", + "POST /comments/create failed: network error (no response from server)", ); + // And the host must NOT appear anywhere in the model-visible message. + assert.ok(!err.message.includes("10.0.0.5")); }); test("path drops the host and the query string", () => {