Skip to content

perplexity_research times out in MCP clients — server buffers full SSE stream, never emits notifications/progress #110

Description

@sergattic

Summary

perplexity_research (Sonar Deep Research) reliably hits MCP error -32001: Request timed out in MCP clients with sub-2-min request timeouts (observed in Claude Desktop at ~60–90s), even though PERPLEXITY_TIMEOUT_MS default is 300000ms.

Root cause is in src/server.ts: streaming is enabled for sonar-deep-research, but consumeSSEStream() accumulates the entire response in memory and only returns at end-of-stream. Nothing is sent to the MCP client until the whole thing finishes. From the client's perspective the tool call is a single silent RPC, and Claude Desktop's default MCP request timeout kills it long before the deep research completes (deep research routinely spends 60+ seconds in the search/reasoning phase before yielding tokens).

PERPLEXITY_TIMEOUT_MS only governs the server→API timeout. The blocker is client→server.

Reproduction

Client: Claude Desktop, MCP server installed via npx -y @perplexity-ai/mcp-server.

{
  "tool": "perplexity_research",
  "arguments": {
    "messages": [{"role": "user", "content": "<any non-trivial research prompt>"}],
    "reasoning_effort": "medium"
  }
}

Result after ~1m30s:

MCP error -32001: Request timed out

Server log shows the API call still in flight server-side. Lowering reasoning_effort to minimal works because the call finishes inside the client window. That's a workaround, not a fix.

Fix

Per MCP spec, notifications/progress resets the client's request timeout. The streaming code path already has per-chunk granularity — it just needs to emit progress notifications as deltas arrive (and a periodic heartbeat during the initial silent search phase).

Working patch in src/server.ts:

  1. Add an optional onProgress callback to consumeSSEStream(response, onProgress?) — called once per delta chunk, plus a 10s heartbeat when the stream is silent.
  2. Thread it through performChatCompletion(..., onProgress?).
  3. In the perplexity_research tool handler, read extra._meta.progressToken and extra.sendNotification. When both are present, emit:
await sendNotification({
  method: "notifications/progress",
  params: {
    progressToken,
    progress: counter,
    message: `Deep research running… ${chunkCount} chunks, ${totalChars} chars streamed`,
  },
});

This keeps the client's request timer alive for the full duration of the research call (validated locally — medium-effort runs that previously timed out now complete cleanly).

Same fix could optionally be applied to perplexity_reason (sonar-reasoning-pro) although that model typically finishes inside the default window.

Happy to open a PR with the patch if useful — concentrated in src/server.ts (~30 lines).

Environment

  • @perplexity-ai/mcp-server 0.9.0
  • Claude Desktop 1.7196.0
  • Node 20.20.2 on macOS

Related

https://community.perplexity.ai/t/perplexity-sonar-deep-research-model-timeout-issue-seeking-solution/2094 — same model, different layer (HTTP client side).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions