fix(httpapi): return mcp server not found errors (#28817)
This commit is contained in:
@@ -329,58 +329,37 @@ const scenarios: Scenario[] = [
|
||||
http.protected
|
||||
.post("/mcp/{name}/auth", "mcp.auth.start")
|
||||
.at((ctx) => ({ path: route("/mcp/{name}/auth", { name: "httpapi-missing" }), headers: ctx.headers() }))
|
||||
.json(
|
||||
400,
|
||||
(body) => {
|
||||
object(body)
|
||||
check(typeof body.error === "string", "unsupported MCP OAuth response should include error")
|
||||
},
|
||||
"status",
|
||||
),
|
||||
.json(404, object, "status"),
|
||||
http.protected
|
||||
.delete("/mcp/{name}/auth", "mcp.auth.remove")
|
||||
.mutating()
|
||||
.at((ctx) => ({ path: route("/mcp/{name}/auth", { name: "httpapi-missing" }), headers: ctx.headers() }))
|
||||
.json(200, (body) => {
|
||||
object(body)
|
||||
check(body.success === true, "MCP auth removal should return success")
|
||||
}),
|
||||
.json(404, object, "status"),
|
||||
http.protected
|
||||
.post("/mcp/{name}/auth/authenticate", "mcp.auth.authenticate")
|
||||
.at((ctx) => ({
|
||||
path: route("/mcp/{name}/auth/authenticate", { name: "httpapi-missing" }),
|
||||
headers: ctx.headers(),
|
||||
}))
|
||||
.json(
|
||||
400,
|
||||
(body) => {
|
||||
object(body)
|
||||
check(typeof body.error === "string", "unsupported MCP OAuth authenticate response should include error")
|
||||
},
|
||||
"status",
|
||||
),
|
||||
.json(404, object, "status"),
|
||||
http.protected
|
||||
.post("/mcp/{name}/auth/callback", "mcp.auth.callback")
|
||||
.at((ctx) => ({
|
||||
path: route("/mcp/{name}/auth/callback", { name: "httpapi-missing" }),
|
||||
headers: ctx.headers(),
|
||||
body: { code: 1 },
|
||||
body: { code: "code" },
|
||||
}))
|
||||
.status(400),
|
||||
.json(404, object, "status"),
|
||||
http.protected
|
||||
.post("/mcp/{name}/connect", "mcp.connect")
|
||||
.mutating()
|
||||
.at((ctx) => ({ path: route("/mcp/{name}/connect", { name: "httpapi-missing" }), headers: ctx.headers() }))
|
||||
.json(200, (body) => {
|
||||
check(body === true, "missing MCP connect should remain a no-op success")
|
||||
}),
|
||||
.json(404, object, "status"),
|
||||
http.protected
|
||||
.post("/mcp/{name}/disconnect", "mcp.disconnect")
|
||||
.mutating()
|
||||
.at((ctx) => ({ path: route("/mcp/{name}/disconnect", { name: "httpapi-missing" }), headers: ctx.headers() }))
|
||||
.json(200, (body) => {
|
||||
check(body === true, "missing MCP disconnect should remain a no-op success")
|
||||
}),
|
||||
.json(404, object, "status"),
|
||||
http.protected.get("/pty/shells", "pty.shells").json(200, array),
|
||||
http.protected.get("/pty", "pty.list").json(200, array),
|
||||
http.protected
|
||||
|
||||
@@ -192,4 +192,36 @@ describe("mcp HttpApi", () => {
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
it.instance(
|
||||
"returns typed not found errors for missing MCP servers",
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
const tmp = yield* TestInstance
|
||||
const handler = yield* handlerScoped
|
||||
|
||||
for (const input of [
|
||||
{ method: "POST", route: "/mcp/missing/auth" },
|
||||
{ method: "POST", route: "/mcp/missing/auth/authenticate" },
|
||||
{ method: "POST", route: "/mcp/missing/auth/callback", body: JSON.stringify({ code: "code" }) },
|
||||
{ method: "DELETE", route: "/mcp/missing/auth" },
|
||||
{ method: "POST", route: "/mcp/missing/connect" },
|
||||
{ method: "POST", route: "/mcp/missing/disconnect" },
|
||||
]) {
|
||||
const response = yield* request(handler, input.route, tmp.directory, {
|
||||
method: input.method,
|
||||
headers: input.body ? { "content-type": "application/json" } : undefined,
|
||||
body: input.body,
|
||||
})
|
||||
|
||||
expect(response.status).toBe(404)
|
||||
expect(yield* json(response)).toEqual({
|
||||
_tag: "McpServerNotFoundError",
|
||||
name: "missing",
|
||||
message: "MCP server not found: missing",
|
||||
})
|
||||
}
|
||||
}),
|
||||
{ config: { mcp: {} } },
|
||||
)
|
||||
})
|
||||
|
||||
@@ -173,4 +173,21 @@ describe("PublicApi OpenAPI v2 errors", () => {
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
test("documents MCP server not-found errors", () => {
|
||||
const spec = OpenApi.fromApi(PublicApi) as OpenApiSpec
|
||||
|
||||
for (const route of [
|
||||
["post", "/mcp/{name}/auth"],
|
||||
["post", "/mcp/{name}/auth/authenticate"],
|
||||
["post", "/mcp/{name}/auth/callback"],
|
||||
["delete", "/mcp/{name}/auth"],
|
||||
["post", "/mcp/{name}/connect"],
|
||||
["post", "/mcp/{name}/disconnect"],
|
||||
] as const) {
|
||||
expect(componentName(responseRef(spec.paths[route[1]]?.[route[0]]?.responses?.["404"]) ?? "")).toBe(
|
||||
"McpServerNotFoundError",
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user