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

# Authentication

> How AI agents authenticate to the Bizzy MCP server

The Bizzy MCP server is a protected resource. Clients authenticate using **OAuth
2.1 with PKCE and Dynamic Client Registration (RFC 7591)**, following the
[MCP authorization specification](https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization).

Modern MCP clients — Claude Desktop, Claude Code, Cursor, Windsurf, and the MCP
Inspector — run the entire flow automatically. As an end user you only need to:

1. Point the client at `https://mcp.bizzyco.ai`.
2. Sign in to Bizzy when the consent screen opens in your browser.
3. Approve the consent screen, choosing which tool categories the client may
   use.

The rest of this page describes the flow in detail for client developers and for
anyone debugging a connection.

## How the flow works

```
Client → /mcp (or /sse)            (no token)
Server → 401 + WWW-Authenticate
Client → /.well-known/oauth-protected-resource/mcp   (route-scoped; host-level also available)
Client → /.well-known/oauth-authorization-server
Client → POST /oauth/register      (Dynamic Client Registration)
Client → /oauth/authorize          (browser, with PKCE code_challenge)
User   → consent screen
Server → redirect with auth code
Client → POST /oauth/token         (code + code_verifier)
Server → access_token (+ refresh_token if offline_access)
Client → /mcp or /sse              (Authorization: Bearer <token>)
```

The handshake is plain OAuth 2.1; no Bizzy-specific extensions are involved.

## Discovery

The server publishes two host-derived metadata documents. A client discovers
everything else from these.

| Endpoint                                  | Purpose                                                                                                                                  |
| ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `/.well-known/oauth-protected-resource`   | RFC 9728 protected-resource metadata. Lists the authorization server.                                                                    |
| `/.well-known/oauth-authorization-server` | RFC 8414 authorization-server metadata. Lists `/oauth/register`, `/oauth/authorize`, `/oauth/token`, supported scopes, and PKCE methods. |

You can fetch them directly:

```bash theme={null}
curl -s https://mcp.bizzyco.ai/.well-known/oauth-protected-resource | jq
curl -s https://mcp.bizzyco.ai/.well-known/oauth-authorization-server | jq
```

## 401 response shape

A request to `/mcp` or `/sse` without a valid Bearer token returns HTTP `401`
with a `WWW-Authenticate` header pointing back to a route-scoped
protected-resource metadata URL on the same host:

```http theme={null}
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="OAuth", resource_metadata="https://mcp.bizzyco.ai/.well-known/oauth-protected-resource/mcp", error="invalid_token", error_description="Missing or invalid access token"
Content-Type: application/json

{"error":"invalid_token","error_description":"Missing or invalid access token"}
```

The 401 — not a JSON-RPC error envelope — is the canonical signal that the
client should run discovery and the OAuth flow. The `resource_metadata` URL is
route-scoped (`/mcp` and `/sse` advertise their own metadata documents); a
host-level document is also available at `/.well-known/oauth-protected-resource`
for clients that prefer it.

## Dynamic Client Registration

Clients register themselves at `POST /oauth/register` per RFC 7591. Registration
is open (no admin approval required) but rate-limited to **10 requests per 60
seconds per IP**, so clients should cache the returned `client_id` (and any
`client_secret`) and reuse it across launches rather than re-registering every
time. Prefer the OS credential store (Keychain on macOS, Credential Manager on
Windows, libsecret on Linux) over a plaintext file on disk, especially for
`client_secret` values.

## PKCE

The server requires PKCE on every authorization request and only accepts the
`S256` code-challenge method. Plain PKCE is rejected — clients that send
`code_challenge_method=plain` will fail the authorize step.

## Resource indicator

The server requires an RFC 8707 `resource` parameter on every authorization
request, and its value must identify this MCP server (the same origin you
connect to, e.g. `https://mcp.bizzyco.ai`). This binds the issued access
token's audience to Bizzy so it can't be replayed against another server. An
authorize request that omits `resource`, or sends one pointing at a different
origin, is rejected with an `Invalid resource` error. Modern MCP clients derive
this from the server URL and send it automatically; custom clients must include
it.

## Scopes

Two scopes are advertised:

| Scope            | Required | Purpose                                                                                                                                                                                               |
| ---------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `mcp`            | Yes      | Access to the MCP transport endpoints.                                                                                                                                                                |
| `offline_access` | No       | Issues a refresh token alongside the access token. Request only for persistent or background clients; omit for interactive one-shot use, since the 90-day refresh token is a longer-lived credential. |

During consent the user also chooses which tool categories the client may call.
The set of tools advertised over `tools/list` is filtered against that selection
on every request.

## Token lifetime

| Token         | TTL     | Notes                                                          |
| ------------- | ------- | -------------------------------------------------------------- |
| Access token  | 60 min  | Bearer token presented on `/mcp` and `/sse`.                   |
| Refresh token | 90 days | Issued only when `offline_access` was granted; rotated on use. |

Access tokens are refreshed via `POST /oauth/token` with
`grant_type=refresh_token`. Refresh tokens rotate on every use per OAuth 2.1.
The token endpoint is rate-limited to **10 requests per 60 seconds per IP**;
cache the access token for its lifetime rather than re-exchanging on every call.

## Rate limits

Two limits apply to authenticated traffic, and they surface differently.

### Tool calls: your plan's per-minute limit

Every `tools/call` spends one request from your account's MCP allowance:

| Plan         | MCP requests per minute |
| ------------ | ----------------------- |
| Free         | 10                      |
| Starter      | 30                      |
| Professional | 100                     |
| Enterprise   | 300                     |

The limit is also the burst capacity: you can spend your full per-minute
allowance at once, and it replenishes continuously over the following minute.
MCP and API allowances are independent — heavy API traffic never consumes your
MCP capacity. See [Understanding Limits](/admin-guide/billing/limits) for how
rate limits fit into your plan.

A throttled tool call is **not** an HTTP error. The request succeeds at the
transport level and the throttle is reported in the JSON-RPC envelope, so
clients must inspect the response body rather than the status code alone:

```json theme={null}
{
    "jsonrpc": "2.0",
    "id": 1,
    "error": {
        "code": -32004,
        "message": "Rate limit exceeded",
        "data": { "retryAfter": 6 }
    }
}
```

Wait `data.retryAfter` seconds before retrying that call. Other methods
(`tools/list`, `ping`, and the rest of the protocol) do not spend from this
allowance.

### Transport: per-(user, client) backstop

Independently, `/mcp` and `/sse` accept at most **600 requests per 60 seconds
per (user, client)** as an abuse backstop. Its ceiling sits above every plan's
tool-call limit, so ordinary use never reaches it. A throttled request here is
HTTP `429` with the same JSON-RPC error body and a `Retry-After` header.

The server does not emit `X-RateLimit-*` headers on either surface.

## Reference

* [MCP authorization spec (2025-06-18)](https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization)
* [RFC 9728 — OAuth 2.0 Protected Resource Metadata](https://datatracker.ietf.org/doc/html/rfc9728)
* [RFC 8414 — OAuth 2.0 Authorization Server Metadata](https://datatracker.ietf.org/doc/html/rfc8414)
* [RFC 7591 — Dynamic Client Registration](https://datatracker.ietf.org/doc/html/rfc7591)
