| 112 | const pool: MCPClients<TServers> = { |
| 113 | clients, |
| 114 | async tools(options?: ToolsOptions): Promise<Array<ServerTool>> { |
| 115 | // Settle (like the connect path) so a single failing server is reported |
| 116 | // by config key instead of rejecting with an unattributed SDK error. |
| 117 | const entries = Object.entries(clients) |
| 118 | const results = await Promise.allSettled( |
| 119 | entries.map(([, c]) => |
| 120 | (c as MCPClient<ServerDescriptor>).tools(options), |
| 121 | ), |
| 122 | ) |
| 123 | const failedNames = entries |
| 124 | .map(([key], i) => (results[i]?.status === 'rejected' ? key : null)) |
| 125 | .filter((k): k is string => k !== null) |
| 126 | if (failedNames.length > 0) { |
| 127 | const firstFailure = results.find( |
| 128 | (r): r is PromiseRejectedResult => r.status === 'rejected', |
| 129 | ) |
| 130 | throw new MCPConnectionError( |
| 131 | `Failed to list tools from MCP server(s): ${failedNames.join(', ')}`, |
| 132 | firstFailure?.reason, |
| 133 | ) |
| 134 | } |
| 135 | const all = results.flatMap((r) => |
| 136 | r.status === 'fulfilled' ? r.value : [], |
| 137 | ) |
| 138 | const seen = new Set<string>() |
| 139 | for (const t of all) { |
| 140 | if (seen.has(t.name)) throw new DuplicateToolNameError(t.name) |
| 141 | seen.add(t.name) |
| 142 | } |
| 143 | return all |
| 144 | }, |
| 145 | getServers(): Record< |
| 146 | string, |
| 147 | { |