modelcontextprotocol / modelcontextprotocol/typescript-sdk
Claude.AI hangs with 408 timeout when MCP tool response JSON contains U+2028 (LINE SEPARATOR) or U+2029 (PARAGRAPH SEPARATOR)
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 13.4k
- Forks
- 2.2k
- Avg merge
- 3d 15h
- Merged PRs (30d)
- 4
Description
Summary
Claude.AI times out with 408 (Waited 300.0 seconds) for certain MCP tool calls, even though the MCP server logs show a successful and fast 200 response.
This appears to happen when upstream JSON data includes raw Unicode line/paragraph separators in string fields:
- U+2028 LINE SEPARATOR
- U+2029 PARAGRAPH SEPARATOR
Environment
- Client: Claude.AI (web)
- Transport: MCP Streamable HTTP via POST /mcp
- Server runtime: Node.js + TypeScript
- MCP SDK: @modelcontextprotocol/sdk ^1.29.0
- Hosting: Railway
- Backend API source: KEXP public API
Actual Behavior
- Claude.AI tool call hangs and eventually returns 408 timeout.
- Server logs show:
- Request received
- Tool executed
- 200 response returned quickly
- Issue is reproducible with one specific record containing unusual Unicode content, while adjacent records work normally.
Expected Behavior
- Tool call should succeed and return parsed response.
- If parsing fails client-side, client should fail fast with a parse error, not hang until timeout.
Repro (Minimal)
Create a minimal MCP tool that returns JSON containing raw U+2028 in a string value.
Example server snippet
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
const server = new McpServer({ name: 'unicode-repro', version: '1.0.0' });
server.tool('repro_u2028', 'Returns JSON with U+2028 in a string', {}, async () => {
const payload = {
comment: 'Line one.\u2028Line two.'
};
return {
content: [
{
type: 'text',
text: JSON.stringify(payload)
}
]
};
});
const transport = new StdioServerTransport();
await server.connect(transport);
Repro steps
- Connect this MCP server to Claude.AI.
- Invoke the repro_u2028 tool.
- Observe timeout behavior.
- Change the payload so separators are escaped in transport text (for example replacing raw U+2028 with escaped sequence form before parse/forward).
- Re-run and observe successful response.
Why this seems important
U+2028 and U+2029 are valid in JSON text, but historically treated as line terminators in JavaScript contexts.
A parser/transport boundary handling bug could cause a stall instead of a deterministic parse error.
Server-Side Workaround Confirmed
Sanitizing response text before JSON.parse resolved the issue in production for the affected records.
const text = await response.text();
const sanitized = text
.replace(/\u2028/g, '\\u2028')
.replace(/\u2029/g, '\\u2029');
const data = JSON.parse(sanitized);
Request
Please investigate Claude.AI MCP response parsing/transport handling for payloads containing U+2028 and U+2029, and return a deterministic error or successful parse instead of timeout behavior.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at the Streamable HTTP POST /mcp response path and JSON parsing boundary, using the supplied repro_u2028 tool with raw U+2028 and U+2029. Trace the response handling and add coverage for both separators. Done means a successful parsed tool response or an immediate deterministic parse error instead of a timeout.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- node.js, typescript
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100