fix(agents): guard bundle mcp connection errors

This commit is contained in:
Vincent Koc
2026-06-05 11:50:02 +02:00
parent d49c74ac6c
commit 27162e13e7
2 changed files with 58 additions and 17 deletions

View File

@@ -8,6 +8,7 @@ import {
createBundleMcpJsonSchemaValidator,
isMcpMethodNotFoundError,
snapshotListedMcpToolsForCatalog,
toLintErrorObject,
} from "./agent-bundle-mcp-runtime.js";
import { cleanupBundleMcpHarness } from "./agent-bundle-mcp-test-harness.js";
import {
@@ -364,6 +365,38 @@ describe("isMcpMethodNotFoundError", () => {
});
});
describe("toLintErrorObject", () => {
it("normalizes hostile MCP connection rejections without touching proxy internals", () => {
const hostileError = new Proxy(
{},
{
get() {
throw new Error("property denied");
},
getPrototypeOf() {
throw new Error("prototype denied");
},
ownKeys() {
throw new Error("keys denied");
},
},
);
const readableMessage = {};
Object.defineProperty(readableMessage, "message", {
get() {
return "connect failed";
},
});
expect(toLintErrorObject(hostileError, "Non-Error rejection").message).toBe(
"Non-Error rejection",
);
expect(toLintErrorObject(readableMessage, "Non-Error rejection").message).toBe(
"connect failed",
);
});
});
describe("session MCP runtime", () => {
it("accepts draft-2020-12 tool output schemas from external MCP catalogs", () => {
const validator = createBundleMcpJsonSchemaValidator().getValidator<{

View File

@@ -233,6 +233,14 @@ function stringifyMcpRuntimeError(error: unknown): string {
}
}
function readMcpRuntimeErrorField(error: unknown, key: string): unknown {
try {
return isMcpConfigRecord(error) ? error[key] : undefined;
} catch {
return undefined;
}
}
function redactErrorUrls(error: unknown): string {
return redactSensitiveUrlLikeString(stringifyMcpRuntimeError(error));
}
@@ -249,16 +257,8 @@ async function listAllTools(client: Client, timeoutMs: number) {
return tools;
}
function readMcpRuntimeErrorCode(error: unknown): unknown {
try {
return isMcpConfigRecord(error) ? error.code : undefined;
} catch {
return undefined;
}
}
export function isMcpMethodNotFoundError(error: unknown): boolean {
if (readMcpRuntimeErrorCode(error) === ErrorCode.MethodNotFound) {
if (readMcpRuntimeErrorField(error, "code") === ErrorCode.MethodNotFound) {
return true;
}
const message = stringifyMcpRuntimeError(error);
@@ -1244,16 +1244,24 @@ export const testing = {
};
export { testing as __testing };
function toLintErrorObject(value: unknown, fallbackMessage: string): Error {
if (value instanceof Error) {
return value;
export function toLintErrorObject(value: unknown, fallbackMessage: string): Error {
const message = readMcpRuntimeErrorField(value, "message");
if (typeof message === "string" && message.trim()) {
return new Error(message);
}
const name = readMcpRuntimeErrorField(value, "name");
if (typeof name === "string" && name.trim()) {
return new Error(name);
}
try {
if (value instanceof Error) {
return value;
}
} catch {
return new Error(fallbackMessage);
}
if (typeof value === "string") {
return new Error(value);
}
const error = new Error(fallbackMessage, { cause: value });
if ((typeof value === "object" && value !== null) || typeof value === "function") {
Object.assign(error, value);
}
return error;
return new Error(fallbackMessage, { cause: value });
}