openclaw - ✅(Solved) Fix MCP streamable-http transport — spawned server process times out [1 pull requests, 1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#72111Fetched 2026-04-27 05:34:43
View on GitHub
Comments
1
Participants
2
Timeline
2
Reactions
0
Timeline (top)
commented ×1cross-referenced ×1

Error Message

failed to start server "openspace" (/opt/homebrew/bin/openspace-mcp --transport streamable-http --host 127.0.0.1 --port 8083): Error: MCP server connection timed out after 30000ms

PR fix notes

PR #72515: fix(mcp): reject mixed command+url transport config (#72111)

Description (problem / solution / changelog)

Closes #72111.

Problem

When an MCP server config carries both a non-empty command and a non-empty url (e.g. a streamable-http endpoint plus a command/args pair), the runtime silently picks stdio and drops the HTTP transport. The user sees:

MCP server connection timed out after 30000ms

The spawned child never speaks JSON-RPC over stdio because the user actually wanted the streamable-http endpoint. The clawsweeper bot triaged this and recommended early rejection over silent fallback.

Root cause

resolveMcpTransportConfig in src/agents/mcp-transport-config.ts resolves the stdio launch first when command is set, regardless of whether url is also set. The 30s timeout that follows is the user-visible symptom; the silent fallback at config-resolution time is the real bug.

Reproducer

In ~/.openclaw/openclaw.json:

{
  \"mcp\": {
    \"servers\": {
      \"broken\": {
        \"command\": \"node\",
        \"args\": [\"something.js\"],
        \"url\": \"https://my-mcp.example.com/mcp\"
      }
    }
  }
}

Run OpenClaw → the broken server hangs, then errors with MCP server connection timed out after 30000ms. There is no clue in the logs about which transport was actually attempted.

Fix

Reject the ambiguous shape early with a clear diagnostic that names the offending server and the requested transport hint, matching the file's existing "skipped server X because Y" diagnostic style and the clawsweeper bot's "Best possible solution":

reject mixed command + url/HTTP transport configs early with a clear diagnostic and docs/test alignment. The current silent stdio precedence should not remain the user-visible timeout path.

Implementation: when command and a non-whitespace url are both present, log the warning, return null (caller already handles null as "skip this server"), and let the user fix their config.

Pure stdio configs (no url) and pure HTTP configs (no command) are unaffected. Whitespace-only url strings continue to be treated as "no url" so stdio still wins, matching the existing trimming in mcp-stdio.ts.

Diff: +17 lines in mcp-transport-config.ts, +52 in tests.

Verification

pnpm vitest run src/agents/mcp-transport-config.test.ts
# 11 passed (7 original + 4 new)

pnpm check
# 0 errors, 0 warnings; all policy guards green

Tests added (4 new)

  1. Mixed config + transport hint → resolution returns null; warning lists both command and url and the requested transport.
  2. Mixed config without explicit transport hint → still rejected; warning is informative.
  3. Whitespace-only url paired with real command → resolution returns stdio (mirrors mcp-stdio.ts trimming).
  4. Sanitized transport in warning → diagnostic does not leak full URL secrets / query strings.

Risk notes

  • Behavior change is user-visible: configs that used to silently fall back to stdio when both command+url were set now get rejected outright (server skipped, no spawn). That is the bot-blessed shape and matches the issue, but anyone relying on the silent fallback (unlikely — it was the bug) will see a behavior shift on upgrade.
  • Did NOT touch src/agents/mcp-stdio.ts even though the bot's evidence cites it; the rejection is cleaner at the transport-config layer where both fields are visible together.
  • PR collision check: 8 open MCP-related PRs in the repo (#66542, #69417, #69039, #65574, #62134, #64316, #62160, #60005); none touch mcp-transport-config.ts validation logic for the mixed shape. Safe to land independently.

Changed files

  • src/agents/mcp-transport-config.test.ts (modified, +52/-0)
  • src/agents/mcp-transport-config.ts (modified, +14/-0)

Code Example

failed to start server "openspace" (/opt/homebrew/bin/openspace-mcp --transport streamable-http --host 127.0.0.1 --port 8083): Error: MCP server connection timed out after 30000ms

---

curl -X POST http://127.0.0.1:8083/mcp \
    -H "Accept: application/json, text/event-stream" \
    -d '{"jsonrpc":"2.0","id":1,"method":"initialize",...}'
  # Returns valid response

---

{
  "openspace": {
    "command": "/opt/homebrew/bin/openspace-mcp",
    "args": ["--transport", "streamable-http", "--host", "127.0.0.1", "--port", "8083"],
    "transport": "streamable-http",
    "url": "http://127.0.0.1:8083/mcp",
    "env": { ... }
  }
}
RAW_BUFFERClick to expand / collapse

Issue: MCP streamable-http transport — spawned server process times out

OpenClaw version: 2026.4.24
MCP SDK version: @modelcontextprotocol/sdk 1.29.0
OpenSpace version: 1.26.0 (HKUDS/OpenSpace)


Problem

When configuring an MCP server with transport: "streamable-http", OpenClaw spawns the server process correctly but the connection times out after 30000ms:

failed to start server "openspace" (/opt/homebrew/bin/openspace-mcp --transport streamable-http --host 127.0.0.1 --port 8083): Error: MCP server connection timed out after 30000ms

The server process does start (verified by lsof showing port 8083 in LISTEN state), but OpenClaw's MCP client cannot establish a connection to it.

What works

  • STDIO transport: works correctly
  • Accept header: fixed (no more 406 errors — issue #66940 appears resolved)
  • Direct curl to OpenSpace HTTP endpoint: works correctly with correct Accept header:
    curl -X POST http://127.0.0.1:8083/mcp \
      -H "Accept: application/json, text/event-stream" \
      -d '{"jsonrpc":"2.0","id":1,"method":"initialize",...}'
    # Returns valid response
  • Session persistence: OpenSpace HTTP correctly returns mcp-session-id and a second request with the session ID also works

Configuration

{
  "openspace": {
    "command": "/opt/homebrew/bin/openspace-mcp",
    "args": ["--transport", "streamable-http", "--host", "127.0.0.1", "--port", "8083"],
    "transport": "streamable-http",
    "url": "http://127.0.0.1:8083/mcp",
    "env": { ... }
  }
}

Hypothesis

When OpenClaw spawns a child process for streamable-http transport, it may not be properly handling the process lifecycle or the stdio redirect. The spawned process starts and binds to the port, but the MCP client transport fails to complete the connection handshake.

The issue appears to be in how OpenClaw's StreamableHTTPClientTransport interacts with a spawned child process (vs a pre-existing HTTP server). The Accept header fix in #66940 resolved the pre-existing server case, but the process spawning case may have a separate issue.

Environment

  • macOS (Darwin 25.3.0, arm64)
  • Node.js v25.7.0
  • Python 3.14 (OpenSpace MCP server)
  • Launchd-managed process spawning vs OpenClaw-managed spawning

Labels: mcp, streamable-http, transport

extent analysis

TL;DR

The issue is likely due to OpenClaw's StreamableHTTPClientTransport not properly handling the process lifecycle or stdio redirect when spawning a child process for streamable-http transport, causing the connection to timeout.

Guidance

  • Verify that the StreamableHTTPClientTransport is correctly configured and implemented to handle spawned child processes.
  • Check the stdio redirect configuration to ensure it is properly set up for the spawned process.
  • Investigate the difference in behavior between launchd-managed process spawning and OpenClaw-managed spawning to identify potential issues.
  • Test the StreamableHTTPClientTransport with a pre-existing HTTP server to see if the issue is specific to spawned child processes.

Example

No code snippet is provided as the issue is more related to configuration and process management.

Notes

The issue appears to be specific to the streamable-http transport and spawned child processes, and may not be related to the Accept header fix in #66940.

Recommendation

Apply a workaround by using a pre-existing HTTP server instead of spawning a child process, if possible, to avoid the potential issue with StreamableHTTPClientTransport and process lifecycle management.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - ✅(Solved) Fix MCP streamable-http transport — spawned server process times out [1 pull requests, 1 comments, 2 participants]