Expire idle Streamable HTTP sessions by default and cap concurrent sessions (#3395)
This commit is contained in:
parent
6705402e24
commit
d2290ca343
2 changed files with 915 additions and 234 deletions
|
|
@ -13,7 +13,7 @@ from starlette.routing import Mount, Route
|
|||
|
||||
from docs_src.asgi import tutorial001, tutorial002, tutorial003, tutorial004, tutorial005, tutorial006
|
||||
from mcp import Client
|
||||
from mcp.server import MCPServer
|
||||
from mcp.server import MCPServer, Server
|
||||
|
||||
# See test_index.py for why this is a per-module mark and not a conftest hook.
|
||||
pytestmark = [pytest.mark.anyio, pytest.mark.filterwarnings("error::mcp.MCPDeprecationWarning")]
|
||||
|
|
@ -44,6 +44,8 @@ async def test_streamable_http_app_takes_runs_options_except_port() -> None:
|
|||
"event_store",
|
||||
"retry_interval",
|
||||
"max_request_body_size",
|
||||
"session_idle_timeout",
|
||||
"max_sessions",
|
||||
"transport_security",
|
||||
"host",
|
||||
}
|
||||
|
|
@ -68,6 +70,18 @@ async def test_streamable_http_app_applies_the_configured_request_body_limit() -
|
|||
assert response.status_code == 413
|
||||
|
||||
|
||||
async def test_streamable_http_app_applies_the_configured_session_limits() -> None:
|
||||
"""The documented `session_idle_timeout` and `max_sessions` options reach the session manager, from
|
||||
both the high-level and the low-level factory."""
|
||||
server = MCPServer("Notes")
|
||||
server.streamable_http_app(session_idle_timeout=5, max_sessions=7)
|
||||
assert (server.session_manager.session_idle_timeout, server.session_manager.max_sessions) == (5, 7)
|
||||
|
||||
lowlevel = Server("Notes")
|
||||
lowlevel.streamable_http_app(session_idle_timeout=None, max_sessions=None)
|
||||
assert (lowlevel.session_manager.session_idle_timeout, lowlevel.session_manager.max_sessions) == (None, None)
|
||||
|
||||
|
||||
async def test_mounting_at_the_root_keeps_the_default_path() -> None:
|
||||
"""tutorial002: `Mount("/")` plus the default `streamable_http_path` leaves the endpoint at `/mcp`."""
|
||||
(mount,) = tutorial002.app.routes
|
||||
|
|
|
|||
|
|
@ -53,6 +53,8 @@ def test_streamable_http_app_has_no_era_knob() -> None:
|
|||
"event_store",
|
||||
"retry_interval",
|
||||
"max_request_body_size",
|
||||
"session_idle_timeout",
|
||||
"max_sessions",
|
||||
"transport_security",
|
||||
"host",
|
||||
}
|
||||
|
|
@ -76,6 +78,20 @@ async def test_a_legacy_session_is_minted_in_process_and_a_stray_session_id_is_a
|
|||
assert stray.status_code == 404
|
||||
|
||||
|
||||
def test_legacy_sessions_expire_and_are_capped_by_default() -> None:
|
||||
"""The session lifetime section: a session is closed after 30 idle minutes and each worker process
|
||||
holds at most 10 000 of them, unless `run()` / `streamable_http_app()` say otherwise."""
|
||||
server = MCPServer("Bookshop")
|
||||
server.streamable_http_app()
|
||||
assert server.session_manager.session_idle_timeout == 30 * 60
|
||||
assert server.session_manager.max_sessions == 10_000
|
||||
|
||||
server = MCPServer("Bookshop")
|
||||
server.streamable_http_app(session_idle_timeout=None, max_sessions=None)
|
||||
assert server.session_manager.session_idle_timeout is None
|
||||
assert server.session_manager.max_sessions is None
|
||||
|
||||
|
||||
async def test_stateless_http_never_mints_a_session() -> None:
|
||||
"""The `stateless_http=True` section: the same legacy `initialize` no longer gets an `Mcp-Session-Id`."""
|
||||
app = MCPServer("Bookshop").streamable_http_app(stateless_http=True)
|
||||
|
|
|
|||
Loading…
Reference in a new issue