modelcontextprotocol / modelcontextprotocol/python-sdk

ClientDisconnect returns HTTP 500

未关闭 适合新手
#1,648 6 条评论 1 个 reaction 已指派 0 人 在 GitHub 查看

还没有人认领这个 Issue。

bug P2 ready for work
主要语言
Python
星标
24.3k
派生
4k
平均合并
1 天 1 小时
30 天内合并 PR
31

描述

Initial Checks
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

贡献指南

打开贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 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

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。