Skip to content

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

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

fix(fastmcp): add session_idle_timeout parameter to prevent unbounded session memory growth#3443
abhijeethp wants to merge 4 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

marvin-context-protocol bot commented Mar 16, 2026

Test Failure Analysis

Summary: All CI jobs fail during dependency resolution because mcp 1.27.0 does not exist on PyPI (latest is 1.26.0), making the project's requirements unsatisfiable.

Root Cause: The PR bumped the mcp minimum version in pyproject.toml from >=1.24.0,<2.0 to >=1.27.0,<2.0 to use the new session_idle_timeout parameter on StreamableHTTPSessionManager. Since mcp 1.27.0 has not been released yet, uv sync --locked fails immediately on every job:

× 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 in pyproject.toml and restore runtime detection for session_idle_timeout support using inspect.signature (to check whether StreamableHTTPSessionManager accepts the parameter before passing it). Once mcp 1.27.0 ships to PyPI, update the lockfile and add the hard dependency.

Detailed Analysis

Failing log excerpt (all 6 jobs, identical root cause):

× 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.

The change that caused the failure (pyproject.toml):

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

The uv.lock file was not updated in the PR (it still records mcp 1.26.0), and since mcp 1.27.0 does not exist on PyPI, the lockfile is unsatisfiable. All 6 matrix jobs fail at the uv sync --locked step before any tests run.

The implementation in src/fastmcp/server/http.py conditionally passes session_idle_timeout to StreamableHTTPSessionManager only when not None, which is a reasonable guard — but it still requires the SDK to accept that kwarg at runtime. The correct approach while mcp 1.27.0 is unreleased is to also guard with inspect.signature to avoid a TypeError on older SDK versions.

Related Files
  • pyproject.tomlmcp minimum version bumped to unavailable >=1.27.0
  • uv.lock — not updated; still locked to mcp 1.26.0, making it inconsistent
  • src/fastmcp/server/http.pysession_idle_timeout conditionally passed to StreamableHTTPSessionManager
  • src/fastmcp/server/mixins/transport.pysession_idle_timeout threaded through run_http_async and http_app
  • src/fastmcp/settings.py — new session_idle_timeout setting added
  • tests/client/test_streamable_http.py — new TestSessionIdleTimeout test class

Analysis updated by marvin for workflow run 23779063047

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