modelcontextprotocol / modelcontextprotocol/python-sdk
ClientDisconnect returns HTTP 500
Dieses Issue hat noch niemand übernommen.
- Vorherrschende Sprache
- Python
- Sterne
- 24.3k
- Forks
- 4k
- Ø Merge
- 1 T. 1 Std.
- Gemergte PRs (30 T.)
- 31
Beschreibung
Initial Checks
- I confirm that I'm using the latest version of MCP Python SDK
- I confirm that I searched for my issue in https://github.com/modelcontextprotocol/python-sdk/issues before opening this issue
Description
StreamableHTTPServerTransport._handle_post_request in mcp/server/streamable_http.py incorrectly handles starlette.requests.ClientDisconnect exceptions.
Current behavior:
- Returns HTTP 500 (Internal Server Error)
- Logs as ERROR with full traceback
- Triggers production 5XX alerts
When This Occurs
ClientDisconnect happens during normal operations:
- Network timeouts
- User cancels request
- Load balancer timeouts
- Mobile client network interruptions
These are client-side events, not server failures.
Root Cause
File: src/mcp/server/streamable_http.py
Line: ~490-500
The broad except Exception handler catches ClientDisconnect and returns 500:
except Exception as err: # pragma: no cover
logger.exception("Error handling POST request") # ❌ Logs as ERROR
response = self._create_error_response(
f"Error handling POST request: {err}",
HTTPStatus.INTERNAL_SERVER_ERROR, # ❌ Returns 500
INTERNAL_ERROR,
)
await response(scope, receive, send)
Example Code
Reproduction
Steps
1. Install MCP SDK:
python3 -m venv venv
source venv/bin/activate
pip install mcp
2. Create minimal_mcp_server.py based on the documentation:
#!/usr/bin/env python3
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("Bug Demo", json_response=True)
@mcp.tool()
def add(a: int, b: int) -> int:
"""Add two numbers"""
return a + b
if __name__ == "__main__":
mcp.run(transport="streamable-http")
3. Create test_client_disconnect.py:
#!/usr/bin/env python3
import socket
import time
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(("localhost", 8000))
# Send headers claiming 100KB body
headers = (
b"POST /mcp HTTP/1.1\r\n"
b"Host: localhost\r\n"
b"Content-Type: application/json\r\n"
b"Content-Length: 100000\r\n"
b"Accept: application/json, text/event-stream\r\n"
b"\r\n"
)
sock.send(headers)
# Send partial body then disconnect
sock.send(b'{"jsonrpc": "2.0", "method": "initialize", "params": {')
time.sleep(0.05)
sock.close()
print("✓ Client disconnect simulated")
4. Run:
Terminal 1
python minimal_mcp_server.py
Terminal 2
python test_client_disconnect.py
5. Observe the bug in Terminal 1:
Error handling POST request
Traceback (most recent call last):
File ".../mcp/server/streamable_http.py", line 351, in _handle_post_request
body = await request.body()
^^^^^^^^^^^^^^^^^^^^
File ".../starlette/requests.py", line 243, in body
async for chunk in self.stream():
File ".../starlette/requests.py", line 237, in stream
raise ClientDisconnect()
starlette.requests.ClientDisconnect
Python & MCP Python SDK
Python 3.12.9, MCP Python SDK v1.21.2
Beitragsleitfaden
Erste Schritte
- Lies das ganze Issue und danach den Beitragsleitfaden des Projekts.
- Schreib ins Issue, dass du es übernimmst — das erspart doppelte Arbeit.
- Forke das Repository und arbeite in einem Branch.
- Öffne einen Pull Request, der die Issue-Nummer nennt.
Rechercherichtung
Beginne in src/mcp/server/streamable_http.py bei StreamableHTTPServerTransport._handle_post_request, ungefähr beim weit gefassten Exception-Handler in den Zeilen 490–500, und überprüfe, wie ClientDisconnect dorthin gelangt. Führe die bereitgestellte Socket-Reproduktion mit unvollständigem Body aus, um die aktuelle Antwort und Protokollierung zu beobachten. Als erledigt gilt die Aufgabe, wenn normale Client-Trennungen nicht mehr zu einem HTTP 500, einem Traceback auf ERROR-Level oder einem 5XX-Alarm in der Produktion führen.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Bewertung
- Tech-Stack
- python
- Bereich
- api, backend
- Issue-Typ
- Bug
- Schwierigkeit
- 2/5
- Geschätzter Aufwand
- 1-3 Stunden
- Aktivitätsstatus
- Aktiv
- Klarheit
- Klar beschrieben
- Anfängerfreundlichkeit
- 76/100