# Connect an AI assistant to Superdocu (MCP)

Superdocu exposes an **MCP** (Model Context Protocol) server that lets an AI
assistant (Claude, ChatGPT, etc.) act on a consumer account: list/validate
documents, chase contacts… through the same access controls as the app.

Client-side integration is **a single URL**: everything else (OAuth, client
registration, consent) is automatic.

## For a client (production)

MCP server URL: `https://app.superdocu.com/mcp`

### Claude (Desktop or claude.ai)
1. Settings → **Connectors** → **Add custom connector**.
2. Paste `https://app.superdocu.com/mcp`.
3. A window opens: sign in to Superdocu and **authorize** the requested
   accesses (shown in clear).
4. Connected. The assistant can call the Superdocu tools.

### ChatGPT
Settings → **Connectors** → add an MCP connector with the same URL.

### Any other MCP client
Point the client at `https://app.superdocu.com/mcp`. It discovers the
authorization server via the standard endpoints
(`/.well-known/oauth-protected-resource`,
`/.well-known/oauth-authorization-server`), registers itself (DCR, RFC 7591)
and runs the OAuth 2.1 + PKCE flow.

No config file, no API key to copy: that's by design.

## What happens behind the scenes (OAuth 2.1)

```
client → GET /.well-known/...                discovery
       → POST /oauth/register                registration (DCR, public client)
       → /oauth/authorize (browser)          consumer login + consent screen (PKCE S256)
       → POST /oauth/token                   exchange code → JWT (aud = .../mcp, 1h, refresh)
       → POST /mcp (Bearer <JWT>)            MCP session
```

The consent screen lists the requested scopes in clear (e.g. "See every
contact in your company", "Review, approve and reject documents") as
checkboxes — you can **uncheck any access you don't want to grant** before
authorizing.

## Available tools

The server only exposes to each assistant the tools covered by the scopes it
has authorized (and a tool also requires the matching consumer role
permission). The surface mirrors the REST API v2 one-to-one.

Always on:

| Tool | Required scope | Effect |
|---|---|---|
| `me_show` | `mcp` | Consumer identity + permissions + scopes |

`see_all_contacts` and/or `manage_contacts` (contacts surface):

| Tool | Required scope | Effect |
|---|---|---|
| `contact_list` / `contact_show` | `see_all_contacts` or `manage_contacts` | List / read contacts |
| `contact_workflow_list` / `contact_workflow_show` | `see_all_contacts` or `manage_contacts` | List / read assigned workflows |
| `workflow_list` / `workflow_show` | `see_all_contacts` or `manage_contacts` | List / read workflow templates |
| `contact_tag_list` | `see_all_contacts` or `manage_contacts` | List company tags |
| `shared_document_list` / `shared_document_url` | `see_all_contacts` or `manage_contacts` | List / signed URL of shared documents |
| `contact_timeline_global` / `contact_timeline_contact` | `see_all_contacts` | Activity timelines |
| `contact_download` / `contact_workflow_download` / `download_show` | `see_all_contacts` or `manage_contacts` | Schedule and poll ZIP exports |
| `contact_create` / `contact_update` / `contact_delete` | `manage_contacts` | Create / update / delete a contact |
| `contact_invite` / `contact_enable` / `contact_disable` | `manage_contacts` | Invite / enable / disable a contact |
| `contact_remind` / `reminder_options` / `reminder_list` | `manage_contacts` | Send / inspect / list reminders |
| `contact_workflow_create` / `update` / `archive` / `unarchive` / `delete` | `manage_contacts` | Manage a contact's workflows |
| `contact_tag_create` / `contact_tag_delete` | `manage_contacts` | Create / delete a tag |
| `shared_document_update` / `update_position` / `delete` | `manage_contacts` | Manage shared documents |

`validate_documents` (validation surface):

| Tool | Required scope | Effect |
|---|---|---|
| `dashboard_show` | `see_all_contacts` + `validate_documents` | Triage stats + pending actions |
| `documents_group_show` / `step_request_show` | `validate_documents` | Read a document group / step request |
| `documents_group_approve` / `request` / `mark_not_applicable` / `approve_not_applicable` / `refuse_not_applicable` / `update_expiration` / `delete` | `validate_documents` | Validate a document group |
| `document_approve` / `reject` / `url` / `delete` | `validate_documents` | Validate a single document |
| `step_request_approve` / `reject` / `update` | `validate_documents` | Validate / correct a form answer |
| `custom_documents_group_create` | `validate_documents` | Add a custom document group to a state |

File uploads (importing contacts, uploading documents) are not exposed over
MCP: the protocol is JSON-RPC with no multipart, so binary input stays on the
REST API. Outbound files are returned as short-lived signed URLs
(`*.url`, `*.download` + `download_show`).

These tools mirror the REST API v2: same organizers, same access controls, same
data shapes. For ids, payloads, error codes and the end-to-end validation
workflow, read the [API v2 tutorial](https://developers.superdocu.com/api-v2/tutorial).

## Errors

A failed call comes back as an MCP **tool error** (`isError: true`), not a
JSON-RPC protocol error: the result is a text block holding a JSON object with a
machine-readable `code`, the `resource`, and a human-readable `detail` (some
codes add fields). This is a different shape from the REST JSON:API error
envelope — handle it on `code`.

| `code` | Meaning |
|---|---|
| `insufficient_scope` | The token was never granted a scope this tool needs — re-authorize the connection (see below). |
| `permission_denied` | The scope is granted but the consumer's role lacks the permission — an account admin must grant it. |
| `not_found` | No record with that `id` is visible to this consumer. |
| `missing_required_attributes` | A required attribute is absent; the missing names are echoed in `missing`. |
| `validation_failed` | The record was rejected; reasons are in `detail`. |
| `rate_limited` | A per-token quota was hit (see Rate limits) — `limit` and `retry_after` say how long to wait. |
| `invalid_cursor` | A `list` got a malformed `after`/`before` cursor — drop it to restart from the first page. |
| a precondition reason (e.g. `not_rejectable`, `already_archived`, `active_contacts_limit_reached`) | The record isn't in a state that allows the action; read its `available_actions` first. |

## Rate limits

The tools that email a contact or schedule a file export are rate-limited **per
access token**, on the same budgets as the REST API v2 — and counted separately
from it (each token gets its own allowance):

| Tools | Limit |
|---|---|
| `contact_invite`, `contact_remind`, `document_reject`, `step_request_reject`, `documents_group_request` (anything that emails a contact) | 30 / min |
| `contact_download`, `contact_workflow_download` (ZIP exports) | 10 / min |

Over budget, the call returns a tool error with code `rate_limited`, a
human-readable `detail`, the applied `limit` and a `retry_after` in seconds —
wait that long, then retry. Every other tool (reads, generic writes,
`download_show` polling) is only bounded by a generous per-IP fallback.

## Granting more access later (new scopes)

Scopes are **fixed when the connection is authorized** — they are baked into the
access token. A live connection never gains a scope retroactively, and a tool
gated by a scope you didn't grant simply won't appear in the tool list.

When Superdocu adds a new scope (or you want to grant one you skipped):

1. The new scope is advertised in the discovery metadata (`scopes_supported`),
   but your existing connection keeps its original scopes.
2. To obtain it, **re-authorize the connection**. In Claude/ChatGPT, remove and
   re-add the Superdocu connector: the client re-registers, and the consent
   screen lets you approve the additional scopes.
3. The new tools then appear on the next connection with the upgraded token.

The assistant is told this via the server instructions, so it can guide you when
a capability is missing. Note: even with the right scope, a tool also requires
the matching **role permission** on the consumer account — otherwise the call
returns `permission_denied`, which an account admin must resolve.
