Hacktoberfest 2026 : les issues que les mainteneurs ont marquées pour octobre, ouvertes et accessibles aux débutants. Parcourir les issues Hacktoberfest

Streamable HTTP client hangs on a null-id JSON-RPC error in a 200 JSON response

Ouverte Adaptée aux débutants
#3,639 1 commentaire 0 réactions 0 personnes assignées Voir sur GitHub

Les mainteneurs répondent en général sous 1 jour

Personne n'a encore pris cette issue.

Évaluation

Difficulté
2/5
Temps estimé
1-3 heures
Accessibilité débutants
75/100
Type d'issue
Bug
Clarté
Clairement spécifiée
Activité
Active
Stack technique
python
Domaine
api

Piste de recherche

Commencez dans src/mcp/client/streamable_http.py, au niveau de _handle_json_response, puis comparez son traitement avec _handle_sse_event et _handle_post_request. Ajoutez le test de régression à côté du test existant pour null-id non-2xx dans tests/client/test_notification_response.py ; le travail est terminé lorsqu’une erreur JSON-RPC 200 avec id null lève MCPError au lieu de laisser la requête en attente.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Description

bug v1 v2
Initial Checks
Release line

2.x (current stable)

Description

On current main (2118f14f), when a server answers a request POST with a 200 application/json body holding a JSON-RPC error with "id": null, the streamable HTTP client never resolves the call. session.list_tools() (or any request) waits forever, or until a read timeout if one is set.

JSON-RPC 2.0 allows id: null on an error when the server could not determine the request id. Gateways and non-SDK servers can send one with status 200.

_handle_json_response in src/mcp/client/streamable_http.py forwards the parsed message unchanged. The dispatcher then drops it as a response to an unknown id (jsonrpc_dispatcher.py, "unknown/late request id").

The other two response paths in the same transport already handle this:

  • SSE (_handle_sse_event): replaces a response's id with the original request id.
  • Non-2xx JSON body (_handle_post_request): rebuilds the error under the request's id. The comment there says: "The server may have set id: null (request rejected before its id was parsed); use this request's id so correlation works."

So the same error body resolves the call over SSE or with a 4xx status, but hangs with a 200.

Expected: the call raises MCPError with the server's error, the same as the SSE and non-2xx paths.

Proposed fix: in _handle_json_response, when the parsed message is a JSONRPCError, set its id to the POST's request id. A 2xx JSON body answers exactly that POST, so nothing else can be waiting on it. That is a 4-line change plus a regression test next to the existing non-2xx null-id test in tests/client/test_notification_response.py.

I'd like to fix this and have a branch ready: https://github.com/hyeonsang010716/python-sdk/tree/fix/streamable-http-json-null-id

I found and prepared this with AI assistance. I've reviewed the change, checked the repro below fails on main and passes with the fix, and can explain it.

Example Code
import json

import anyio
import httpx2
from starlette.applications import Starlette
from starlette.requests import Request
from starlette.responses import JSONResponse, Response
from starlette.routing import Route

from mcp import ClientSession, MCPError
from mcp.client.streamable_http import streamable_http_client


async def handle_mcp(request: Request) -> Response:
    data = json.loads(await request.body())
    if data.get("method") == "initialize":
        result = {"protocolVersion": "2025-06-18", "capabilities": {}, "serverInfo": {"name": "s", "version": "1"}}
        return JSONResponse({"jsonrpc": "2.0", "id": data["id"], "result": result})
    if "id" not in data:
        return Response(status_code=202)
    # e.g. a gateway that fails before it has parsed the request id
    return JSONResponse({"jsonrpc": "2.0", "id": None, "error": {"code": -32603, "message": "upstream failed"}})


async def main() -> None:
    app = Starlette(routes=[Route("/mcp", handle_mcp, methods=["POST"])])
    async with httpx2.AsyncClient(transport=httpx2.ASGITransport(app=app)) as http:
        async with streamable_http_client("http://localhost/mcp", http_client=http) as (read, write):
            async with ClientSession(read, write) as session:
                await session.initialize()
                try:
                    with anyio.fail_after(3):
                        await session.list_tools()
                except MCPError as exc:
                    print("raised MCPError:", exc.error.message)
                except TimeoutError:
                    print("list_tools() never returned")


anyio.run(main)

Output on main:

list_tools() never returned

Output with the fix:

raised MCPError: upstream failed
Python & MCP Python SDK
Python 3.14.7
mcp main @ 2118f14f (also present in v2.3.0)
Langage dominant
Python
Étoiles
24.5k
Forks
4k
Merge moyen
1 j 14 h
PR mergées (30 j)
34

Préparer son environnement

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Autres issues de modelcontextprotocol/python-sdk

Toutes les issues de modelcontextprotocol/python-sdk

Issues similaires

Plus d'issues Python

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.