* Flatten the `clients` input into a registry keyed UNIFORMLY by `prefix` (the * value the widget sends as `serverId`). A pool contributes one entry per * configured server (keyed by that server's `prefix`, NOT its config key); a * single client is keyed by `getInfo().prefix`. Entries whose `pref
(clients: McpAppClientsInput)
| 119 | * overwrite. |
| 120 | */ |
| 121 | function buildRegistry(clients: McpAppClientsInput): AppRegistry { |
| 122 | const entries = Array.isArray(clients) ? clients : [clients] |
| 123 | const byServerId: Record<string, McpServerDescriptor> = {} |
| 124 | let fallback: McpServerDescriptor | null = null |
| 125 | let total = 0 |
| 126 | |
| 127 | const add = (info: { |
| 128 | transport: McpServerDescriptor['transport'] |
| 129 | prefix: string | undefined |
| 130 | }) => { |
| 131 | const descriptor: McpServerDescriptor = { |
| 132 | transport: info.transport, |
| 133 | prefix: info.prefix, |
| 134 | } |
| 135 | total += 1 |
| 136 | const key = info.prefix |
| 137 | if (key === undefined || key === '') { |
| 138 | if (fallback !== null) { |
| 139 | throw new Error( |
| 140 | 'createMcpAppCallHandler: multiple clients without a prefix; serverId routing is ambiguous', |
| 141 | ) |
| 142 | } |
| 143 | fallback = descriptor |
| 144 | return |
| 145 | } |
| 146 | if (key in byServerId) { |
| 147 | throw new Error(`createMcpAppCallHandler: duplicate serverId "${key}"`) |
| 148 | } |
| 149 | byServerId[key] = descriptor |
| 150 | } |
| 151 | |
| 152 | for (const entry of entries) { |
| 153 | if (isPool(entry)) { |
| 154 | for (const info of Object.values(entry.getServers())) { |
| 155 | add(info) |
| 156 | } |
| 157 | } else { |
| 158 | add(entry.getInfo()) |
| 159 | } |
| 160 | } |
| 161 | return { byServerId, fallback, total } |
| 162 | } |
| 163 | |
| 164 | /** |
| 165 | * Invoke an optional `onError` hook, absorbing BOTH synchronous and asynchronous |
no test coverage detected