| 115 | |
| 116 | /** Call a tool on a connected client and return a text result for the model. */ |
| 117 | export async function callMcpTool( |
| 118 | connection: McpConnection, |
| 119 | originalName: string, |
| 120 | args: Record<string, unknown>, |
| 121 | ): Promise<{ text: string; isError?: boolean }> { |
| 122 | const result = await connection.client.callTool({ name: originalName, arguments: args }) |
| 123 | const content = (result.content ?? []) as Array<Record<string, unknown>> |
| 124 | const parts: string[] = [] |
| 125 | for (const block of content) { |
| 126 | const type = block.type as string |
| 127 | if (type === "text" && typeof block.text === "string") { |
| 128 | parts.push(block.text) |
| 129 | } else if (type === "resource") { |
| 130 | const resource = block.resource as Record<string, unknown> | undefined |
| 131 | if (resource && typeof resource.text === "string") parts.push(resource.text) |
| 132 | } else if (type === "image") { |
| 133 | parts.push(`[image: ${block.mimeType}]`) |
| 134 | } else if (type === "audio") { |
| 135 | parts.push(`[audio: ${block.mimeType}]`) |
| 136 | } else if (type === "resource_link" && typeof block.uri === "string") { |
| 137 | parts.push(`[resource: ${block.uri}]`) |
| 138 | } |
| 139 | } |
| 140 | const text = parts.join("\n") || "(empty MCP tool result)" |
| 141 | return { text, isError: Boolean(result.isError) } |
| 142 | } |
| 143 | |
| 144 | function withTimeout<T>(promise: Promise<T>, ms: number, message: string): Promise<T> { |
| 145 | let timer: ReturnType<typeof setTimeout> | undefined |