Skip to content

Commit b0d683a

Browse files
fix(mcp): isolate hosted request contexts
1 parent a8d11c7 commit b0d683a

2 files changed

Lines changed: 17 additions & 0 deletions

File tree

‎engraphis/dashboard_app.py‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,16 +382,24 @@ def create_app() -> FastAPI:
382382
pass
383383
_prev_path = _mcp_mod.mcp.settings.streamable_http_path
384384
_prev_security = _mcp_mod.mcp.settings.transport_security
385+
_prev_stateless = getattr(_mcp_mod.mcp.settings, "stateless_http", False)
385386
try:
386387
_mcp_mod.mcp.settings.streamable_http_path = "/"
387388
_mcp_mod.mcp.settings.transport_security = _mcp_transport_security(_mcp_mod.mcp)
389+
# Hosted callers can bind a tenant service and principal around each HTTP
390+
# operation. Stateful Streamable HTTP runs tool callbacks in the persistent
391+
# initialization task, which would retain the first request's ContextVars
392+
# for later requests. Stateless mode keeps every callback in its request
393+
# context and is therefore required for request-scoped service isolation.
394+
_mcp_mod.mcp.settings.stateless_http = True
388395
_mcp_asgi = _mcp_mod.mcp.streamable_http_app()
389396
finally:
390397
# streamable_http_app() captures these settings in its session manager. Restore
391398
# the global FastMCP instance so importing the dashboard cannot alter the
392399
# standalone MCP server in the same process.
393400
_mcp_mod.mcp.settings.streamable_http_path = _prev_path
394401
_mcp_mod.mcp.settings.transport_security = _prev_security
402+
_mcp_mod.mcp.settings.stateless_http = _prev_stateless
395403
_mcp_mgr = _mcp_mod.mcp.session_manager
396404
except (Exception, SystemExit) as _exc: # noqa: BLE001 - MCP mount stays optional
397405
import logging as _logging

‎tests/test_dashboard_v2.py‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -876,6 +876,15 @@ def test_dashboard_and_mcp_recall_share_the_v2_service(monkeypatch, tmp_path):
876876
assert dashboard["score_semantics"] == mcp["score_semantics"]
877877

878878

879+
def test_dashboard_mcp_mount_is_stateless_for_request_scoped_contexts(monkeypatch, tmp_path):
880+
pytest.importorskip("mcp", reason="MCP extra not installed")
881+
from engraphis import mcp_server
882+
883+
with _client(monkeypatch, tmp_path) as client:
884+
assert client.app.state.mcp_over_http is True
885+
assert mcp_server.mcp.session_manager.stateless is True
886+
887+
879888
def test_dashboard_keyword_fallback_reports_truthful_lexical_scores(monkeypatch, tmp_path):
880889
with _client(monkeypatch, tmp_path) as client:
881890
def mismatched_embedder(*_args, **_kwargs):

0 commit comments

Comments
 (0)