refactor(mcp): use Effect-native catch + tryPromise instead of try/catch

Per CLAUDE.md style guide ("Avoid try/catch where possible") and the
opencode Effect rules ("Use Effect.tryPromise for promise-based APIs",
"Use Effect.fnUntraced for internal helpers"), replace the plain
async function + try/catch in listTools with an Effect-native
listToolsTolerant that composes via Effect.tryPromise + Effect.catch.

Also drops the no-op `Effect.map((tools) => tools)` identity in defs(),
and extracts a tiny `wrapAsError` helper to remove the duplicated
"err instanceof Error ? ... : new Error(String(err))" expression.

No behavior change. 20/20 tests still green.
This commit is contained in:
Developer
2026-05-09 19:15:36 -04:00
parent d3a69ad910
commit ff6f032faa

View File

@@ -137,25 +137,30 @@ function remoteURL(key: string, value: string) {
log.warn("invalid remote mcp url", { key })
}
function isSchemaReferenceError(err: Error) {
return /can't resolve reference|schema.*reference|reference.*schema/i.test(err.message)
function isSchemaReferenceError(err: unknown) {
return err instanceof Error && /can't resolve reference|schema.*reference|reference.*schema/i.test(err.message)
}
async function listTools(key: string, client: MCPClient, timeout: number) {
try {
return (await client.listTools(undefined, { timeout })).tools
} catch (err) {
const error = err instanceof Error ? err : new Error(String(err))
if (!isSchemaReferenceError(error)) throw error
const wrapAsError = (err: unknown) => (err instanceof Error ? err : new Error(String(err)))
log.warn("failed to validate MCP tool output schemas, retrying without output schema validation", {
key,
error,
})
const result = await client.request({ method: "tools/list" }, TolerantListToolsResultSchema, { timeout })
return result.tools as MCPToolDef[]
}
function listToolsTolerant(key: string, client: MCPClient, timeout: number) {
return Effect.tryPromise({
try: () => client.listTools(undefined, { timeout }),
catch: wrapAsError,
}).pipe(
Effect.map((result) => result.tools),
Effect.catch((err) => {
if (!isSchemaReferenceError(err)) return Effect.fail(err)
log.warn("failed to validate MCP tool output schemas, retrying without output schema validation", {
key,
error: err,
})
return Effect.tryPromise({
try: () => client.request({ method: "tools/list" }, TolerantListToolsResultSchema, { timeout }),
catch: wrapAsError,
}).pipe(Effect.map((result) => result.tools as MCPToolDef[]))
}),
)
}
// Convert MCP tool definition to AI SDK Tool type
@@ -190,11 +195,7 @@ function convertMcpTool(mcpTool: MCPToolDef, client: MCPClient, timeout?: number
}
function defs(key: string, client: MCPClient, timeout?: number) {
return Effect.tryPromise({
try: () => listTools(key, client, timeout ?? DEFAULT_TIMEOUT),
catch: (err) => (err instanceof Error ? err : new Error(String(err))),
}).pipe(
Effect.map((tools) => tools),
return listToolsTolerant(key, client, timeout ?? DEFAULT_TIMEOUT).pipe(
Effect.catch((err) => {
log.error("failed to get tools from client", { key, error: err })
return Effect.succeed(undefined)