@@ -25,6 +25,11 @@ interface CachedClient {
2525
2626const cache = new Map < string , CachedClient > ( ) ;
2727const CACHE_TTL_MS = 5 * 60 * 1000 ;
28+ // OAuth clients use a much shorter TTL so a mid-session reconnect (new tokens
29+ // in the DB) is picked up quickly. We can't close an OAuth client right after
30+ // getTools() — its connection is what backs every subsequent tool invocation
31+ // in the same turn, and closing it produces "Not connected" errors.
32+ const OAUTH_CACHE_TTL_MS = 30 * 1000 ;
2833
2934function configsSignature ( configs : UserMcpServerConfig [ ] ) : string {
3035 return configs
@@ -121,13 +126,14 @@ export async function buildUserMcpToolsFromConfigs(
121126 return t ;
122127 } ) ;
123128
124- // Only cache the client when it's safe to. OAuth servers' auth state can
125- // change between turns (user just connected via loopback flow); a cached
126- // pre-connect client would mask the new tokens until TTL expires. So if
127- // any config is OAuth, skip the cache entirely .
129+ // Cache so subsequent middleware invocations within the same turn (and
130+ // adjacent turns) reuse the live connection. OAuth servers get a shorter
131+ // TTL so a user's reconnect is picked up promptly without leaving the
132+ // current turn's tool calls dangling against a closed client .
128133 const hasOAuth = configs . some ( ( c ) => c . authType === 'oauth' ) ;
129- if ( prefixed . length > 0 && ! hasOAuth ) {
130- cache . set ( signature , { client, signature, expiresAt : now + CACHE_TTL_MS } ) ;
134+ if ( prefixed . length > 0 ) {
135+ const ttl = hasOAuth ? OAUTH_CACHE_TTL_MS : CACHE_TTL_MS ;
136+ cache . set ( signature , { client, signature, expiresAt : now + ttl } ) ;
131137 } else {
132138 client . close ( ) . catch ( ( ) => { } ) ;
133139 }
0 commit comments