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.
Everything goes to one URL with one header. There are four methods:
initialize, tools/list, tools/call and
ping. A DELETE ends your session.
| Endpoint | https://tabtunnel.dev/v1/mcp |
| Method | POST, always |
| Auth | Authorization: Bearer tt_live_… |
| Protocol version | 2025-06-18 |
| Session | Mcp-Session-Id, handed back by initialize |
| Body limit | 1 MiB |
Every session starts here. Read the session id off the response headers.
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.
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.
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"}'
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.
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.
error:
-32700 parse, -32600 invalid request,
-32601 no such method, -32602 bad params,
-32603 internal.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.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.404 means start again.
Expired, evicted or another token's — one answer, on purpose. Call
initialize again.Is something here wrong, or missing? Tell us.