()
| 1415 | } |
| 1416 | |
| 1417 | async function updateSdkMcp() { |
| 1418 | // Check if SDK MCP servers need to be updated (new servers added or removed) |
| 1419 | const currentServerNames = new Set(Object.keys(sdkMcpConfigs)) |
| 1420 | const connectedServerNames = new Set(sdkClients.map(c => c.name)) |
| 1421 | |
| 1422 | // Check if there are any differences (additions or removals) |
| 1423 | const hasNewServers = Array.from(currentServerNames).some( |
| 1424 | name => !connectedServerNames.has(name), |
| 1425 | ) |
| 1426 | const hasRemovedServers = Array.from(connectedServerNames).some( |
| 1427 | name => !currentServerNames.has(name), |
| 1428 | ) |
| 1429 | // Check if any SDK clients are pending and need to be upgraded |
| 1430 | const hasPendingSdkClients = sdkClients.some(c => c.type === 'pending') |
| 1431 | // Check if any SDK clients failed their handshake and need to be retried. |
| 1432 | // Without this, a client that lands in 'failed' (e.g. handshake timeout on |
| 1433 | // a WS reconnect race) stays failed forever — its name satisfies the |
| 1434 | // connectedServerNames diff but it contributes zero tools. |
| 1435 | const hasFailedSdkClients = sdkClients.some(c => c.type === 'failed') |
| 1436 | |
| 1437 | const haveServersChanged = |
| 1438 | hasNewServers || |
| 1439 | hasRemovedServers || |
| 1440 | hasPendingSdkClients || |
| 1441 | hasFailedSdkClients |
| 1442 | |
| 1443 | if (haveServersChanged) { |
| 1444 | // Clean up removed servers |
| 1445 | for (const client of sdkClients) { |
| 1446 | if (!currentServerNames.has(client.name)) { |
| 1447 | if (client.type === 'connected') { |
| 1448 | await client.cleanup() |
| 1449 | } |
| 1450 | } |
| 1451 | } |
| 1452 | |
| 1453 | // Re-initialize all SDK MCP servers with current config |
| 1454 | const sdkSetup = await setupSdkMcpClients( |
| 1455 | sdkMcpConfigs, |
| 1456 | (serverName, message) => |
| 1457 | structuredIO.sendMcpMessage(serverName, message), |
| 1458 | ) |
| 1459 | sdkClients = sdkSetup.clients |
| 1460 | sdkTools = sdkSetup.tools |
| 1461 | |
| 1462 | // Store SDK MCP tools in appState so subagents can access them via |
| 1463 | // assembleToolPool. Only tools are stored here — SDK clients are already |
| 1464 | // merged separately in the query loop (allMcpClients) and mcp_status handler. |
| 1465 | // Use both old (connectedServerNames) and new (currentServerNames) to remove |
| 1466 | // stale SDK tools when servers are added or removed. |
| 1467 | const allSdkNames = uniq([...connectedServerNames, ...currentServerNames]) |
| 1468 | setAppState(prev => ({ |
| 1469 | ...prev, |
| 1470 | mcp: { |
| 1471 | ...prev.mcp, |
| 1472 | tools: [ |
| 1473 | ...prev.mcp.tools.filter( |
| 1474 | t => |
no test coverage detected