client's `read_stream_writer` open after SSE disconnection hanging `.receive()`
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 3/5
- Tempo stimato
- 1-2 giorni
- Idoneità per principianti
- 42/100
- Tipo di issue
- Bug
- Chiarezza
- Abbastanza chiara
- Stato di attività
- Ferma
- Stack tecnologico
- python
- Ambito
- api, networking
Direzione di ricerca
Inizia in src/mcp/client/streamable_http.py intorno alle righe 433-435 e riproduci la chiamata bloccata con l'esempio Python usando StreamableHttpTransport. Traccia come il timeout di lettura SSE chiude la connessione e come la sessione consuma i messaggi di read_stream_writer. Il lavoro è completato quando la chiamata del client restituisce un risultato o solleva un'eccezione dopo il timeout, invece di rimanere bloccata indefinitamente.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
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
This issue MAY be related to the following
- https://github.com/modelcontextprotocol/python-sdk/issues/1805
- https://github.com/modelcontextprotocol/python-sdk/issues/1764
- https://github.com/modelcontextprotocol/python-sdk/issues/262
Experiencing deadlocks on streamable_http transport. In order to reproduce the issue the following can be run.
import asyncio
import logging
import threading
import time
from fastmcp import Client, FastMCP
from fastmcp.client.transports import StreamableHttpTransport
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
handlers=[logging.StreamHandler()],
)
logger = logging.getLogger(__name__)
HOST = "127.0.0.1"
PORT = 8765
SERVER_URL = f"http://{HOST}:{PORT}/mcp"
mcp = FastMCP(name="timeout issue")
SSE_TIMEOUT = 0.1
SLEEP = 60
@mcp.tool
def blocking_call() -> str:
time.sleep(SLEEP)
return "42"
def run_server():
mcp.run(transport="streamable-http", host=HOST, port=PORT)
async def test_blocking_sse():
transport = StreamableHttpTransport(SERVER_URL, sse_read_timeout=SSE_TIMEOUT)
async with Client(transport) as client:
tools = await client.list_tools()
print(f"available tools: {[t.name for t in tools]}")
result = await client.call_tool("blocking_call", {})
print(f"blocking result: {result}")
if __name__ == "__main__":
server_thread = threading.Thread(target=run_server, daemon=True)
server_thread.start()
time.sleep(2)
try:
asyncio.run(test_blocking_sse())
finally:
logger.info(f"{time.strftime('%Y-%m-%d %H:%M:%S')} [CLIENT] Shutting down...")
Execution context := locally built fastmcp at this commit 790ea92
Observed Behavior: the client hangs forever after the SSE read timeout fires, looking at the logs:
- tool request is sent, server returns HTTP 200 with [Content-Type: text/event-stream]
- server starts executing the tool (blocking time.sleep(60))
- client's SSE read timeout fires (after ~5 seconds with default
httpxtimeout) - client closes the HTTP connection
- client hangs indefinitely - call_tool() never returns
- the tool eventually completes on the server side, but when the server tries to send the response back, it gets
BrokenResourceErrorbecause the HTTP connection was already closed by the client.
I know noting about nothing, so I can imagine the above example is just an issue on my side, maybe:
ssetimeout should always be greater or equal to the expected timeout of tool calls (now thatsse_read_timeoutis deprecated it'shttpx.Timeoutcounterpart should be >= tool timeout)- long running tool calls MUST send progress updates
but one thing is for sure and that is, the above configuration hangs indefenitely because the session layer never gets to know that the transport layer is dead after the SSE stream is closed
Within my ignorance of many aspects of the implementation, the missing else branch seems to be the root cause.
and the fix looks something like
if last_event_id is not None: # pragma: no branch
...
else:
error_response = JSONRPCError(...)
await ctx.read_stream_writer.send(SessionMessage(JSONRPCMessage(root=error_response)))
consequently raising McpError
Example Code
Python & MCP Python SDK
Execution context for ease of implementation
- `mcp==1.24.0`
- example codebase run against locally built `fastmcp` at [790ea92](https://github.com/jlowin/fastmcp/tree/790ea92eb59256da68c83097321ebde8f8819bcf) --> 1.24.0
- Python: `python-3.12.7-macos-aarch64-none/bin/python3.12`
- Lingua principale
- Python
- Stelle
- 24.3k
- Fork
- 4k
- Merge medio
- 1g 1h
- PR unite (30g)
- 31
Guida per i contributori
Apri la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di modelcontextprotocol/python-sdk
-
v1 v2
Difficoltà 2/5 1-3 ore Idoneità per principianti 85/100
modelcontextprotocol/python-sdk#3546 · 4 commenti ·
-
v1 v2
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
modelcontextprotocol/python-sdk#3545 · 1 commento ·
-
v1 v2
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 91/100
modelcontextprotocol/python-sdk#3508 · 2 commenti ·
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 64/100
modelcontextprotocol/python-sdk#3504 ·
-
v1 v2
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
modelcontextprotocol/python-sdk#3492 · 1 commento ·
Tutte le issue di modelcontextprotocol/python-sdk
Issue simili
-
🐛 Bug 🔔 Pending processing
Difficoltà 2/5 1-3 ore Idoneità per principianti 84/100
jumpserver/jumpserver#17584 ·
-
link-check link-check:sphinx-theme
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 65/100
qgis/QGIS-Documentation#11275 ·
-
bug priority:normal ready-for-dev
Difficoltà 2/5 1-3 ore Idoneità per principianti 88/100
OpenHands/extensions#626 · 1 commento ·
-
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 90/100
CSCfi/sd-search-api#39 ·