Skip to content

feat(mcp): bound retained stateful sessions through a supported lifecycle API #2157

Closed as not planned

Description

@AlexStocks

Problem

Follow-up to #2084 R7. Request body and active HTTP concurrency limits do not bound retained stateful MCP sessions after their HTTP requests finish.

The current lock resolves FastMCP 3.4.4. Its public FastMCP.http_app does not accept session_idle_timeout; the underlying MCP SDK manager supports the option, but the pinned FastMCP composition does not forward it. Changing the private manager inside its lifespan would create an unsupported coupling.

PR #2162 provides an explicit stateless HTTP deployment option. That avoids retained transport sessions for tools-only deployments, but the default remains stateful and has no configured idle expiry.

Implementation boundary

Inspect src/powercontext/server/mcp.py, server/settings.py, the pinned FastMCP composition, and actual supported host workflows. Evaluate a compatible dependency update that exposes a public idle-timeout composition option, or another documented public session lifecycle boundary.

Define idle time, maximum retained sessions, active stream behavior, admission behavior, and reconnection/resumability before changing defaults. Scope limits per process explicitly; do not claim that HTTP request concurrency bounds retained session count.

Acceptance criteria

  • Real SDK clients initialize, list tools, call a tool, and disconnect in supported stateful and stateless modes.
  • Idle sessions expire and release retained tasks/state.
  • Live requests and supported streams are not incorrectly expired.
  • Expired session requests receive a protocol-compatible response and can initialize a new session.
  • Capacity, cleanup on cancellation, shutdown, and repeated reconnects are exercised through real HTTP.
  • Document configuration and host compatibility in both locales.

Related but separate

#2082 concerns session/principal isolation and must retain its own security acceptance criteria. A timeout or stateless opt-in does not establish that the default stateful identity issue is fixed.

Activity

  1. AlexStocks commented on Oct 21, 2026

    @AlexStocks
    ContributorAuthor

    Additional pinned-composition evidence for #2084 R7 / #2157.

    Environment: FastMCP 3.4.4, MCP SDK 1.28.1, CPython 3.11.15 under Linux.
    The probe calls public FastMCP.http_app(path="/") through real ASGI HTTP
    requests. It does not access the private session manager or mutate its state.

    1. Twenty GET requests without Mcp-Session-Id, accepting JSON and SSE, all
      return HTTP 400 and each returns a newly allocated session header.
    2. After those responses finish, the otherwise empty application has 60
      TaskHandle._run_coro tasks, compared with zero before the requests.
    3. Twenty protocol DELETE requests using the returned session IDs all return
      200. After allowing scheduled cleanup to run, those tasks are gone.
    4. A new initial POST is cancelled while its body receiver is waiting, before
      any response headers are sent. The request exits, but 3 task handles remain.
      Shutting down the application lifespan releases them.

    The task counts are diagnostics for this pinned composition, not a stable
    public metric or a general claim that every session always has three tasks.
    The cancellation case exercises bare FastMCP. PR #2162 pre-buffers external
    PowerContext bodies, so this exact body-read cancellation is not claimed as a
    reproduction through the patched complete Server stack.

    This establishes two additional acceptance cases for a supported stateful
    lifecycle solution:

    • Rejected sessionless requests must not leave unbounded retained state.
    • Capacity ownership and expiry must cover initialization before a response
      exposes its session identity, including cancellation and failed handshakes.

    It also limits a possible response-header wrapper: public protocol DELETE
    can clean up known IDs, but observing response headers alone does not account
    for every allocated session. Releasing an initialization reservation merely
    because its HTTP request exited can undercount retained state. Such a wrapper
    is not by itself proof of a retained-session capacity bound.

    FastMCP.http_app in the pinned version still has no session_idle_timeout
    parameter. No private-manager patch, dependency change or new lifecycle default
    has been submitted. Stateful idle expiry and retained capacity remain open.

  2. AlexStocks commented on Oct 21, 2026

    @AlexStocks
    ContributorAuthor

    The default-stateful premise is superseded by merged #2134 (339e1d0be3dcc935f74c82af4a222b7dc7b691f4, merged 2026-10-21). Current master unconditionally composes http_app(path="/", stateless_http=True) so tool execution receives each HTTP request's current authentication identity. It does not issue MCP transport session IDs or promise cross-request SSE resumption.

    #2162 has now merged that change at 26ea7ba34fd64c08a0c4ff7d8c7685fae0debe34, removes its superseded stateful opt-in, and documents the actual transport in both locales. Linux validation through real HTTP/SDK clients, one/two admission slots, cross-user identity, revocation and reconnect paths passed (40 related cases). The request-body/response admission limit remains distinct from any hypothetical retained-state bound.

    Closing this retained-stateful lifecycle proposal as not planned for the currently supported stateless transport. This does not claim an idle-expiry/capacity implementation for stateful MCP, nor that the earlier bare-FastMCP cancellation experiment was a complete Server reproduction. Reintroducing stateful transport would require a separate explicit design restoring per-request identity safety, supported lifecycle/capacity ownership, reconnection and cancellation acceptance; changing private SDK managers is not adopted here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions