Skip to content

Commit 1326431

Browse files
committed
Enforce session management per MCP spec (404 for unknown sessions, 400 for missing session)
1 parent 01a0157 commit 1326431

2 files changed

Lines changed: 18 additions & 42 deletions

File tree

dash/mcp/_server.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -116,23 +116,26 @@ def _handle_post() -> Response:
116116
request_id = data.get("id")
117117
session_id = request.headers.get("mcp-session-id")
118118

119-
stale_session = False
120119
if method == "initialize":
121120
session_id = _create_session()
122121
elif session_id and session_id not in sessions:
123-
stale_session = True
124-
sessions[session_id] = {}
122+
return Response(
123+
json.dumps({"error": "Session not found. Please reinitialize."}),
124+
content_type="application/json",
125+
status=404,
126+
)
125127
elif not session_id:
126-
session_id = _create_session()
128+
return Response(
129+
json.dumps({"error": "Missing session ID. Send an initialize request first."}),
130+
content_type="application/json",
131+
status=400,
132+
)
127133

128134
response_data = _process_mcp_message(data)
129135

130136
if response_data is None:
131137
return Response("", status=202)
132138

133-
if stale_session:
134-
_inject_warning(response_data, _STALE_SESSION_WARNING)
135-
136139
return Response(
137140
json.dumps(response_data),
138141
content_type="application/json",

tests/integration/mcp/test_server.py

Lines changed: 8 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ def test_post_initialize_creates_session(self):
6060
data = json.loads(r.data)
6161
assert data["result"]["protocolVersion"] == LATEST_PROTOCOL_VERSION
6262

63-
def test_post_without_session_auto_assigns(self):
63+
def test_post_without_session_returns_400(self):
6464
app = _make_app()
6565
client = app.server.test_client()
6666
r = client.post(
@@ -70,31 +70,25 @@ def test_post_without_session_auto_assigns(self):
7070
),
7171
content_type="application/json",
7272
)
73-
assert r.status_code == 200
74-
assert "mcp-session-id" in r.headers
75-
data = json.loads(r.data)
76-
assert "tools" in data["result"]
73+
assert r.status_code == 400
7774

78-
def test_stale_session_error_includes_hint(self):
75+
def test_stale_session_returns_404(self):
7976
app = _make_app()
8077
client = app.server.test_client()
8178
r = client.post(
8279
f"/{MCP_PATH}",
8380
data=json.dumps(
8481
{
8582
"jsonrpc": "2.0",
86-
"method": "tools/call",
83+
"method": "tools/list",
8784
"id": 1,
88-
"params": {"name": "no_such_tool", "arguments": {}},
85+
"params": {},
8986
}
9087
),
9188
content_type="application/json",
9289
headers={"mcp-session-id": "old-session-from-before-restart"},
9390
)
94-
assert r.status_code == 200
95-
data = json.loads(r.data)
96-
assert "session was not recognised" in data["error"]["message"]
97-
assert "tools/list" in data["error"]["message"]
91+
assert r.status_code == 404
9892

9993
def test_post_with_valid_session(self):
10094
app = _make_app()
@@ -161,7 +155,7 @@ def test_delete_terminates_session(self):
161155
headers={"mcp-session-id": session_id},
162156
)
163157
assert r2.status_code == 204
164-
# Post-delete requests still succeed
158+
# Post-delete requests return 404
165159
r3 = client.post(
166160
f"/{MCP_PATH}",
167161
data=json.dumps(
@@ -170,7 +164,7 @@ def test_delete_terminates_session(self):
170164
content_type="application/json",
171165
headers={"mcp-session-id": session_id},
172166
)
173-
assert r3.status_code == 200
167+
assert r3.status_code == 404
174168

175169
def test_delete_nonexistent_session_returns_404(self):
176170
app = _make_app()
@@ -196,27 +190,6 @@ def test_get_with_stale_session_returns_404(self):
196190
)
197191
assert r.status_code == 404
198192

199-
def test_get_returns_sse_stream(self):
200-
app = _make_app()
201-
client = app.server.test_client()
202-
# First create a session via POST initialize
203-
init = client.post(
204-
f"/{MCP_PATH}",
205-
data=json.dumps(
206-
{"jsonrpc": "2.0", "method": "initialize", "id": 1, "params": {}}
207-
),
208-
content_type="application/json",
209-
)
210-
session_id = init.headers["mcp-session-id"]
211-
# GET with valid session returns SSE stream
212-
r = client.get(
213-
f"/{MCP_PATH}",
214-
headers={"mcp-session-id": session_id},
215-
)
216-
assert r.status_code == 200
217-
assert r.content_type == "text/event-stream"
218-
assert r.headers.get("Cache-Control") == "no-cache"
219-
220193
def test_post_rejects_wrong_content_type(self):
221194
app = _make_app()
222195
client = app.server.test_client()

0 commit comments

Comments
 (0)