← Blog/Websocket
Inspect WebSocket traffic on a localhost tunnel
Inspect handshake and frames on a Mercur WebSocket tunnel, share the session, and debug CORS that is actually a 503 from the relay.
A WebSocket client that fails in the browser often leaves you with a CORS banner and no payload. Mercur now ships a WSS session inspector on Solo and Team: each public client on a WebSocket endpoint becomes a Connection you can open, share, and read from an MCP client.
This post covers what shipped, who can use it, how it differs from HTTP history,
and the Socket.IO / CORS cases that look like a browser policy problem when the
relay actually returned 503.
What shipped
WebSocket endpoints already forwarded wss:// to a local port. The inspector is
the missing HTTP-history analogue for those sessions.
Open a running WebSocket endpoint and switch to Connections. Each public client is a row: path, query, state (connecting, open, closed, error), duration, and an event timeline. Select a row for handshake headers and the frames that crossed after Upgrade — text or binary. Ping and pong are not stored.
The list polls every few seconds; Refresh fetches immediately. Filters cover path contains and grouped open/closed. There is no inject into a live socket and no Retry on frames.
Share uses the same 24-hour revocable token as HTTP
request history. /share/[token] renders connection history for a WebSocket
endpoint. TCP stays unshareable.
Remote MCP gained read-only tools: list_connections, get_connection,
list_connection_events (no payloads), and get_connection_event (stored
frame). Typical flow: list_endpoints → list_connections →
get_connection_event.
After 200 message events on one connection, later frames increment a dropped counter. Close and error still persist. Retention matches HTTP logs: 30 days, then the plan cap drops the oldest rows.
The public URL still looks like this (your subdomain differs):
wss://your-endpoint.mercur.shWalkthrough: Inspect a WebSocket session.
Who can use it
WebSocket endpoints and the inspector are included on paid plans. Guest mode and
the free plan stay HTTP-only. The public webhook inspector is a different
product: it records HTTP and answers 204, and it never forwards WebSocket
upgrade.
- Solo / Team — create a WebSocket endpoint, run the macOS app or
mercur ws <port>, inspect Connections, share, MCP - Guest / Free — HTTP only
Team members share workspace endpoints. See Pricing for the catalog.
What you can use it for
Use it when a browser, mobile app, or bot must hold a WebSocket to a process on your laptop, and you need to see whether Upgrade succeeded and what frames followed.
Local chat, presence, or live boards. Point the client at the public wss://
URL, watch the connection go open, and confirm the first text frame is the
payload you expected.
Handshake debugging. The Connections inspector shows the path, query, and
headers the public client sent, including Origin. That is the origin Socket.IO
CORS will check — the page origin, not the tunnel host.
Share a live failure. Send the 24-hour link so a teammate can read the same handshake without signing in.
Inspect from the IDE. With a workspace API key or OAuth grant, ask an MCP client to list connections instead of pasting console JSON into chat. Setup: Remote MCP.
What it is not
The inspector stores WebSocket frames. It does not decode Socket.IO packets, GraphQL subscriptions, or MQTT.
- No console send / inject into a live socket
- No log-only WSS bin without an agent
- No Socket.IO or GraphQL first-class parser
- No TCP connection inspector
- Guest inspector stays HTTP-only
HTTP and WebSocket are different Mercur endpoints. Polling HTTP and Upgrade do not share one tunnel. That split is the usual cause of “CORS” on Socket.IO.
Start using the inspector
Sign in on Solo or Team, create a WebSocket endpoint, and start the agent on
the local port. Copy the wss:// URL into the client you are testing. Open
Connections and trigger one connect.
Open Endpoints
Leave Public access on if anyone with the URL may connect. Restrict the address list when the caller should be a known office range.
Troubleshooting
The browser reports CORS, curl shows 503
Chrome’s CORS error on
XMLHttpRequest to /socket.io/?transport=polling means the response had no
Access-Control-Allow-Origin. Mercur error pages (503, 502, 403, 429)
are plain text without CORS headers. The browser then reports CORS even when the
real problem is “no agent” or “wrong protocol”.
Check with curl before changing Socket.IO cors:
curl -i https://your-endpoint.mercur.sh/socket.io/?EIO=4&transport=polling503 No agent for client means the relay has no live HTTP agent for that
host. Any of these produce the same body:
- the desktop app or CLI is not running
- the claim lapsed after 15 seconds without a heartbeat
- the hostname does not match an endpoint
- you sent HTTP (Socket.IO polling) to a WebSocket endpoint
Socket.IO polling vs WebSocket endpoints
Default Socket.IO starts with HTTP long-polling, then tries Upgrade: websocket. Mercur HTTP endpoints forward GET/POST and do not proxy
WebSocket upgrade. Mercur WebSocket endpoints accept upgrade and do not
serve polling.
So:
- Polling against a WebSocket endpoint →
503 No agent for client→ browser CORS - Upgrade against an HTTP endpoint → the socket is dropped, no HTTP CORS header
transports: ["websocket"] only helps on a WebSocket endpoint. Serving the
HTML from the same tunnel origin avoids cross-origin XHR. If you keep the page
on http://localhost:63342, the local Socket.IO server must allow that origin
in cors.origin — not the *.mercur.sh host.
The HTTP agent buffers the full upstream body before returning it. Socket.IO long-poll through an HTTP tunnel is a poor fit even after CORS is correct.
Catalog: Troubleshooting.
Connections tab is empty
The tab exists only on WebSocket endpoints. An HTTP endpoint shows request
history, not Connections. If the endpoint is WebSocket and the list is empty,
the agent is not claimed, or no client completed Upgrade. Failed handshakes can
still appear as state: error.
Handshake dropped with no HTTP status
Rejections before Upgrade completes close the socket without an HTTP response.
The client reports a reset. Causes match the HTTP list: unknown host, stopped
endpoint, address not allowed, no agent, quota, or session limit. Open the
endpoint in the console; an error connection row is the inspectable form of
that failure.
FAQ
Which plans include the WSS session inspector? Solo and Team. Guest and Free cannot create WebSocket endpoints.
Does Mercur decode Socket.IO events? No. Frames are stored as text or binary. Use the Connections timeline plus your own client logs.
How long does a share link last? 24 hours, same as HTTP request-history share. You can revoke it from the endpoint.
Can an agent read connections over MCP? Yes, on Solo and Team, with the same OAuth grant or workspace API key as HTTP history. Tools are read-only.
Related
Open Endpoints
Read the WebSocket inspector docs