Skip to content

Commit 48087db

Browse files
committed
fix: fallback error handling since mcp v1.24.0
1 parent 327b8c5 commit 48087db

File tree

2 files changed

+159
-1
lines changed

2 files changed

+159
-1
lines changed

packages/agents/src/mcp/errors.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,34 @@ export function toErrorMessage(error: unknown): string {
22
return error instanceof Error ? error.message : String(error);
33
}
44

5+
function getErrorCode(error: unknown): number | undefined {
6+
if (
7+
error &&
8+
typeof error === "object" &&
9+
"code" in error &&
10+
typeof (error as { code: unknown }).code === "number"
11+
) {
12+
return (error as { code: number }).code;
13+
}
14+
return undefined;
15+
}
16+
517
export function isUnauthorized(error: unknown): boolean {
18+
const code = getErrorCode(error);
19+
if (code === 401) return true;
20+
621
const msg = toErrorMessage(error);
722
return msg.includes("Unauthorized") || msg.includes("401");
823
}
924

25+
// MCP SDK change (v1.24.0, commit 6b90e1a):
26+
// - Old: Error POSTing to endpoint (HTTP 404): Not Found
27+
// - New: StreamableHTTPError with code: 404 and message Error POSTing to endpoint: Not Found
1028
export function isTransportNotImplemented(error: unknown): boolean {
29+
const code = getErrorCode(error);
30+
if (code === 404 || code === 405) return true;
31+
1132
const msg = toErrorMessage(error);
12-
// Treat common "not implemented" surfaces as transport not supported
1333
return (
1434
msg.includes("404") ||
1535
msg.includes("405") ||
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
import { describe, expect, it } from "vitest";
2+
import {
3+
isTransportNotImplemented,
4+
isUnauthorized,
5+
toErrorMessage
6+
} from "../../mcp/errors";
7+
8+
// Helper to create error-like objects with code property (like StreamableHTTPError/SseError)
9+
function createErrorWithCode(code: number, message: string) {
10+
const error = new Error(message) as Error & { code: number };
11+
error.code = code;
12+
return error;
13+
}
14+
15+
describe("MCP Error Utilities", () => {
16+
describe("toErrorMessage", () => {
17+
it("should extract message from Error objects", () => {
18+
expect(toErrorMessage(new Error("test error"))).toBe("test error");
19+
});
20+
21+
it("should convert non-Error values to string", () => {
22+
expect(toErrorMessage("string error")).toBe("string error");
23+
expect(toErrorMessage(404)).toBe("404");
24+
expect(toErrorMessage({ code: 404 })).toBe("[object Object]");
25+
});
26+
});
27+
28+
describe("isUnauthorized", () => {
29+
it("should detect 401 error code", () => {
30+
expect(isUnauthorized(createErrorWithCode(401, "Unauthorized"))).toBe(
31+
true
32+
);
33+
expect(isUnauthorized(createErrorWithCode(401, ""))).toBe(true);
34+
});
35+
36+
it("should detect Unauthorized in message", () => {
37+
expect(isUnauthorized(new Error("Unauthorized"))).toBe(true);
38+
expect(isUnauthorized(new Error("Request Unauthorized"))).toBe(true);
39+
});
40+
41+
it("should detect 401 in message", () => {
42+
expect(isUnauthorized(new Error("HTTP 401"))).toBe(true);
43+
expect(isUnauthorized(new Error("Error: 401 Unauthorized"))).toBe(true);
44+
});
45+
46+
it("should return false for other errors", () => {
47+
expect(isUnauthorized(new Error("Not Found"))).toBe(false);
48+
expect(isUnauthorized(new Error("500 Internal Server Error"))).toBe(
49+
false
50+
);
51+
expect(isUnauthorized(createErrorWithCode(404, "Not Found"))).toBe(false);
52+
});
53+
});
54+
55+
describe("isTransportNotImplemented", () => {
56+
it("should detect 404 error code (MCP SDK StreamableHTTPError)", () => {
57+
// StreamableHTTPError from MCP SDK v1.24.0+
58+
expect(
59+
isTransportNotImplemented(
60+
createErrorWithCode(
61+
404,
62+
"Streamable HTTP error: Error POSTing to endpoint: Not Found"
63+
)
64+
)
65+
).toBe(true);
66+
expect(
67+
isTransportNotImplemented(createErrorWithCode(404, "Not Found"))
68+
).toBe(true);
69+
});
70+
71+
it("should detect 405 error code", () => {
72+
expect(
73+
isTransportNotImplemented(
74+
createErrorWithCode(405, "Method Not Allowed")
75+
)
76+
).toBe(true);
77+
});
78+
79+
it("should detect 404 status code in message (legacy format)", () => {
80+
expect(isTransportNotImplemented(new Error("HTTP 404"))).toBe(true);
81+
expect(isTransportNotImplemented(new Error("Error: 404"))).toBe(true);
82+
expect(
83+
isTransportNotImplemented(new Error("Non-200 status code (404)"))
84+
).toBe(true);
85+
});
86+
87+
it("should detect 405 status code in message", () => {
88+
expect(isTransportNotImplemented(new Error("HTTP 405"))).toBe(true);
89+
expect(
90+
isTransportNotImplemented(new Error("Method Not Allowed 405"))
91+
).toBe(true);
92+
});
93+
94+
it("should detect 'Not Implemented' text", () => {
95+
expect(isTransportNotImplemented(new Error("Not Implemented"))).toBe(
96+
true
97+
);
98+
expect(isTransportNotImplemented(new Error("501 Not Implemented"))).toBe(
99+
true
100+
);
101+
});
102+
103+
it("should detect 'not implemented' (lowercase)", () => {
104+
expect(
105+
isTransportNotImplemented(new Error("Transport not implemented"))
106+
).toBe(true);
107+
expect(
108+
isTransportNotImplemented(new Error("Feature not implemented yet"))
109+
).toBe(true);
110+
});
111+
112+
it("should NOT match 'Not Found' text without 404 code", () => {
113+
// This ensures we don't incorrectly treat "Resource Not Found" errors
114+
// as transport-not-implemented when the code is different (e.g., session not found)
115+
expect(isTransportNotImplemented(new Error("Not Found"))).toBe(false);
116+
expect(isTransportNotImplemented(new Error("Resource Not Found"))).toBe(
117+
false
118+
);
119+
expect(
120+
isTransportNotImplemented(createErrorWithCode(400, "Session Not Found"))
121+
).toBe(false);
122+
});
123+
124+
it("should return false for other errors", () => {
125+
expect(isTransportNotImplemented(new Error("Unauthorized"))).toBe(false);
126+
expect(
127+
isTransportNotImplemented(new Error("500 Internal Server Error"))
128+
).toBe(false);
129+
expect(isTransportNotImplemented(new Error("Connection refused"))).toBe(
130+
false
131+
);
132+
expect(isTransportNotImplemented(new Error("Timeout"))).toBe(false);
133+
expect(
134+
isTransportNotImplemented(createErrorWithCode(500, "Server Error"))
135+
).toBe(false);
136+
});
137+
});
138+
});

0 commit comments

Comments
 (0)