Title: Bug: stdio transport fails to handle Content-Length header format from MCP clients · Issue #2546 · modelcontextprotocol/python-sdk · GitHub
Open Graph Title: Bug: stdio transport fails to handle Content-Length header format from MCP clients · Issue #2546 · modelcontextprotocol/python-sdk
X Title: Bug: stdio transport fails to handle Content-Length header format from MCP clients · Issue #2546 · modelcontextprotocol/python-sdk
Description: Bug: stdio transport fails to handle Content-Length header format from MCP clients Description The stdio_server transport in mcp/server/stdio.py reads stdin line-by-line and attempts to parse each line as JSON. This works for NDJSON (new...
Open Graph Description: Bug: stdio transport fails to handle Content-Length header format from MCP clients Description The stdio_server transport in mcp/server/stdio.py reads stdin line-by-line and attempts to parse each ...
X Description: Bug: stdio transport fails to handle Content-Length header format from MCP clients Description The stdio_server transport in mcp/server/stdio.py reads stdin line-by-line and attempts to parse each ...
Opengraph URL: https://github.com/modelcontextprotocol/python-sdk/issues/2546
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"Bug: stdio transport fails to handle Content-Length header format from MCP clients","articleBody":"# Bug: stdio transport fails to handle Content-Length header format from MCP clients\n\n## Description\n\nThe `stdio_server` transport in `mcp/server/stdio.py` reads stdin line-by-line and attempts to parse each line as JSON. This works for NDJSON (newline-delimited JSON) format but **fails completely** when MCP clients send messages using the `Content-Length` header format.\n\nSeveral popular MCP clients (including those built on the TypeScript SDK `@modelcontextprotocol/sdk`) send stdio messages with `Content-Length` headers, following the pattern established by LSP (Language Server Protocol). This causes a compatibility gap where Python SDK-based servers cannot be initialized by these clients.\n\n## Steps to Reproduce\n\n1. Create a minimal MCP server using the Python SDK:\n```python\nfrom mcp.server.fastmcp import FastMCP\nmcp = FastMCP(\"test-server\")\nmcp.run(transport=\"stdio\")\n```\n\n2. Send an MCP `initialize` request using Content-Length header format (as used by TypeScript SDK-based clients):\n```\nContent-Length: 157\\r\\n\\r\\n{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"initialize\",\"params\":{\"protocolVersion\":\"2024-11-05\",\"capabilities\":{},\"clientInfo\":{\"name\":\"test\",\"version\":\"1.0\"}}}\n```\n\n3. Observe the server response.\n\n## Expected Behavior\n\nThe server should either:\n- (a) Properly parse the `Content-Length` header and read the specified number of bytes as the message body, **OR**\n- (b) Gracefully skip non-JSON lines (like `Content-Length` headers) and process valid JSON messages\n\n## Actual Behavior\n\nThe server reads the input line-by-line via `async for line in stdin:` and tries to parse each line as JSON:\n\n```python\n# mcp/server/stdio.py, line 60-68\nasync def stdin_reader():\n async with read_stream_writer:\n async for line in stdin:\n try:\n message = types.JSONRPCMessage.model_validate_json(line)\n except Exception as exc:\n await read_stream_writer.send(exc)\n continue\n```\n\nWhen receiving `Content-Length` header format:\n\n1. Line `Content-Length: 157` → JSON parse error → exception sent to stream → **error notification emitted**\n2. Empty line `` → JSON parse error → exception sent to stream → **error notification emitted**\n3. JSON body without trailing newline → **never read** (stdin reader blocks waiting for `\\n`)\n\nThe server outputs two error notifications:\n```json\n{\"method\":\"notifications/message\",\"params\":{\"level\":\"error\",\"logger\":\"mcp.server.exception_handler\",\"data\":\"Internal Server Error\"},\"jsonrpc\":\"2.0\"}\n{\"method\":\"notifications/message\",\"params\":{\"level\":\"error\",\"logger\":\"mcp.server.exception_handler\",\"data\":\"Internal Server Error\"},\"jsonrpc\":\"2.0\"}\n```\n\nAnd the actual `initialize` response is **never sent**, causing the client to timeout with:\n```\nfailed to initialize MCP client: transport error: context deadline exceeded\n```\n\n## Verification\n\nI verified this with a Node.js test script that spawns the Python MCP server and sends messages in both formats:\n\n| Format | Result |\n|--------|--------|\n| NDJSON (raw JSON + `\\n`) | ? Success - correct `initialize` response |\n| Content-Length header + body (no trailing `\\n`) | ? Failed - 2 error notifications, no response (body never read) |\n| Content-Length header + body + `\\n` | ?? Partial - 2 error notifications + correct response (error notifications may confuse client) |\n\n## Environment\n\n- **mcp package version**: 1.27.0 (latest)\n- **Python version**: 3.14\n- **OS**: Windows 11\n- **MCP client**: Qoder IDE (uses TypeScript SDK)\n\n## Suggested Fix\n\nThe `stdin_reader` in `mcp/server/stdio.py` should be updated to handle both formats:\n\n**Option A: Skip non-JSON lines silently**\nInstead of sending parse exceptions to the stream, silently skip lines that don't parse as valid JSON-RPC messages. This would allow Content-Length header lines and empty lines to be ignored while processing the actual JSON body.\n\n```python\nasync def stdin_reader():\n async with read_stream_writer:\n async for line in stdin:\n stripped = line.strip()\n if not stripped:\n continue # Skip empty lines\n try:\n message = types.JSONRPCMessage.model_validate_json(stripped)\n except Exception:\n continue # Skip non-JSON lines (e.g., Content-Length headers)\n session_message = SessionMessage(message)\n await read_stream_writer.send(session_message)\n```\n\n**Option B: Full Content-Length header support**\nImplement proper Content-Length header parsing (read header, extract length, read body of that length), falling back to NDJSON for backward compatibility.\n\n## Additional Context\n\n- The MCP specification (2025-03-26) states stdio messages are \"delimited by newlines\" (NDJSON format).\n- However, the TypeScript SDK (`@modelcontextprotocol/sdk`) uses `Content-Length` headers for stdio transport, creating a real-world compatibility gap.\n- Other projects (e.g., [Switchboard](https://github.com/George55562/Switchboard)) have documented this issue and implemented dual-format support: \"Looks ahead to detect Content-Length headers, Falls back to line-delimited parsing if no header found, Skips non-JSON lines.\"\n- This affects any Python SDK-based MCP server when used with clients that send Content-Length headers (Qoder, and potentially others built on the TypeScript SDK).\n","author":{"url":"https://github.com/weishigao","@type":"Person","name":"weishigao"},"datePublished":"2026-05-06T11:11:34.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":1},"url":"https://github.com/2546/python-sdk/issues/2546"}
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:83b2152d-c4e7-3d70-6d89-c8420e4d026f |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | EA24:1D330B:C132DA:1019960:6A5B6953 |
| html-safe-nonce | d1b7f7cf2e5aa7122956c168b78a9dab1ba84e702f708d44968ac1ca8c4700b9 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJFQTI0OjFEMzMwQjpDMTMyREE6MTAxOTk2MDo2QTVCNjk1MyIsInZpc2l0b3JfaWQiOiI4Mjk3NjE2ODg2MjM1NjIxNzE1IiwicmVnaW9uX2VkZ2UiOiJpYWQiLCJyZWdpb25fcmVuZGVyIjoiaWFkIn0= |
| visitor-hmac | 83f419a475af6f255cdf9b1981bde7f8bdbae430bd01086576dffab6e0310fe3 |
| hovercard-subject-tag | issue:4390874233 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/modelcontextprotocol/python-sdk/2546/issue_layout |
| twitter:image | https://opengraph.githubassets.com/b3163e0c46c7502a5581ec8bb2d24c3738494536d12b7f05ec8f2c57dc7ec983/modelcontextprotocol/python-sdk/issues/2546 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/b3163e0c46c7502a5581ec8bb2d24c3738494536d12b7f05ec8f2c57dc7ec983/modelcontextprotocol/python-sdk/issues/2546 |
| og:image:alt | Bug: stdio transport fails to handle Content-Length header format from MCP clients Description The stdio_server transport in mcp/server/stdio.py reads stdin line-by-line and attempts to parse each ... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | weishigao |
| hostname | github.com |
| expected-hostname | github.com |
| None | 5290d7e14309ad1e76106a9c4237bd1041517e83ea182c8ab756752cb0c6940b |
| turbo-cache-control | no-preview |
| go-import | github.com/modelcontextprotocol/python-sdk git https://github.com/modelcontextprotocol/python-sdk.git |
| octolytics-dimension-user_id | 182288589 |
| octolytics-dimension-user_login | modelcontextprotocol |
| octolytics-dimension-repository_id | 862584018 |
| octolytics-dimension-repository_nwo | modelcontextprotocol/python-sdk |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 862584018 |
| octolytics-dimension-repository_network_root_nwo | modelcontextprotocol/python-sdk |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 9c975978430e9ad293956f2bbdaf153b1bd84a99 |
| ui-target | canary-2 |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width