Abelin101/mcp-http-streaming-bug-repro
If you are the rightful owner of mcp-http-streaming-bug-repro and would like to certify it and/or have it hosted online, please leave a comment on the right or send an email to dayong@mcphub.com.
This document provides a structured overview of a minimal HTTP MCP server designed to reproduce a ChatGPT UI streaming bug.
ChatGPT HTTP MCP Bug: Tools execute successfully but UI shows “Error in message stream” (Minimal Repro Included)
Summary
I’ve isolated a reproducible bug in ChatGPT’s HTTP MCP message streaming pipeline.
When ChatGPT connects to an HTTP MCP server via a publicly accessible URL (ngrok), it:
- Connects successfully
- Discovers the tools successfully (
ListToolsRequest) - Sends
CallToolRequestsuccessfully - My MCP server executes the tool successfully
- My MCP server returns a valid response
- BUT the ChatGPT UI fails to render the result and instead shows:
This issue persists even with a minimal 60-line MCP server, no external dependencies, and a single trivial echo tool.
Logs clearly show that the tool executes and returns normally, which confirms the issue is on the ChatGPT UI side.
This is not specific to my project — it reproduces 100% on a minimal FastAPI MCP server.
Environment
- ChatGPT Model: ChatGPT 5.1 (Developer Mode)
- MCP Type: HTTP MCP
- Tunnel: ngrok http 9098
- Client Browsers tested: Chrome, Edge, Opera — normal & incognito
- Server: FastAPI +
FastApiMCP(fromfastapi-mcp) - OS: Windows 11 + WSL2 (Ubuntu 24.04)
- Python: 3.10
- FastAPI: Latest
- MCP SDK: Latest
fastapi-mcprelease
Minimal MCP Server (single tool)
Here is the entire reproducible MCP server (slim_server.py).
This is the full server — nothing omitted.
Click to expand the minimal server code
# slim_server.py - minimal reproducible HTTP MCP server
import asyncio
import json
import logging
from typing import Any, Dict
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse, PlainTextResponse
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from fastapi_mcp import FastApiMCP # <-- the same import used in the SDK examples
# -------------------------------------------------------------------
# Logging
# -------------------------------------------------------------------
log = logging.getLogger("minimal_mcp")
log.setLevel(logging.INFO)
if not log.handlers:
sh = logging.StreamHandler()
sh.setFormatter(logging.Formatter("%(asctime)s [%(levelname)s] %(message)s"))
log.addHandler(sh)
logging.getLogger("mcp.server.lowlevel.server").setLevel(logging.INFO)
# -------------------------------------------------------------------
# FastAPI app
# -------------------------------------------------------------------
app = FastAPI(title="Minimal MCP Server", version="0.0.1")
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
)
# -------------------------------------------------------------------
# Health check
# -------------------------------------------------------------------
@app.get("/healthz")
async def healthz():
return PlainTextResponse("ok", status_code=200)
# -------------------------------------------------------------------
# Request logging middleware (verbose for MCP)
# -------------------------------------------------------------------
@app.middleware("http")
async def log_requests(request: Request, call_next):
body_bytes = await request.body()
path = request.url.path
headers = {k: v for k, v in request.headers.items()}
decoded = body_bytes.decode("utf-8", "ignore")
if path.startswith("/mcp"):
log.info(f"HTTP {request.method} {path} headers={headers} body={decoded!r}")
response = await call_next(request)
log.info(
f"HTTP {request.method} {path} -> {response.status_code} "
f"({response.headers.get('content-type')})"
)
return response
# -------------------------------------------------------------------
# Minimal tool: echo
# -------------------------------------------------------------------
class EchoRequest(BaseModel):
message: str
class EchoResponse(BaseModel):
message: str
@app.post(
"/echo",
operation_id="echo",
response_model=EchoResponse,
tags=["mcp", "test"],
)
async def echo_tool(body: EchoRequest) -> EchoResponse:
log.info(f"echo_tool called with message={body.message!r}")
return EchoResponse(message=f"echo: {body.message}")
# -------------------------------------------------------------------
# MCP Mount
# -------------------------------------------------------------------
mcp_http = FastApiMCP(
app,
name="Minimal MCP Repro",
description="A minimal HTTP MCP server for reproducing ChatGPT UI streaming bug.",
include_operations=["echo"],
)
mcp_http.mount() # mounts /mcp and /mcp/messages
log.info("Mounted HTTP MCP endpoint at /mcp")
# -------------------------------------------------------------------
# Run app
# -------------------------------------------------------------------
if __name__ == "__main__":
import uvicorn
uvicorn.run(
"ops.mcp.slim_server:app", # adjust to your module path
host="127.0.0.1",
port=9098,
reload=True,
log_level="info",
)
</details>
Reproduction Steps
Run the MCP server above.
Expose it via ngrok:
ngrok http 9098
Example URL:
https://adela-unlaudable-nondidactically.ngrok-free.dev
In ChatGPT → GPT Builder → Add Tool → MCP Server (HTTP):
https://<my-ngrok-url>.ngrok-free.dev/mcp
ChatGPT successfully connects and shows the echo tool.
Open a new chat with the custom GPT.
Ask:
Call the echo tool with message "hello"
✔ What happens in ngrok logs:
GET /mcp 200 OK
POST /mcp/messages/ 202 Accepted
POST /mcp/messages/ 202 Accepted
POST /mcp 405 Method Not Allowed # expected
✔ What happens in the FastAPI logs:
Processing request of type ListToolsRequest
Processing request of type CallToolRequest
echo_tool called with message='hello'
HTTP POST /echo -> 200 OK
✔ What happens in local curl:
$ curl -X POST http://127.0.0.1:9098/echo -d '{"message":"hello"}'
{"message":"echo: hello"}
❌ What happens in ChatGPT UI:
Error in message stream
The UI never displays the tool response, even though the backend definitely received it.
Expected vs Actual
✔ Expected
After calling the echo MCP tool, ChatGPT should show:
{ "message": "echo: hello" }
❌ Actual
ChatGPT logs show MCP handshake + tool execution succeeded
My MCP server returns correctly
The UI displays a red banner:
Error in message stream
No assistant response appears.
Why this is an OpenAI bug
Because:
The minimal server is correct
The MCP connector successfully registers the server + tool
The connector successfully sends call_tool
My server executes the tool without error
My server returns a valid MCP response
The TCP stream over ngrok is healthy
The UI fails after the backend receives the response
This strongly suggests a client-side UI streaming bug in ChatGPT when rendering responses for HTTP MCP.
Attachments / Evidence
✔ Full minimal server source
(above)
✔ MCP server logs
(included via log_requests)
✔ ngrok logs
(showing correct 200/202 activity)
✔ ChatGPT UI screenshots
(a reproducible failure every time)
Notes
This does not occur locally — only through ChatGPT UI.
Happens across multiple browsers and different machines.
Reproduces 100% with this minimal server.
If there’s any way to enable additional debug logging on the ChatGPT side or test a beta build, I’d be happy to help.
Thanks!