Relay HTTP Control API
The relay service exposes a small authenticated HTTP API for direct browser-tool control of a connected Vibe Browser extension. It also exposes a standards-based MCP Streamable HTTP endpoint at /mcp — see Remote MCP endpoint below.
This API is intended for server-side automations that already know the extension uuid and secret and want a simple request/response surface without speaking the raw WebSocket protocol.
Authentication
Pass the extension secret using either:
Authorization: Bearer <secret>X-Vibe-Secret: <secret>
If the UUID is unknown, the relay returns 404.
If the UUID is known but the extension is currently offline, tool endpoints return 409.
If the secret is wrong, the relay returns 401.
Endpoints
Assume base URL https://relay.api.vibebrowser.app.
Get extension status
curl -H "Authorization: Bearer $VIBE_BROWSER_SECRET" \
"https://relay.api.vibebrowser.app/api/v1/extensions/$VIBE_BROWSER_UUID/status"
Returns connection state, tool cache status, pending request count, and extension version.
List tools
Return cached tools:
curl -H "Authorization: Bearer $VIBE_BROWSER_SECRET" \
"https://relay.api.vibebrowser.app/api/v1/extensions/$VIBE_BROWSER_UUID/tools"
Refresh tools from the live extension first:
curl -H "Authorization: Bearer $VIBE_BROWSER_SECRET" \
"https://relay.api.vibebrowser.app/api/v1/extensions/$VIBE_BROWSER_UUID/tools?refresh=1"
Call one browser tool
curl -X POST \
-H "Authorization: Bearer $VIBE_BROWSER_SECRET" \
-H "Content-Type: application/json" \
"https://relay.api.vibebrowser.app/api/v1/extensions/$VIBE_BROWSER_UUID/call-tool" \
-d '{
"name": "navigate_to_url",
"arguments": {"url": "https://example.com"}
}'
Snapshot tools
curl -X POST \
-H "Authorization: Bearer $VIBE_BROWSER_SECRET" \
-H "Content-Type: application/json" \
"https://relay.api.vibebrowser.app/api/v1/extensions/$VIBE_BROWSER_UUID/call-tool" \
-d '{
"name": "take_snapshot",
"arguments": {"format": "accessibility_tree"}
}'
Notes
- This API exposes browser tools, not the internal copilot chat/task loop.
- Tool names and argument schemas come from the connected extension.
- Call
tools?refresh=1before the first tool call in a new session when the tool surface may have changed.
CDP Compatibility Facade
The relay also exposes a small CDP-shaped compatibility facade for remote clients that prefer Chrome DevTools style verbs.
This is not a raw CDP tunnel. It is a narrow allowlisted mapping onto Vibe browser tools.
Version
curl -H "Authorization: Bearer $VIBE_BROWSER_SECRET" \
"https://relay.api.vibebrowser.app/api/v1/extensions/$VIBE_BROWSER_UUID/cdp/json/version"
List targets
curl -H "Authorization: Bearer $VIBE_BROWSER_SECRET" \
"https://relay.api.vibebrowser.app/api/v1/extensions/$VIBE_BROWSER_UUID/cdp/json/list"
Execute a CDP-compatible command
curl -X POST \
-H "Authorization: Bearer $VIBE_BROWSER_SECRET" \
-H "Content-Type: application/json" \
"https://relay.api.vibebrowser.app/api/v1/extensions/$VIBE_BROWSER_UUID/cdp/execute" \
-d '{
"id": 1,
"method": "Page.navigate",
"params": {
"pageId": 123,
"url": "https://example.com"
}
}'
Supported methods
Browser.getVersionTarget.getTargetsTarget.createTargetTarget.closeTargetPage.navigatePage.reloadPage.captureScreenshotRuntime.evaluate
Notes
- Use
Authorization: Bearer <secret>orX-Vibe-Secretfor auth; query-string tokens are intentionally not supported. Target.getTargetsandcdp/json/listare backed bylist_pages.Page.captureScreenshotis backed bytake_screenshot.Runtime.evaluateis backed byevaluate_scriptand returns JSON-RPC style results.
Remote MCP endpoint (/mcp)
POST https://relay.api.vibebrowser.app/mcp is a standards-based MCP Streamable HTTP endpoint. It is stateless JSON mode: one JSON-RPC message in, one JSON-RPC response out. There is no SSE stream (GET /mcp returns 405) and no server-side session (Mcp-Session-Id is not issued; DELETE /mcp is a no-op 200).
This is the surface documented for AI clients in MCP Integration → Remote MCP. This page documents it at the protocol level for anyone integrating directly.
Authentication
Auth is a different credential from the /api/v1 API above — /mcp uses the extension's uuid only, the same identifier already used for WS agent connections (wss://relay.../<uuid>). The extension secret is not accepted here.
Precedence:
X-Remote-Session: <uuid>Authorization: Bearer <uuid>(alias)
Both accept either the bare UUID or a full relay URL containing it (e.g. wss://relay.api.vibebrowser.app/<uuid>) — the trailing UUID is extracted.
Second-factor attach token (#1534)
The session UUID alone is not sufficient for a session that opted in to a
per-session attach token (a second factor that closes the original IDOR). If
the extension registered an attach token (a 64-lowercase-hex string, protocol
v2), every agent — WebSocket and /mcp — must present the matching token:
Authorization: Bearer <token>(preferred), orX-Vibe-Attach-Token: <token>(compatibility).
On /mcp, because Authorization: Bearer may already carry the session UUID,
send the UUID via X-Remote-Session and the token via Authorization: Bearer
(or just use X-Vibe-Attach-Token). Query-string tokens are never accepted.
Enforcement:
- Per-session opt-in: if a session registered a token, a missing/invalid
token is rejected regardless of the global switch — WebSocket agents are closed
with code
4010(generic reason),/mcpreturns HTTP401. - Global switch
RELAY_REQUIRE_ATTACH_TOKEN(defaultfalse): whentrue, even token-less legacy sessions fail closed for token-less agents. - Revocation supersedes token auth: a revoked identifier is denied first (WS
4009,/mcp403) even if a correct token is presented. - Token rotation evicts existing agents: when the extension rotates its
attach token (explicitly, or detected on ordinary re-auth presenting a
different token), every WebSocket agent still attached under the OLD
token is closed with the distinct code
4011"Attach token rotated" — not the generic4010used for auth failures, so a well-behaved agent can tell "you need a fresh token" apart from "you were never authorized" and reconnect with the new one. Any in-flight request for that agent is failed first with a generic error (never echoing token material). Rotating to the SAME token is a no-op — idempotent, no eviction.
Tokens are compared in constant time and are never logged or echoed in error
bodies/close reasons. Structured, PII-free counters (ok/token, ok/legacy,
denied/missing, denied/invalid) are exposed on GET /health under
attachToken.counters, and rotation counters (detected, agentsClosed,
pendingCleared) under attachToken.rotation.
Error codes
| Condition | Response |
|---|---|
No X-Remote-Session / Authorization header | HTTP 401, { "error": "..." } |
| Revoked session identifier | HTTP 403, { "error": "Session revoked" } |
| UUID not a known registered extension | HTTP 404, { "error": "..." } |
Missing/invalid attach token for a token-enabled session (or token-less session while RELAY_REQUIRE_ATTACH_TOKEN=true) | HTTP 401, { "error": "Unauthorized: attach token required" } |
| Malformed JSON body | JSON-RPC -32700 (HTTP 200 — parse errors are still transport-successful) |
| Request body is a JSON array (batching) | JSON-RPC -32600 |
Unknown method | JSON-RPC -32601 |
tools/list / tools/call while extension is offline or heartbeat-stale | JSON-RPC -32002, message tells the user to open Chrome and check Settings → AI Agent Control → Remote |
tools/call extension-side tool error | JSON-RPC -32000 with the extension's error text |
tools/call while MAX_PENDING_PER_SESSION in-flight requests are already queued | JSON-RPC -32000 "Relay busy" |
initialize always succeeds once the UUID is known/registered, even if the extension (Chrome) is currently offline, so client setup doesn't require Chrome to be open.
curl example
# initialize
curl -X POST https://relay.api.vibebrowser.app/mcp \
-H "Content-Type: application/json" \
-H "X-Remote-Session: $VIBE_BROWSER_UUID" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26"}}'
# tools/list
curl -X POST https://relay.api.vibebrowser.app/mcp \
-H "Content-Type: application/json" \
-H "X-Remote-Session: $VIBE_BROWSER_UUID" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'
# tools/call
curl -X POST https://relay.api.vibebrowser.app/mcp \
-H "Content-Type: application/json" \
-H "X-Remote-Session: $VIBE_BROWSER_UUID" \
-d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"list_pages","arguments":{}}}'