# MCP (/docs/mcp)

Scrape from an AI agent through EliseBot's remote MCP server.

The MCP server is at `https://api.elisebot.dev/mcp`, over Streamable HTTP. There is no anonymous MCP caller: a client
signs in with OAuth, or sends an organization's API key.

## Connect with OAuth

Add the URL to the client. In claude.ai and Claude Desktop, it is a custom connector under Settings → Connectors; in
Claude Code:

```sh
claude mcp add --transport http elisebot https://api.elisebot.dev/mcp
```

The client opens the [dashboard](https://app.elisebot.dev) in the browser (Claude Code through `/mcp`): log in, pick the
organization, and approve. Its calls then spend that organization's credits.

The organization's **Connected apps** page lists every approved client, and any member can revoke one there. A client
also loses access when the member who approved it leaves the organization.

## Connect with an API key

For a client without OAuth, or a headless agent, send an organization's [API key](/docs/authentication) as a Bearer
token:

```sh
claude mcp add --transport http elisebot https://api.elisebot.dev/mcp \
  --header "Authorization: Bearer $ELISEBOT_API_KEY"
```

Any other client takes the URL and the header, e.g. Cursor's `mcp.json`:

```json
{"mcpServers": {"elisebot": {"url": "https://api.elisebot.dev/mcp", "headers": {"Authorization": "Bearer <key>"}}}}
```

## Tools

Each tool mirrors one endpoint and takes its body as arguments. Credits, quotas and idempotency keys are the
organization's, shared with its REST calls.

| Tool                  | Mirrors                                                  | Returns                                                                 |
| --------------------- | -------------------------------------------------------- | ----------------------------------------------------------------------- |
| `scrape`              | [`POST /v2/scrape`](/docs/scrape)                        | The page's content, then its `metadata` as JSON.                        |
| `get_scrape`          | [`GET /v2/scrape/{id}`](/docs/scrape#status)             | The REST status, with the document as `data` once `completedAt` is set. |
| `batch_scrape`        | [`POST /v2/batch/scrape`](/docs/batch-scrape)            | The REST response; `idempotencyKey` takes the place of the header.      |
| `get_batch_scrape`    | [`GET /v2/batch/scrape/{id}`](/docs/batch-scrape#status) | The REST status, with the `package.url` to download with a shell.       |
| `cancel_batch_scrape` | [`DELETE /v2/batch/scrape/{id}`](/docs/cancel)           | The REST response.                                                      |

- `responseOptions.format` defaults to `markdown` here; REST keeps `html`.
- A page that failed is a normal result, its `metadata.errorCode` set, as it is a 200 on REST.
- A scrape past `timeout` is a normal result too: the REST 202 body as JSON, whose `id` is the scrape to read with
  `get_scrape`, which carries the document as `data` once `completedAt` is set.
- A batch scrape's package is a gzipped file, not tool output: an agent fetches `package.url` with a shell, e.g.
  `curl -L`.

## Errors

- A tool error is a result with `isError` set, carrying the same JSON as the REST [error](/docs/errors), plus
  `retryAfter` in seconds on `RATE_LIMITED`.
- A missing or unknown key, or a scheme other than Bearer, is an HTTP 401 `UNAUTHORIZED`; an unknown, expired or
  revoked OAuth token is a 401 `INVALID_ACCESS_TOKEN`. Both carry `WWW-Authenticate: Bearer`, whose
  `resource_metadata` leads an OAuth client to the sign-in.
- The server is stateless and takes only POST: `GET` and `DELETE /mcp` are 405 `METHOD_NOT_ALLOWED`. A body over
  16 MiB is a plain 413.

## Large documents

`scrape` returns the whole document, up to 10 MiB, while MCP clients cap a tool's output: Claude Code at about 25,000
tokens (`MAX_MCP_OUTPUT_TOKENS` raises it). `scrape` and `get_scrape` take `maxLength`, an MCP-only argument
that cuts the content to that many characters; the metadata then carries `truncated: true` and `totalLength`, the full
length in characters. `onlyMainContent` makes a page smaller too.
