Claude.AI hangs with 408 timeout when MCP tool response JSON contains U+2028 (LINE SEPARATOR) or U+2029 (PARAGRAPH SEPARATOR)

Author: randyramigCreated May 25, 2026Updated Sep 17, 2026
Labelsbugready for workP2fix proposed

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

  1. Connect this MCP server to Claude.AI.
  2. Invoke the repro_u2028 tool.
  3. Observe timeout behavior.
  4. Change the payload so separators are escaped in transport text (for example replacing raw U+2028 with escaped sequence form before parse/forward).
  5. 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.

Source: modelcontextprotocol/typescript-sdk