@@ -130,7 +130,12 @@ function isMcpConfigured(entry: McpEntry): entry is ConfigMCP.Info {
130130const sanitize = ( s : string ) => s . replace ( / [ ^ a - z A - Z 0 - 9 _ - ] / g, "_" )
131131
132132// Convert MCP tool definition to AI SDK Tool type
133- function convertMcpTool ( mcpTool : MCPToolDef , client : MCPClient , timeout ?: number ) : Tool {
133+ function convertMcpTool (
134+ mcpTool : MCPToolDef ,
135+ getClient : ( ) => MCPClient | undefined ,
136+ clientName : string ,
137+ timeout ?: number ,
138+ ) : Tool {
134139 const inputSchema = mcpTool . inputSchema
135140
136141 // Spread first, then override type to ensure it's always "object"
@@ -145,6 +150,10 @@ function convertMcpTool(mcpTool: MCPToolDef, client: MCPClient, timeout?: number
145150 description : mcpTool . description ?? "" ,
146151 inputSchema : jsonSchema ( schema ) ,
147152 execute : async ( args : unknown ) => {
153+ const client = getClient ( )
154+ if ( ! client ) {
155+ throw new Error ( `MCP server \"${ clientName } \" is not connected` )
156+ }
148157 return client . callTool (
149158 {
150159 name : mcpTool . name ,
@@ -473,6 +482,27 @@ export const layer = Layer.effect(
473482 )
474483
475484 function watch ( s : State , name : string , client : MCPClient , bridge : EffectBridge . Shape , timeout ?: number ) {
485+ const prevOnClose = client . onclose
486+ client . onclose = ( ) => {
487+ prevOnClose ?.( )
488+ if ( s . clients [ name ] !== client ) return
489+
490+ log . warn ( "mcp client disconnected" , { name } )
491+ delete s . clients [ name ]
492+ delete s . defs [ name ]
493+ s . status [ name ] = { status : "failed" , error : "Connection closed" }
494+ void bridge . promise ( bus . publish ( ToolsChanged , { server : name } ) . pipe ( Effect . ignore ) )
495+ }
496+
497+ const prevOnError = client . onerror
498+ client . onerror = ( error ) => {
499+ prevOnError ?.( error )
500+ log . error ( "mcp client transport error" , {
501+ name,
502+ error : error instanceof Error ? error . message : String ( error ) ,
503+ } )
504+ }
505+
476506 client . setNotificationHandler ( ToolListChangedNotificationSchema , async ( ) => {
477507 log . info ( "tools list changed notification received" , { server : name } )
478508 if ( s . clients [ name ] !== client || s . status [ name ] ?. status !== "connected" ) return
@@ -657,7 +687,12 @@ export const layer = Layer.effect(
657687
658688 const timeout = entry ?. timeout ?? defaultTimeout
659689 for ( const mcpTool of listed ) {
660- result [ sanitize ( clientName ) + "_" + sanitize ( mcpTool . name ) ] = convertMcpTool ( mcpTool , client , timeout )
690+ result [ sanitize ( clientName ) + "_" + sanitize ( mcpTool . name ) ] = convertMcpTool (
691+ mcpTool ,
692+ ( ) => s . clients [ clientName ] ,
693+ clientName ,
694+ timeout ,
695+ )
661696 }
662697 } ) ,
663698 { concurrency : "unbounded" } ,
0 commit comments