mirror of
https://github.com/anomalyco/opencode.git
synced 2026-05-13 15:44:56 +00:00
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:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user