Docs

MCP as an API

There is no separate REST API, and you do not need one. MCP is JSON-RPC 2.0 over a single POST — curl is a complete client.

The shape of it

Everything goes to one URL with one header. There are four methods: initialize, tools/list, tools/call and ping. A DELETE ends your session.

Endpointhttps://tabtunnel.dev/v1/mcp
MethodPOST, always
AuthAuthorization: Bearer tt_live_…
Protocol version2025-06-18
SessionMcp-Session-Id, handed back by initialize
Body limit1 MiB

1. Initialize

Every session starts here. Read the session id off the response headers.

terminal
curl -sD- -X POST https://tabtunnel.dev/v1/mcp \
  -H "Authorization: Bearer $TABTUNNEL_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "initialize",
    "params": {
      "protocolVersion": "2025-06-18",
      "clientInfo": { "name": "my-client", "version": "1.0.0" },
      "capabilities": {}
    }
  }'

The reply carries an mcp-session-id header and a result naming the server, its capabilities and an instructions string worth reading — it is what we tell a model about how to use the rest.

2. List the tools

This is the authoritative schema: names, descriptions and every argument. Read it from here rather than from the reference page, which is a summary and does not carry argument types.

terminal
curl -s -X POST https://tabtunnel.dev/v1/mcp \
  -H "Authorization: Bearer $TABTUNNEL_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Mcp-Session-Id: $SESSION" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'

3. Call one

terminal
curl -s -X POST https://tabtunnel.dev/v1/mcp \
  -H "Authorization: Bearer $TABTUNNEL_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Mcp-Session-Id: $SESSION" \
  -d '{
    "jsonrpc": "2.0",
    "id": 3,
    "method": "tools/call",
    "params": { "name": "list_browsers", "arguments": {} }
  }'

Results come back as MCP content blocks: one text block of JSON for most tools, an image block for screenshot, and prose plus a metadata block for read_page.

4. Close it, when you are done

terminal
curl -s -X DELETE https://tabtunnel.dev/v1/mcp \
  -H "Authorization: Bearer $TABTUNNEL_TOKEN" \
  -H "Mcp-Session-Id: $SESSION"

204 whether it existed or not. Sessions expire on their own, but each one counts against the number of sessions your token may hold open until it does, and a session that ends releases the tabs it opened.

Two failures, and they are not the same

  • A protocol fault — malformed envelope, unknown method, missing session — is a JSON-RPC error: -32700 parse, -32600 invalid request, -32601 no such method, -32602 bad params, -32603 internal.
  • A tool failure — browser shut, tab not yours, ref stale — is a successful response carrying isError: true, with a code, whether it is worth retrying, and what to do instead. That is deliberate: the agent is the one reading it.

Things worth knowing before you write a client

  • There is no stream. GET answers 405 — that is the transport's own way of saying a server offers no server-to-client channel, so do not wait on SSE.
  • A 404 means start again. Expired, evicted or another token's — one answer, on purpose. Call initialize again.
  • Give it a generous timeout. A command to an idle browser waits while it is woken, and one a person is being asked about waits while they answer. Many clients default to 60 s; that is the number to check first when a call fails and the browser then does the thing anyway.
  • There is no CORS allowance and never will be. This endpoint is for HTTP clients, not for pages. A browser cannot call it, which is what stops a web app driving somebody's Chrome with a pasted token.
  • Bad credentials are rate-limited per address. Only the rejected attempts count towards the limit; calls that work do not.

Is something here wrong, or missing? Tell us.