Skip to content

fix(fastmcp): add session_idle_timeout parameter to prevent unbounded session memory growth#3443

Open
abhijeethp wants to merge 3 commits intoPrefectHQ:mainfrom
abhijeethp:main
Open

fix(fastmcp): add session_idle_timeout parameter to prevent unbounded session memory growth#3443
abhijeethp wants to merge 3 commits intoPrefectHQ:mainfrom
abhijeethp:main

Conversation

@abhijeethp
Copy link
Copy Markdown
Contributor

Description

Add session_idle_timeout parameter to prevent unbounded session memory growth

In stateful HTTP mode (the default), StreamableHTTPSessionManager accumulates sessions in an in-memory dict without any TTL, cleanup, or max session limit. Disconnected clients leave orphaned sessions with live tasks and memory streams. Terminated sessions persist as zombie entries. Over time this causes unbounded memory growth, eventually leading to OOM on long-running servers.

The MCP Python SDK has merged support for session_idle_timeout ([modelcontextprotocol/python-sdk#2022](modelcontextprotocol/python-sdk#2022), [modelcontextprotocol/python-sdk#1994](modelcontextprotocol/python-sdk#1994)) but has not yet released it. This PR threads the parameter through FastMCP so it is ready to use once the SDK releases.

Changes

  • settings.py: Add session_idle_timeout: float | None = None (configurable via FASTMCP_SESSION_IDLE_TIMEOUT env var)
  • server/http.py: Accept session_idle_timeout in create_streamable_http_app(), pass it to StreamableHTTPSessionManager with runtime detection — logs a warning if the installed MCP SDK doesn't support it yet
  • server/mixins/transport.py: Thread session_idle_timeout through http_app() and run_http_async() with settings fallback
  • tests/client/test_streamable_http.py: 6 new tests covering parameter threading, settings defaults, settings override, SDK compatibility warning, and end-to-end idle session cleanup (auto-skipped until SDK releases)

Usage (once MCP SDK >= 1.27.0 is released)

mcp = FastMCP("MyServer")
app = mcp.http_app(session_idle_timeout=1800)  # 30 min

Or via environment variable:

export FASTMCP_SESSION_IDLE_TIMEOUT=1800

This PR was authored with the help of Claude Code.

Contributors Checklist

  • My change closes #(issue number)
  • I have followed the repository's development workflow
  • I have tested my changes manually and by adding relevant tests
  • I have performed all required documentation updates

Review Checklist

  • I have self-reviewed my changes
  • My Pull Request is ready for review

@marvin-context-protocol marvin-context-protocol bot added bug Something isn't working. Reports of errors, unexpected behavior, or broken functionality. server Related to FastMCP server implementation or server-side functionality. http Related to HTTP transport, networking, or web server functionality. labels Mar 7, 2026
@jlowin jlowin added the DON'T MERGE PR is not ready for merging. Used by authors to prevent premature merging. label Mar 15, 2026
Copy link
Copy Markdown
Member

@jlowin jlowin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks - with no 1.27 in sight yet, I've added a dont merge label to prevent accidental conflicts.

I recommend the following:

  • remove the conditional test skip; when you add this we should bump mcp>=1.27, so add that to pyproject.toml
  • relatedly, I would expect CI to fail here until 1.27 exists. right now CI is protected if it runs on a lower version but users would experience an error if they tried to pass this I think

@abhijeethp
Copy link
Copy Markdown
Contributor Author

abhijeethp commented Mar 16, 2026

Thanks @jlowin for reviewing. Plan sounds good 👍
I made the changes as you suggested.
I will rerequest for review once 1.27 is out and this branch works

@marvin-context-protocol
Copy link
Copy Markdown
Contributor

Test Failure Analysis

Summary: All CI jobs fail at the Setup uv step because mcp 1.27.0 doesn't exist on PyPI yet, making the lockfile unsatisfiable.

Root Cause: The latest commit ("Require mcp>=1.27.0 and remove runtime SDK detection") bumped the mcp dependency in pyproject.toml from >=1.24.0,<2.0 to >=1.27.0,<2.0. Since mcp 1.27.0 hasn't been released, uv sync --locked fails immediately:

× No solution found when resolving dependencies:
╰─▶ Because only mcp<=1.26.0 is available and your project depends on
    mcp>=1.27.0,<2.0, we can conclude that your project's requirements are
    unsatisfiable.

Suggested Solution: Revert the mcp version pin back to >=1.24.0,<2.0 (or whatever was there before) and restore runtime detection for session_idle_timeout support. The PR description notes the SDK feature is merged but not yet released — the right approach is to detect it at runtime, not to hard-require an unreleased version. Once mcp 1.27.0 ships to PyPI, the lockfile can be updated and the hard dependency can be added.

Detailed Analysis

Failing log excerpt (all 5 jobs):

× No solution found when resolving dependencies for split (markers:
│ python_full_version >= '3.14'):
╰─▶ Because only mcp<=1.26.0 is available and your project depends on
    mcp>=1.27.0,<2.0, we can conclude that your project's requirements are
    unsatisfiable.
    And because your project requires fastmcp[anthropic], we can conclude
    that your project's requirements are unsatisfiable.

hint: While the active Python version is 3.13, the resolution failed for
other Python versions supported by your project.
##[error]Process completed with exit code 1.

File causing the issue: pyproject.toml line ~10:

-    "mcp>=1.24.0,<2.0",
+    "mcp>=1.27.0,<2.0",

The previous version of this PR used runtime inspection (inspect.signature) to detect whether StreamableHTTPSessionManager supports session_idle_timeout, which is the correct approach when depending on an unreleased feature.

Related Files
  • pyproject.toml — dependency version bumped to unavailable mcp>=1.27.0
  • uv.lock — lockfile generated with the unsatisfiable constraint
  • src/fastmcp/server/http.py — where session_idle_timeout is threaded into StreamableHTTPSessionManager

fastmcp.settings.session_idle_timeout = 60.0
server = create_test_server()
# Should not raise - the setting is picked up
app = server.http_app()
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a little weak of an assertion -- is there a way for us to verify this was applied to the server?

fastmcp.settings.session_idle_timeout = 60.0
server = create_test_server()
# Explicit value should override settings
app = server.http_app(session_idle_timeout=120.0)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this one is probably fine due to type checking?

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

Labels

bug Something isn't working. Reports of errors, unexpected behavior, or broken functionality. DON'T MERGE PR is not ready for merging. Used by authors to prevent premature merging. http Related to HTTP transport, networking, or web server functionality. server Related to FastMCP server implementation or server-side functionality.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants