Skip to content

server: make stateful MCP sessions safer for one-shot clients #903

Description

@bokelley

Problem

Stateful Streamable HTTP is the fastest mode when clients reuse a session, but it is brittle when a client opens a fresh MCP session per operation and does not explicitly terminate it.

We hit this in a real salesagent deployment while running a managed storefront flow:

  1. get_products
  2. create_media_buy
  3. sync_creatives

The upstream salesagent container was OOM-killed during the media-buy / creative path. The last logs before the kill showed repeated MCP session setup:

Created new transport with session ID ...
Processing request of type ListToolsRequest

The local salesagent issue is bokelley/salesagent#676.

Why this matters

The current Python SDK server default keeps stateful MCP sessions alive for 1800 seconds. That is reasonable for a small number of well-behaved, long-lived clients, but it creates a poor failure mode for service-to-service callers that accidentally create one session per tool call:

  • repeated initialize / tools/list overhead
  • retained per-session server state until idle reap
  • memory pressure that is hard to diagnose because there may be no Python exception before OOM

Stateful mode should remain viable and fast. The gap is that abandoned or one-shot sessions need better lifecycle controls and observability.

Requested SDK improvements

Please consider one or more of:

  • Provide clearer server guidance for stateful Streamable HTTP: clients should reuse Mcp-Session-Id across a workflow and explicitly close/terminate when done.
  • Consider a shorter or more workload-safe default session_idle_timeout, or document that public/service-to-service sellers should tune it down.
  • Expose active-session metrics or a debug hook so adopters can see session count, session age, and session creation rate.
  • Add an optional max-active-sessions / max-idle-sessions guard that rejects or reaps old sessions before process memory is exhausted.
  • Make session termination behavior obvious and easy to call from SDK clients.

Workaround downstream

In salesagent we are keeping stateful mode as the default for performance, but passing a shorter session_idle_timeout so abandoned sessions are bounded.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions