modelcontextprotocol / modelcontextprotocol/python-sdk
ClientDisconnect returns HTTP 500
まだ誰も着手していません。
- 主要言語
- Python
- スター
- 24.3k
- フォーク
- 4k
- 平均マージ
- 1日 1時間
- マージ済み PR(30日)
- 31
説明
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
コントリビューションガイド
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
調査の方向性
src/mcp/server/streamable_http.py の StreamableHTTPServerTransport._handle_post_request から始め、490–500 行付近にある広範な例外ハンドラーを確認し、ClientDisconnect がどのようにそこへ到達するかを調べてください。提供されている部分的な body の socket 再現を実行し、現在のレスポンスとログを確認してください。通常のクライアント切断によって HTTP 500、ERROR レベルの traceback、または本番環境での 5XX アラートが発生しなくなれば完了です。
索引モデルが issue の本文から書いたものです。
評価
- 技術スタック
- python
- 領域
- api, backend
- issue の種類
- バグ
- 難易度
- 2/5
- 見積もり時間
- 1〜3時間
- 活発さ
- 活発
- 明瞭さ
- 明確に書かれている
- 初心者へのやさしさ
- 76/100