`list_mcp_tools` hangs when MCP endpoint returns plain-text error as `text/event-stream`

未关闭
#348 1 条评论 0 个 reaction 已指派 1 人 在 GitHub 查看

维护者通常 1 天内回复

@cassiofariasmachado 已经在做这个了。

开始于 2026年9月21日。

评估

这个 Issue 还没有评估数据。

描述

bug
Describe the Bug
Problem

When an AGW MCP endpoint returns 200 text/event-stream with a plain-text body (e.g. Jwt issuer is not configured) instead of a valid SSE data: event, await session.list_tools()
in list_server_tools() blocks indefinitely.

The MCP SSE parser discards lines without a data: prefix — silently, with no error. The ClientSession then waits forever for a JSON-RPC response that will never arrive. The httpx
timeout does not protect against this because the HTTP response itself completes successfully in ~1s.

Since all fragments run concurrently via asyncio.gather, one hanging fragment blocks the entire list_mcp_tools() call until the outer app-level timeout fires (up to 180s).

Proposed fixes

1. Per-fragment timeout (defensive minimum)

result = await asyncio.wait_for(session.list_tools(), timeout=30.0)

2. Race against stream closure (detects the problem immediately)
done, _ = await asyncio.wait(
    [session.list_tools(), stream_closed_event],
    return_when=asyncio.FIRST_COMPLETED
)
# if stream closed before list_tools() responded → raise immediately

3. Validate SSE body before entering ClientSession
first_chunk = await response.aread(64)
if not first_chunk.startswith((b"data:", b"event:")):
    raise McpError(f"Non-SSE response: {first_chunk.decode(errors='replace')}")

Context

- Reproduced on sap.btpn8n:apiResource:ManagedN8nMcpServer:v1, AGW EU12
- The upstream fix (return 4xx or a proper SSE error event) is tracked separately
- This SDK fix is defensive and needed regardless of the upstream fix

### Steps to Reproduce

### Reproduction

Send `initialize` followed by `tools/list` to any MCP endpoint that returns a plain-text error:

```bash
# initialize — works, returns valid SSE
curl -X POST "<mcp-endpoint>" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}'

# tools/list — returns 200 but body is plain text, not an SSE data: event
curl -X POST "<mcp-endpoint>" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'

Actual tools/list response:
HTTP/2 200
content-type: text/event-stream

<plain text error message>

Expected tools/list response:
HTTP/2 200
content-type: text/event-stream

event: message
data: {"jsonrpc":"2.0","id":1,"result":{"tools":[...]}}

The body lacks the data: prefix required by the SSE spec. The MCP SSE parser silently discards it, leaving ClientSession blocked on a queue that will never receive a message.
Expected Behavior

Exception must be thrown, but it should not wait indefinitely for a response.

Screenshots

No response

Used Versions
  • Python version via python --version: ...
  • SAP Cloud SDK for Python version: ...
  • Framework version (if applicable, e.g., Flask, FastAPI): ...
Installed packages via pip list or uv pip list
Package list here
Code Examples
# Your code here
Stack Trace

No response

Log File
Log file ...
Affected Development Phase

Getting Started

Impact

No Impact

Timeline

No response

主要语言
Python
星标
29
派生
45
平均合并
2 天 4 小时
30 天内合并 PR
51

环境准备

从这里开始

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

SAP/cloud-sdk-python 的其他 Issue

查看 SAP/cloud-sdk-python 的全部 Issue

相似的 Issue

更多 Python Issue

把新 issue 发到你的邮箱

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