# Connect agents

> Let your users connect Claude, Codex or any MCP client to their documents, with your sign-in on the consent page and Proof crediting every word the agent writes.

Your users can bring their own AI agents to the documents they keep in your app. An agent reads, suggests, edits and comments through Proof's MCP server, and everything it writes is credited to it on the record and in the seal.

You build two things: a page that tells people how to connect an agent, and a consent page where they approve one. Proof does the rest. It runs the MCP server and the OAuth sign-in, issues the agent's tokens, and checks what the agent may do on every call. Your server never holds an agent's token, which is why an agent's words can't be passed off as a person's, or a person's as an agent's.

## How connecting works

Each platform has its own MCP address. For Acme Notes it is:

```
https://mcp.proof.exalto.ai/acme
```

In test mode it is `https://mcp.proof.exalto.ai/acme/test`, with your test consent URL. Your server can read the address for the key's mode as `mcp_url` from [`GET /v1/platform`](/reference/api/platform/).

When someone adds that address to their agent:

<Steps>

1. The agent calls Proof, learns it needs to sign in, and registers itself with Proof's OAuth server. Proof checks where the agent comes from: its **host**, such as `claude.ai`, which the agent can't claim falsely.
2. The agent opens the person's browser at Proof, which sends it straight on to your **consent URL** with a request id: `https://acme.com/proof/connect?request=areq_5tNc81`.
3. Your consent page signs the person in, as any page of your app would, and shows which agent is asking.
4. When they click **Allow**, your server approves the request with your secret key, naming the signed-in user. Proof answers with a URL, and you send the browser there.
5. Proof finishes the sign-in and gives the agent its tokens. From then on the agent talks only to Proof, as your user's agent.

</Steps>

Tokens for one platform's address don't work on another's. Someone who uses two platforms built on Proof connects their agent to each separately, and the agent keeps the two apart.

## Set your consent URL

In the console, under **Agents**, or with [`PATCH /v1/platform`](/reference/api/platform/), enter the URL of your consent page, such as `https://acme.com/proof/connect`, for each mode. Until you set one, Proof answers every connection attempt with an error telling the person that this platform doesn't accept agents yet.

## Show people how to connect

Give your settings an Agents section that shows your MCP address and how to add it to the agents people use most. The address is the same for all your users; who they are is settled on the consent page.

<Tabs>
  <TabItem label="Claude Code">
    ```bash
    claude mcp add --transport http acme https://mcp.proof.exalto.ai/acme
    ```
    Then run `/mcp` inside Claude Code and choose **acme** to sign in.
  </TabItem>
  <TabItem label="Codex">
    ```bash
    codex mcp add acme --url https://mcp.proof.exalto.ai/acme
    codex mcp login acme
    ```
  </TabItem>
  <TabItem label="VS Code">
    ```bash
    code --add-mcp '{"name":"acme","type":"http","url":"https://mcp.proof.exalto.ai/acme"}'
    ```
    VS Code asks to sign in the first time the agent uses the server. A `vscode:mcp/install?…` link with the same JSON does this in one click from your settings page.
  </TabItem>
  <TabItem label="Gemini CLI">
    ```bash
    gemini mcp add --transport http acme https://mcp.proof.exalto.ai/acme
    ```
    Then run `/mcp auth acme` inside Gemini CLI to sign in.
  </TabItem>
  <TabItem label="Other clients">
    Any client that speaks MCP over Streamable HTTP with OAuth works. Add a server with the URL `https://mcp.proof.exalto.ai/acme`; the client finds the sign-in on its own.
  </TabItem>
</Tabs>

In every case, signing in opens the person's browser on your consent page.

Name the server after your product (`acme` above). Agents show tools grouped by server, so your users see `acme` rather than a name they don't recognise.

## Build the consent page

The consent page is a normal page in your app, behind your normal sign-in.

<Steps>

1. **Look up the request.** Proof tells you which agent is asking. `client.host` is verified; `client.name` is what the agent calls itself.

    ```ts title="connect.ts"
    app.get("/proof/connect", requireSignIn, async (req, res) => {
      const request = await proof.agentRequests.retrieve(String(req.query.request));
      const documents = await proof.users.listDocuments(req.user.proofUserId);

      res.render("connect-agent", {
        requestId: request.id,
        agentName: request.client.name, // "Claude Code"
        agentHost: request.client.host, // "claude.ai"
        documents: documents.data,
      });
    });
    ```

    If the person isn't signed in, `requireSignIn` should send them through your sign-in and back to this URL, query string included. Requests last 10 minutes; an expired one returns `409 request_expired`, and the person starts again from their agent.

2. **Ask the person.** Show the agent's name and host together, such as "Claude Code (claude.ai) wants to work in your Acme Notes documents", and let them choose what it reaches:
    - **All documents**: every document they can open, editing at most. This is what most people want.
    - **Chosen documents**: only the ones they pick, each as an editor, suggester or viewer.

    Keep it to one screen with **Allow** and **Deny**.

3. **Approve or deny on your server.** Always name the signed-in user from your session, never from the form.

    ```ts title="connect.ts"
    app.post("/proof/connect", requireSignIn, verifyCsrf, async (req, res) => {
      const { requestId, decision, access, documents } = req.body;

      if (decision !== "allow") {
        const { redirect_url } = await proof.agentRequests.deny(requestId);
        return res.redirect(303, redirect_url);
      }

      const { redirect_url } = await proof.agentRequests.approve(requestId, {
        user: req.user.proofUserId,
        access, // "all" or "chosen"
        documents, // with "chosen": { "doc_8Hq2Lm4R": "suggester", ... }
      });
      res.redirect(303, redirect_url);
    });
    ```

4. **Send the browser on.** The `redirect_url` returns the person to Proof, which finishes the sign-in and hands them back to their agent. The page they land on tells them they can close it.

</Steps>

:::caution
Approve only from a `POST` your own page sent, with your usual cross-site request protection. A request id alone proves nothing about who is approving it: someone could send another person a link to your consent page with their own request id. Showing the verified host, and asking before approving, is what protects your users.
:::

## Let people manage their agents

List a person's agents in the same Agents section, with what each reaches and a way to remove it.

```ts
const agents = await proof.users.listAgents("usr_5Kq2Lm8R");
// [{ id: "agt_Qe71", name: "Claude Code", host: "claude.ai", access: "all",
//    connected_at: "2026-10-29T16:02:11Z", last_used_at: "2026-10-30T09:14:52Z" }]
```

Show the host beside the name: two agents can share a name, but not a host.

**Remove an agent.** Its tokens stop working at once, mid-task too.

```ts
await proof.agents.revoke("agt_Qe71");
```

**Narrow what it reaches.** Switch it to chosen documents, then give it a role in each:

```ts
await proof.agents.update("agt_Qe71", { access: "chosen" });
await proof.agents.putDocument("agt_Qe71", "doc_8Hq2Lm4R", { role: "suggester" });
await proof.agents.deleteDocument("agt_Qe71", "doc_3pXw9KdA");
```

A good place for per-document choices is your Share dialog: a row for each of the person's agents, with the same roles you offer people. Whatever you set, the agent never gets more than its person's own role in that document. See [Share documents](/guides/share-documents/#what-agents-get).

## Hear about agents

Two webhook events keep your Agents section current without polling:

- `agent.connected` when someone approves an agent they hadn't connected before.
- `agent.revoked` when an agent is removed, by your call or because its user was deleted.

See [Send notifications](/guides/notifications/) for receiving events, and the [event reference](/reference/webhooks/) for their fields.

## What agents see

Agents get Proof's document tools: listing, searching, reading, writing in Markdown, suggesting, and commenting. They're told to suggest unless their person asked them to edit directly. Each edit is credited with the agent's name and its person's, such as "Claude Code (Ada Lovelace's agent)", in the margin, in the record and in the seal.

Folders, tags and anything else your app keeps itself aren't in Proof's tools. To give agents those, register tools of your own, which Proof's server lists beside its own and forwards to you; see [Add your own agent tools](/guides/your-own-agent-tools/).

## Next

- [MCP tools](/reference/mcp/): every tool and what it may do.
- [Agents](/concepts/agents/): how agents are credited and limited.
- [Agent requests](/reference/api/agent-requests/) and [Agents](/reference/api/agents/) in the API reference.