2026 OpenClaw on a Remote Physical Mac: MCP stdio & Streamable HTTP—Reproducible Runbook (spawn ENOENT, Protocol Mismatch, Tool Timeouts + CLI + FAQ)
Teams running OpenClaw on unattended physical Mac gateways hit three recurring MCP failures: stdio spawn ENOENT from a stripped PATH, Streamable HTTP version or content-type skew, and tool calls that exceed client or server timeouts. This guide gives a seven-step rollout, stdio vs HTTP decision matrix, copy-paste MCP server JSON, triage tables, quotable SLO-style thresholds, and FAQ—so you can reproduce fixes in internal docs.
1. Why MCP breaks on remote Mac gateways
MCP (Model Context Protocol) is just structured messages over a transport. On a laptop you “see” failures immediately in a terminal; on a rented or colocated Mac the gateway often runs under launchd with no login shell, a different working directory, and a PATH that does not include Homebrew or Volta shims—so stdio servers fail before they print a single log line.
- Environment drift — Interactive SSH sessions load
~/.zprofile; the daemon does not. Commands that work in SSH still throwspawn ENOENTunder the gateway. - Transport mismatch — Streamable HTTP and SSE-style endpoints require matching protocol revisions,
Acceptheaders, and TLS termination behavior. A reverse proxy that buffers or strips headers surfaces as “version mismatch” or 406—not “network down.” - Timeout layering — The model client, OpenClaw gateway, MCP server, and the underlying tool each enforce different deadlines. Raising only one layer masks the bug and creates hung jobs on shared physical nodes.
When you expose Streamable HTTP through the same edge as webhooks, reuse the bearer-token and path-prefix discipline from our OpenClaw webhooks & reverse-proxy runbook so 401/404 noise does not masquerade as MCP errors.
2. stdio vs Streamable HTTP: which transport when?
Pick the transport that minimizes moving parts on the same host, then graduate to HTTP when you must share a server or terminate auth at the proxy.
| Criterion | stdio MCP server | Streamable HTTP MCP server |
|---|---|---|
| Blast radius on failure | Isolated child process; gateway can restart one server. | Shared port and TLS cert; misconfig affects all clients hitting that URL. |
| PATH & cwd sensitivity | High — ENOENT is the default failure mode. | Lower for binary resolution (server managed by systemd/launchd), higher for upstream URLs. |
| Multi-tenant / remote clients | One gateway process per machine unless you multiplex manually. | Natural fit — one HTTP server, many callers behind auth. |
| Observability | Structured logs from gateway + stderr of child. | Add proxy access logs, upstream timing, and ALPN/TLS tracing. |
3. Seven-step reproducible rollout
- Capture identity — Note the user that owns OpenClaw, the plist or LaunchAgent label, and the cwd from
launchctl print/ service unit. - Replay stdio without the gateway — As that user, run
/bin/bash -lc 'which node; node -v'and the exact MCP command with env-file parity. - Harden stdio config — Absolute paths for
command, explicitargs, optionalcwd, and explicitenv(see JSON below). - Optional HTTP path — Bind MCP HTTP to
127.0.0.1, terminate TLS on nginx/Caddy, and verifycurl -vshows the expectedAcceptnegotiation. - Version lock — Pin the MCP SDK / server package to the same major.minor as the client library inside OpenClaw; record both in your changelog.
- Timeout budget — Measure
tools/callP95 with realistic inputs; set client wait ≥ P95 × 1.5 with a hard cap. - Log contracts — Emit one JSON line per
initialize,tools/list, andtools/callwith a shared correlation id.
For always-on agent flows that must survive restarts, align this runbook with the automation patterns in OpenClaw 24/7 automation on ZoneMac physical nodes.
4. CLI configuration fragments (illustrative JSON)
Adapt keys to your OpenClaw or host config schema; the important part is absolute paths and explicit environment blocks.
stdio server with Node entrypoint (macOS)
{
"mcpServers": {
"repo-tools": {
"command": "/opt/homebrew/bin/node",
"args": ["/usr/local/lib/mcp-servers/repo-tools/dist/main.js"],
"cwd": "/Users/shared/mcp/repo-tools",
"env": {
"PATH": "/opt/homebrew/bin:/usr/bin:/bin",
"NODE_ENV": "production"
}
}
}
}
stdio via login-shell wrapper (when you must source profiles)
{
"mcpServers": {
"legacy-npx": {
"command": "/bin/bash",
"args": ["-lc", "exec npx --yes @example/[email protected]"],
"env": { "HOME": "/Users/shared" }
}
}
}
Streamable HTTP endpoint (client-side reference)
{
"mcpServers": {
"http-shared": {
"url": "https://mcp.internal.example.com/v1/mcp",
"headers": {
"Authorization": "Bearer ${MCP_EDGE_TOKEN}"
}
}
}
}
5. Symptom runbooks
5.1 spawn ENOENT (stdio)
| Signal | Likely cause | Fix order |
|---|---|---|
ENOENT on node |
Daemon PATH missing Homebrew. | Set absolute command or prefix PATH in env. |
| ENOENT on script path | Wrong cwd or symlink not visible to service user. | Set cwd; replace symlink with realpath. |
| Works in SSH only | Interactive shell exports differ from launchd. | Use bash -lc wrapper or migrate vars into plist EnvironmentVariables. |
5.2 Protocol version / 406 (Streamable HTTP)
Compare server and client handshake logs side by side. If the server expects text/event-stream but the proxy injects gzip without flushing, clients stall with “version mismatch” even though semver looks aligned.
5.3 Tool timeouts
Chart three timers: (A) model/tool-router wait, (B) gateway upstream deadline, (C) MCP server internal job timeout. Adjust the tightest layer first, then widen adjacent layers only if traces prove queuing.
6. Numbers you can paste into SLO docs
- ~4W — Apple Silicon Mac mini idle draw while holding SSH + lightweight gateways—useful for TCO models against x86 towers that idle much higher.
- 25s — Rule-of-thumb ceiling for “fast”
tools/callP95 on shared nodes; beyond this, split tools or stream partial results instead of raising timeouts indefinitely. - 600s — Long-running maintenance tools (large repo scans) should move to async jobs with polling, not a single blocking MCP call—mirrors how we treat long Git/npm operations in CI mirrors.
7. FAQ
Should I run MCP servers as root?
Prefer a dedicated service user with file ACLs scoped to the workspace; root breaks macOS privacy prompts and makes TCC audits harder.
Does Rosetta affect MCP binaries?
Yes—mixing arm64 Node with x64-only native add-ons causes instant crash loops that look like timeouts. Standardize arch per server.
Can I reuse corporate HTTPS inspection certs?
You must import the MITM root into the system keychain the service user trusts; otherwise Streamable HTTP TLS fails with opaque handshake errors.
Where do I track protocol bumps?
Keep a one-line compatibility table in your internal wiki: OpenClaw build, MCP SDK version, server package hash, and the date you last ran tools/list smoke tests.
8. Run this stack on a quiet, efficient Mac mini
stdio MCP servers are unforgiving about environment parity—exactly the sort of workload that behaves better on a long-lived macOS host with predictable paths, Gatekeeper, and SIP than on ad-hoc Windows VMs. Apple Silicon Mac mini systems combine very low idle power (on the order of a few watts), near-silent cooling, and a Unix-native toolchain so Node, proxies, and launchd-driven gateways can run 24/7 without the driver churn common on other platforms.
If you are standardizing remote gateways for OpenClaw plus MCP sidecars, consolidating onto Mac mini M4-class hardware reduces moving parts: one architecture for your agents, your build tools, and your observability agents, with FileVault and TCC giving you a cleaner audit story than mixed Linux images.
If you want the lowest-friction place to rehearse the runbook above, Mac mini M4 remains the most cost-effective physical anchor—pick up a node and ship the same JSON configs you validated in staging.
Need a stable physical Mac for OpenClaw + MCP?
Rent a Mac mini node with the same macOS toolchain you use in this runbook—low idle power, quiet 24/7 operation, and predictable paths for stdio servers.