> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vana.org/llms.txt
> Use this file to discover all available pages before exploring further.

# MCP integration

> Connect an AI app to a user's Personal Server over MCP, bounded by the same grant, fees, and access log as any builder read.

<Info>
  **Status.** Two MCP surfaces ship in `@opendatalabs/personal-server-ts` 1.16.0.

  * The **owner's own client** path (OAuth, works with a stock MCP client such as Claude Desktop or Claude Code) runs on both the Node server and the in-browser PS Lite runtime.
  * The **third-party app** path — an app that holds its own key and signs its own handshake — runs on the **Node Personal Server only**. PS Lite does not serve `POST /mcp/session`.
  * Both surfaces are **read-only**. There are no write tools and no owner-management tools.
</Info>

A [Personal Server](/protocol-reference/personal-servers) exposes an MCP server over streamable HTTP. A tool that serves scope contents resolves to the same read path as the HTTP API, behind the same [grant](/protocol-reference/grants-permissions) check and the same access log. The grant is the ceiling on what any client can reach, and revoking it fails the client closed immediately.

This is the integration path for an app whose consumer is an **agent** rather than a backend: the model discovers the user's granted scopes, searches them, and pulls only the blocks it needs, instead of your code hard-coding a scope list.

## Which surface you need

|                           | Owner's own client                                                   | Third-party app                                                     |
| ------------------------- | -------------------------------------------------------------------- | ------------------------------------------------------------------- |
| **Who connects**          | The user, with their own MCP client                                  | Your app, on its own backend                                        |
| **Identity**              | A grantee keypair the Personal Server mints per connection and holds | Your registered builder address; you hold the key                   |
| **How access is granted** | OAuth-style consent inside Vana; the user picks scopes               | An ordinary access request; the grant is issued to your app address |
| **Authentication**        | Connection token from the OAuth flow                                 | Short-lived bearer minted from a signed handshake                   |
| **Reads are charged**     | No                                                                   | Yes, when the grant is priced                                       |
| **Runtimes**              | Node server and PS Lite                                              | Node server only — in practice, users on the desktop app            |

The rest of this page covers the third-party path. On the owner's path the user points their own client at their Personal Server's `/mcp` URL and approves scopes in Vana; your app is not part of that flow.

## The third-party flow

```mermaid theme={null}
flowchart LR
    app["Your app<br/>holds its builder key"]:::neutral
    ps["User's Personal Server<br/>/mcp"]:::sdk

    app -->|"① POST /mcp/session<br/>Web3Signed proof + grantId"| ps
    ps -->|"bearer token (1h)"| app
    app -->|"② stock MCP client<br/>Authorization: Bearer"| ps
    ps -->|"grant-scoped tools + data"| app

    classDef sdk fill:#FFF3D1,stroke:#E6A700,color:#5c4400;
    classDef neutral fill:#EEF1F5,stroke:#9AA4B2,color:#1f2937;
```

You sign **once** to mint a session token, then connect with an unmodified MCP client. The key never leaves your backend — the Personal Server only ever recovers your address, at the handshake.

### Prerequisites

| What                           | How                                                                                                                                                                        |
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| A registered builder identity  | Your app address is registered at the [DP RPC](/protocol-reference/dp-rpc). See [App identity](/build-a-vana-app/app-identity)                                             |
| A grant issued to that address | An ordinary access request over the scopes your agent should reach. See [Grants & permissions](/protocol-reference/grants-permissions#requesting-a-grant-the-connect-flow) |
| The user's Personal Server URL | Returned with the approved access request                                                                                                                                  |
| A funded escrow                | Only if the grant is priced. See [Payments & fees](/protocol-reference/payments-fees)                                                                                      |

### Step 1 — Mint a session

```http theme={null}
POST {personalServerUrl}/mcp/session
Authorization: Web3Signed <base64url(payload)>.<signature>
```

The signed payload is the standard Personal Server request envelope — `aud`, `method`, `uri`, `bodyHash`, `iat`, `exp` — and **must carry a `grantId` claim**. It is signed EIP-191 by your app key. A bearer credential cannot open a session; the handshake exists to prove control of the key.

```json theme={null}
{
  "access_token": "vana_mcp_4b7e...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "oura.sleep chatgpt.conversations"
}
```

The handshake checks that the builder is registered, the grant exists and is not revoked, the grant's grantee is the handshake signer, and the grant's grantor is this server's owner. Per-scope enforcement stays at read time, against the live grant — a grant revoked mid-session fails the next tool call.

<Warning>Session tokens are held in memory, and the handshake proof is single-use. A Personal Server restart drops every live token; sign a fresh proof and re-handshake. Refresh as `expires_in` nears rather than waiting for a `401`.</Warning>

### Step 2 — Connect a stock MCP client

Point any streamable-HTTP MCP transport at `{personalServerUrl}/mcp` with `Authorization: Bearer <access_token>`, then `listTools()` and `callTool()` as usual. The endpoint is stateless — there is no retained MCP session, only the auth token on each request.

### The tool catalog

Every tool is grant-scoped: a scope your grant does not cover returns `scope_not_granted` and the list of scopes that are covered.

| Tool                      | What it does                                                                                                     |
| ------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| `list_granted_scopes`     | Granted scopes with readiness, size class, and a recommended access strategy. **Call this first**                |
| `list_granted_sources`    | The granted source ids, when you only need the sources                                                           |
| `list_scope_blocks`       | A scope's table of contents — block ids, paths, sizes — to feed into `read_scope`                                |
| `read_scope`              | Read approved scope blocks; page with `nextCursor`, or pass exact `blockIds`                                     |
| `search_personal_context` | Search across approved scopes; continue with `nextSearchCursor`                                                  |
| `get_scope_file`          | Fetch an approved file or PDF scope, as metadata plus a resource link by default                                 |
| `request_scope_access`    | Report which requested scopes are missing from this grant, so the agent can ask the user to approve them in Vana |

`request_scope_access` cannot grant anything. It tells you what is missing and what to ask the user for; widening access is always a fresh approval in Vana.

### Paying for reads

When the grant is priced, a chargeable read answers with an x402 challenge rather than data:

```json theme={null}
{
  "payment_required": true,
  "status": 402,
  "challenge": { "x402Version": 1, "accepts": [ { "scheme": "vana-escrow-grant", "...": "..." } ] },
  "message": "This read requires payment. Sign the x402 challenge and call read_scope again with the `payment` argument."
}
```

Sign the challenge's `GenericPayment` message with your app key — the same EIP-712 settlement the HTTP read path uses — and call the tool again with the base64 `X-PAYMENT` payload in its `payment` argument. `read_scope` and `get_scope_file` both take it.

Three behaviours to design around:

* **Search does not settle payments.** `search_personal_context` is free discovery and preview. Chargeable scopes come back in `paymentRequiredScopes`; retrieve each through `read_scope` with a payment proof.
* **A `402` that will never clear is a different error.** A genuine `PAYMENT_REQUIRED` carries a signable challenge. An empty escrow surfaces as `payment_failed` with no challenge — resolve the balance rather than signing in a loop.
* **Each cursor page and each retry is its own charge.** There is no receipt cache today, so a paginated read of one scope settles once per page. Budget for it, or read with explicit `blockIds`.

Reads under the **owner's own** OAuth connection are never charged: the payment path is built only for a self-signing session.

## Limits

* **Read-only.** Agent writes over MCP are not built. An app that needs to store a result writes it through the [Write API](/protocol-reference/write-api) instead, and an agent that should answer a question over data it may not read uses [derivative data](/protocol-reference/derivative-data).
* **Node Personal Server only** for the third-party path. The desktop app bundles the full Personal Server; the web app bundles PS Lite, which does not serve `/mcp/session`. Plan for your users to be on desktop, or check with the Vana team before committing to a launch that assumes otherwise.
* **The Personal Server must be reachable.** A browser-hosted server is unreachable while the user's Vana tab is closed. `503` also covers a server whose bounded-read or compute layer is not wired, so read the error code before deciding whether to retry.
* **Sessions do not survive a restart**, and neither does the replay memory for handshake proofs.

## Auditability

The properties that make a grant safe apply unchanged to an agent:

* **Consent is on-chain** — which app may reach which scopes, until when
* **Each scope-content read is logged** in the Personal Server's access log, attributed to your registered address; the server separately records every MCP tool call in an activity feed the owner can review
* **Each chargeable read settles a fee**, leaving an on-chain record per use
* **Revocation is immediate** — revoke the grant and the next tool call fails, without touching the session

## Related

* [Agents](/applications/agents) — why the protocol treats an agent as an ordinary grantee
* [Personal Servers](/protocol-reference/personal-servers) — what serves the MCP endpoint
* [Grants & permissions](/protocol-reference/grants-permissions) — the grant an MCP session runs under
* [Derivative data](/protocol-reference/derivative-data) — answering a question without reading the sources
* [Write API](/protocol-reference/write-api) — storing a result back in the user's Personal Server
* [Payments & fees](/protocol-reference/payments-fees) — funding the escrow a paid read settles from
