@@ -180,12 +180,12 @@ export async function getNormalActorsAsTools(
180180Actor description: ${ actorDefinitionPruned . description }
181181Instructions: ${ ACTOR_ADDITIONAL_INSTRUCTIONS } ` ,
182182 inputSchema : actorDefinitionPruned . input
183- // So Actor without input schema works - MCP client expects JSON schema valid output
184- || {
185- type : 'object' ,
186- properties : { } ,
187- required : [ ] ,
188- } ,
183+ // So Actor without input schema works - MCP client expects JSON schema valid output
184+ || {
185+ type : 'object' ,
186+ properties : { } ,
187+ required : [ ] ,
188+ } ,
189189 // Additional props true to allow skyfire-pay-id
190190 ajvValidate : fixedAjvCompile ( ajv , { ...actorDefinitionPruned . input , additionalProperties : true } ) ,
191191 memoryMbytes : memoryMbytes > ACTOR_MAX_MEMORY_MBYTES ? ACTOR_MAX_MEMORY_MBYTES : memoryMbytes ,
@@ -207,21 +207,22 @@ async function getMCPServersAsTools(
207207 /**
208208 * This is case for the Skyfire request without any Apify token, we do not support
209209 * standby Actors in this case so we can skip MCP servers since they would fail anyway (they are standby Actors).
210- */
210+ */
211211 if ( apifyToken === null || apifyToken === undefined ) {
212212 return [ ] ;
213213 }
214214
215- const actorsMCPServerTools : ToolEntry [ ] = [ ] ;
216- for ( const actorInfo of actorsInfo ) {
215+ // Process all actors in parallel
216+ const actorToolPromises = actorsInfo . map ( async ( actorInfo ) => {
217217 const actorId = actorInfo . actorDefinitionPruned . id ;
218218 if ( ! actorInfo . webServerMcpPath ) {
219219 log . warning ( 'Actor does not have a web server MCP path, skipping' , {
220220 actorFullName : actorInfo . actorDefinitionPruned . actorFullName ,
221221 actorId,
222222 } ) ;
223- continue ;
223+ return [ ] ;
224224 }
225+
225226 const mcpServerUrl = await getActorMCPServerURL (
226227 actorInfo . actorDefinitionPruned . id , // Real ID of the Actor
227228 actorInfo . webServerMcpPath ,
@@ -232,17 +233,25 @@ async function getMCPServersAsTools(
232233 mcpServerUrl,
233234 } ) ;
234235
235- let client : Client | undefined ;
236+ let client : Client | null = null ;
236237 try {
237238 client = await connectMCPClient ( mcpServerUrl , apifyToken ) ;
239+ if ( ! client ) {
240+ // Skip this Actor, connectMCPClient will log the error
241+ return [ ] ;
242+ }
238243 const serverTools = await getMCPServerTools ( actorId , client , mcpServerUrl ) ;
239- actorsMCPServerTools . push ( ... serverTools ) ;
244+ return serverTools ;
240245 } finally {
241246 if ( client ) await client . close ( ) ;
242247 }
243- }
248+ } ) ;
244249
245- return actorsMCPServerTools ;
250+ // Wait for all actors to be processed in parallel
251+ const actorToolsArrays = await Promise . all ( actorToolPromises ) ;
252+
253+ // Flatten the arrays of tools
254+ return actorToolsArrays . flat ( ) ;
246255}
247256
248257export async function getActorsAsTools (
@@ -382,10 +391,13 @@ The step parameter enforces this workflow - you cannot call an Actor without fir
382391 if ( isActorMcpServer ) {
383392 // MCP server: list tools
384393 const mcpServerUrl = mcpServerUrlOrFalse ;
385- let client : Client | undefined ;
394+ let client : Client | null = null ;
386395 // Nested try to ensure client is closed
387396 try {
388397 client = await connectMCPClient ( mcpServerUrl , apifyToken ) ;
398+ if ( ! client ) {
399+ return buildMCPResponse ( [ `Failed to connect to MCP server ${ mcpServerUrl } ` ] ) ;
400+ }
389401 const toolsResponse = await client . listTools ( ) ;
390402
391403 const toolsInfo = toolsResponse . tools . map ( ( tool ) => `**${ tool . name } **\n${ tool . description || 'No description' } \nInput Schema: ${ JSON . stringify ( tool . inputSchema , null , 2 ) } ` ,
@@ -451,9 +463,12 @@ The step parameter enforces this workflow - you cannot call an Actor without fir
451463 }
452464
453465 const mcpServerUrl = mcpServerUrlOrFalse ;
454- let client : Client | undefined ;
466+ let client : Client | null = null ;
455467 try {
456468 client = await connectMCPClient ( mcpServerUrl , apifyToken ) ;
469+ if ( ! client ) {
470+ return buildMCPResponse ( [ `Failed to connect to MCP server ${ mcpServerUrl } ` ] ) ;
471+ }
457472
458473 const result = await client . callTool ( {
459474 name : mcpToolName ,
@@ -495,7 +510,7 @@ The step parameter enforces this workflow - you cannot call an Actor without fir
495510 if ( ! callResult ) {
496511 // Receivers of cancellation notifications SHOULD NOT send a response for the cancelled request
497512 // https://modelcontextprotocol.io/specification/2025-06-18/basic/utilities/cancellation#behavior-requirements
498- return { } ;
513+ return { } ;
499514 }
500515
501516 const content = buildActorResponseContent ( actorName , callResult ) ;
0 commit comments