@@ -81,6 +81,8 @@ type ServerEntry = {
8181// persisted session row, so their forms are owned by this opaque sentinel session identifier.
8282const GLOBAL_ELICITATION_SESSION_ID = "global"
8383const URL_ELICITATION_FIELD_KEY = "elicitation"
84+ // Connections remain Location-scoped, but shared remote endpoints should not receive concurrent startup bursts.
85+ const endpointLoads = KeyedMutex . makeUnsafe < string > ( )
8486
8587type Data = {
8688 servers : Map < ServerName , Types . DeepMutable < Mcp . ServerConfig > >
@@ -387,7 +389,7 @@ export const layer = (options?: Options) =>
387389 const { McpClient } = yield * Effect . promise ( ( ) => import ( "./client.js" ) )
388390 // List tools as part of connect so a failure here marks the server failed rather than
389391 // leaving it connected with a silently empty tool list and no path to recover.
390- const result = yield * McpClient . connect (
392+ const load = McpClient . connect (
391393 name ,
392394 entry . config ,
393395 location . directory ,
@@ -399,8 +401,10 @@ export const layer = (options?: Options) =>
399401 // A stdio server is spawned on this location's execution plane, not the host's.
400402 Effect . provideService ( Environment . Service , environment ) ,
401403 Scope . provide ( scope ) ,
402- Effect . exit ,
403404 )
405+ const result = yield * (
406+ entry . config . type === "remote" ? endpointLoads . withLock ( entry . config . url ) ( load ) : load
407+ ) . pipe ( Effect . exit )
404408 if ( Exit . isSuccess ( result ) ) {
405409 entry . client = result . value . connection
406410 entry . tools = result . value . tools . map ( ( tool ) => toTool ( name , entry , tool ) )
0 commit comments