@@ -103,7 +103,11 @@ import {
103103} from "../tools/web-search.js" ;
104104import { createUseSkillTool } from "./use-skill.js" ;
105105import { createSkillSearchTool } from "./skill-search.js" ;
106- import { createToolIndex , createToolSearchTool } from "./tool-search.js" ;
106+ import {
107+ createToolIndex ,
108+ createToolSearchTool ,
109+ TOOL_SEARCH_PENDING_WAIT_MS ,
110+ } from "./tool-search.js" ;
107111import { createSearchAgentsTool } from "./agent-search.js" ;
108112import { createReadAgentTraceTool } from "../subagent/trace-tool.js" ;
109113import {
@@ -319,6 +323,14 @@ export interface AgentToolset {
319323 // second add of an active name; failed rows retry through connectMCPServer
320324 // without a second persist. Still true while disable is in progress.
321325 hasMCPServer : ( name : string ) => boolean ;
326+ // Live MCP handshake depth: non-zero while any server is still connecting.
327+ // tool_search consults this on a miss — waiting briefly and reporting a
328+ // retry signal instead of a definitive negative mid-handshake.
329+ pendingMcpConnectionCount : ( ) => number ;
330+ // Bounded wait for in-flight MCP handshakes; resolves to the remaining
331+ // count. Capped by `timeoutMs` so a hung authorization never hangs the
332+ // caller — the tool_search bound passes briefly by default.
333+ awaitPendingMcpConnections : ( timeoutMs ?: number ) => Promise < number > ;
322334 // Catalog unshadow can change local → global/none without rebuilding the
323335 // toolset; connectOne reads this on every late connect.
324336 setMcpServersSource : ( source : "local" | "global" | "none" ) => void ;
@@ -719,6 +731,12 @@ export async function createAgentToolset(
719731 lookup : ( name ) =>
720732 runnerHolder . current ?. currentDefinitions ( ) . find ( ( d ) => d . name === name ) ,
721733 promote : ( names ) => promoter . promote ( names ) ,
734+ // Misses wait briefly for in-flight MCP handshakes (bounded, so hung
735+ // OAuth cannot hang the call) and re-search before answering. Reads the
736+ // connection map live — declared below, populated by the time any
737+ // search runs.
738+ awaitPendingConnections : ( timeoutMs = TOOL_SEARCH_PENDING_WAIT_MS ) =>
739+ awaitPendingMcpConnections ( timeoutMs ) ,
722740 } ) ,
723741 ) ;
724742
@@ -744,6 +762,30 @@ export async function createAgentToolset(
744762 const connectedClients = new Map < string , MCPClient > ( ) ;
745763 const inFlightConnections = new Map < string , Promise < void > > ( ) ;
746764 const inFlightEpochs = new Map < string , number > ( ) ;
765+ // Live handshake depth for tool_search: a miss waits briefly for these
766+ // before answering, so a mid-handshake search finds late-mounting tools
767+ // instead of reporting a definitive negative.
768+ const pendingMcpConnectionCount = ( ) : number => inFlightConnections . size ;
769+ // Bounded wait for in-flight handshakes; resolves to the remaining count.
770+ // Capped by `timeoutMs` so a hung authorization never hangs the caller.
771+ const awaitPendingMcpConnections = async (
772+ timeoutMs = TOOL_SEARCH_PENDING_WAIT_MS ,
773+ ) : Promise < number > => {
774+ if ( inFlightConnections . size === 0 ) return 0 ;
775+ const pending = [ ...inFlightConnections . values ( ) ] ;
776+ let timer : ReturnType < typeof setTimeout > | undefined ;
777+ try {
778+ await Promise . race ( [
779+ Promise . allSettled ( pending ) ,
780+ new Promise < void > ( ( resolve ) => {
781+ timer = setTimeout ( resolve , timeoutMs ) ;
782+ } ) ,
783+ ] ) ;
784+ } finally {
785+ if ( timer !== undefined ) clearTimeout ( timer ) ;
786+ }
787+ return inFlightConnections . size ;
788+ } ;
747789 const disabledNames = new Set < string > ( ) ;
748790 const serverAborts = new Map < string , AbortController > ( ) ;
749791 const serverEpochs = new Map < string , number > ( ) ;
@@ -1218,6 +1260,8 @@ export async function createAgentToolset(
12181260 disconnectMCPServer : publicDisconnectMCPServer ,
12191261 hasMCPServer : ( name ) =>
12201262 connectedClients . has ( name ) || inFlightConnections . has ( name ) ,
1263+ pendingMcpConnectionCount,
1264+ awaitPendingMcpConnections,
12211265 setMcpServersSource : ( source ) => {
12221266 mcpServersSource = source ;
12231267 } ,
0 commit comments