# Session tokens for HTTP MCP
This guide covers how an external agent reaches a **live** Dialup browser session over HTTP MCP.
For the full API reference — discovery, `tools/list`, `tools/call`, grants, and metadata — read
[HTTP MCP API](./mcp-api.md) first.
## Ordinary URL vs session token
| URL | Purpose |
|-----|---------|
| `https://app.example.com/invoices` | Describes the app and static tool catalog via discovery |
| `POST /agent/{token}` | Operates one specific live session (bearer credential) |
Opening the ordinary URL in an agent's own browser creates that browser's session. It does not
attach to work already open in the user's tab.
## Obtaining a token
### From server code
```elixir
{:ok, descriptor} =
Dialup.Session.grant(session_pid,
capabilities: :all,
projections: [:state, :regions, :actions],
expires_in: :timer.minutes(15),
require_version: true
)
token = descriptor["token"]
endpoint = descriptor["endpoint"] # "/agent/{token}"
```
### From the user's open tab
When the user already has the app open, issue a token tied to that tab's registry key:
```bash
curl -X POST 'https://app.example.com/_dialup/agent-handoff?tab_id=TAB_ID' \
-H 'Cookie: dialup_session=SESSION_ID'
```
The response includes `token`, `endpoint`, and `grant` metadata. Pass `endpoint` to your MCP client.
In browser JavaScript (same origin), use `Dialup.tabId` from `dialup.js`:
```javascript
const res = await fetch(`/_dialup/agent-handoff?tab_id=${encodeURIComponent(Dialup.tabId)}`, {
method: "POST",
credentials: "same-origin",
});
const { token, endpoint } = await res.json();
```
## Calling the API
```bash
curl -X POST "https://app.example.com/agent/TOKEN" \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"read_scene","arguments":{}}}'
```
See [HTTP MCP API](./mcp-api.md) for the complete method list, versioning rules, and error codes.
## Revocation and expiry
- `DELETE /agent/:token` — revoke the grant
- `Dialup.Session.revoke(session_pid, token)` — same from Elixir
- Expired tokens return JSON-RPC error `-32002`
Ask the user to issue a fresh token from their still-open tab if a grant expires mid-task.
## Live example
See the [agent handoff demo](https://dialup-framework.org/agent_demo) on dialup-framework.org.
The demo page mints a token via `Dialup.Session.grant/2`. Production apps typically combine
programmatic grants with the handoff endpoint for user-initiated access.