Skip to content

@temporalio/ai-sdk: reuse MCP client connections across tool calls (idle-timeout pooling, matching strands-agents) #2222

Description

@wankhede04

Title: @temporalio/ai-sdk: reuse MCP client connections across tool calls (idle-timeout pooling, matching strands-agents)

Is your feature request related to a problem? Please describe.

In contrib/ai-sdk/src/activities.ts, activitiesForName(name, mcpClientFactory) creates a brand new MCP client on every single listToolsActivity/callToolActivity invocation:

const mcpClient = await mcpClientFactory(args.clientArgs)
try {
  // ... call the tool ...
} finally {
  await mcpClient.close()
}

For a multi-turn agent loop — the normal usage pattern, where an LLM calls the same MCP server's tools repeatedly across steps — this means the underlying MCP transport (a stdio subprocess spawn, an SSE/HTTP handshake, an OAuth token exchange) is torn down and rebuilt on every single tool call and every listTools refresh. For stdio-based or OAuth-secured MCP servers in particular, this adds real, avoidable latency and resource churn to every agent step.

Notably, this repo has already solved this exact problem twice, just not in this package:

  • @temporalio/strands-agents (contrib/strands/src/plugin.ts, contrib/strands/src/temporal-mcp-client.ts) already ships StrandsPluginOptions.mcpConnectionIdleTimeout (default 5 minutes), keeping one lazily-opened MCP connection alive across activity calls and resetting the idle timer on reuse.
  • @temporalio/openai-agents (contrib/openai-agents/src/worker/stateful-mcp-provider.ts) goes further with a StatefulMCPServerProvider that keeps one persistent MCP session alive for an entire workflow run.

@temporalio/ai-sdk is the newest, most actively-developed of the three contrib packages (it just had its ai@7 provider-spec upgrade merged in #2192), and it's the one left without either pattern.

Describe the solution you'd like

Port the lighter, already-proven strands-agents idle-timeout pattern into @temporalio/ai-sdk:

  1. In contrib/ai-sdk/src/activities.ts, change activitiesForName to hold a Map<string, { client: MCPClient; timer: NodeJS.Timeout }> keyed by MCP server name, lazily creating the client via mcpClientFactory on first use and reusing it across subsequent listTools/callTool calls within the same worker process, instead of closing it in every finally.
  2. Add an mcpConnectionIdleTimeout option to createActivities(provider, mcpClientFactories, options?), mirroring StrandsPluginOptions.mcpConnectionIdleTimeout (a Duration, default ~5 minutes); reset the idle timer on each reuse; evict/close the connection on idle expiry or worker shutdown.
  3. Keep today's stateless behavior available as an opt-out (e.g. mcpConnectionIdleTimeout: 0) for transports/servers that don't tolerate a reused/concurrent session.
  4. Add tests mirroring the existing test-ai-sdk.ts MCP tests, verifying the client factory is called once and reused across repeated tools() calls, and that the connection is evicted after the idle window.

This is scoped to a lifecycle/caching change inside one existing file plus one new option — not a rewrite of the MCP integration, and not an attempt to unify all three contrib packages' MCP handling into a shared library (a larger, separate ask).

Describe alternatives you've considered

  • Leaving connection lifecycle as-is and relying on users to configure long-lived MCP servers themselves — doesn't help stdio-subprocess-based servers, where "long-lived" isn't an option if the SDK itself closes the client after every call.
  • Adopting the heavier openai-agents-style dedicated-Worker-per-run session model instead of the strands-agents idle-timeout model — possible, but a bigger design/risk surface for a first pass; happy to expand scope if maintainers prefer that direction.

Additional context

Not aware of any existing issue or PR covering this — searched for connection pooling/reuse, MCP session reuse, and reconnect-overhead related terms with no hits; the only PRs touching contrib/ai-sdk/src/activities.ts are the ai@7 upgrade (#2192) and an unrelated schema fix. Happy to contribute a PR for this if the team is open to the approach (or to the heavier session-model alternative, if preferred).

Metadata

Metadata

Assignees

No one assigned

    Labels

    ai-sdkRelated to AI integrationsenhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions