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

# MCP OAuth Flow

> OAuth 2.1 + Dynamic Client Registration for building custom MCP clients against Mokaru

<Note>
  This page is only relevant if you're **building** an MCP client. End users don't need to know any of this - their MCP client handles the OAuth flow automatically.
</Note>

## Overview

Mokaru's MCP server implements OAuth 2.1 (RFC 6749, RFC 9700) with:

* **PKCE** (RFC 7636, S256 only - `plain` is rejected)
* **Dynamic Client Registration** (RFC 7591)
* **Authorization Server Metadata** (RFC 8414)
* **Refresh Token Rotation** with reuse detection

## Discovery

```http theme={null}
GET https://api.mokaru.ai/.well-known/oauth-protected-resource
```

Returns:

```json theme={null}
{
  "resource": "https://api.mokaru.ai/mcp",
  "authorization_servers": ["https://api.mokaru.ai"],
  "bearer_methods_supported": ["header"]
}
```

Then:

```http theme={null}
GET https://api.mokaru.ai/.well-known/oauth-authorization-server
```

Returns:

```json theme={null}
{
  "issuer": "https://api.mokaru.ai",
  "authorization_endpoint": "https://api.mokaru.ai/oauth/authorize",
  "token_endpoint": "https://api.mokaru.ai/oauth/token",
  "registration_endpoint": "https://api.mokaru.ai/oauth/register",
  "scopes_supported": ["mcp"],
  "response_types_supported": ["code"],
  "grant_types_supported": ["authorization_code", "refresh_token"],
  "token_endpoint_auth_methods_supported": ["none"],
  "code_challenge_methods_supported": ["S256"]
}
```

## Step 1: Register your client

```http theme={null}
POST https://api.mokaru.ai/oauth/register
Content-Type: application/json

{
  "client_name": "My MCP Client",
  "redirect_uris": ["https://my-client.example.com/oauth/callback"],
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"]
}
```

Returns:

```json theme={null}
{
  "client_id": "mcp_abc123...",
  "client_name": "My MCP Client",
  "redirect_uris": ["..."],
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "token_endpoint_auth_method": "none",
  "client_id_issued_at": 1716220800
}
```

**Allowed redirect URIs**: `https://` URIs or `http://localhost` / `http://127.0.0.1` loopback. No `http://` on public hosts.

## Step 2: Authorize

Generate a PKCE pair:

```js theme={null}
const codeVerifier = base64UrlEncode(crypto.randomBytes(32));      // 43 chars
const codeChallenge = base64UrlEncode(sha256(codeVerifier));        // 43 chars
```

Redirect the user's browser to:

```
https://api.mokaru.ai/oauth/authorize?
  response_type=code
  &client_id=mcp_abc123...
  &redirect_uri=https://my-client.example.com/oauth/callback
  &code_challenge=<challenge>
  &code_challenge_method=S256
  &scope=mcp
  &state=<random>
```

Mokaru shows the consent screen. After the user approves:

```
https://my-client.example.com/oauth/callback?code=<code>&state=<state>
```

**Errors**: If the request is malformed or the user is not on a Plus plan, Mokaru shows an HTML error page (does not redirect, per OAuth 2.1).

## Step 3: Exchange code for tokens

```http theme={null}
POST https://api.mokaru.ai/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=<code>
&redirect_uri=https://my-client.example.com/oauth/callback
&client_id=mcp_abc123...
&code_verifier=<verifier>
```

Returns:

```json theme={null}
{
  "access_token": "eyJhbGc...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "eyJhbGc...",
  "scope": "mcp"
}
```

## Step 4: Call /mcp

Use the access token as a Bearer:

```http theme={null}
POST https://api.mokaru.ai/mcp
Authorization: Bearer eyJhbGc...
Content-Type: application/json

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/list"
}
```

## Step 5: Refresh

```http theme={null}
POST https://api.mokaru.ai/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token
&refresh_token=eyJhbGc...
&client_id=mcp_abc123...
```

Returns a new access + refresh token pair. The old refresh token is invalidated immediately.

**Reuse detection**: If you try to use a refresh token that has already been rotated, Mokaru detects this as a possible compromise and **revokes every refresh token for that account**. Treat your refresh tokens like passwords.

## Token format

Access and refresh tokens are JWTs (HS256) with these claims:

```json theme={null}
{
  "iss": "mokaru-api",
  "aud": "mokaru-mcp",
  "sub": "<accountId>",
  "client_id": "mcp_abc123...",
  "scope": "mcp",
  "token_use": "access",
  "iat": 1716220800,
  "exp": 1716224400
}
```

**Do not attempt to verify the signature client-side** - the signing key is server-only. Treat tokens as opaque.

## Error responses

OAuth errors follow RFC 6749:

```json theme={null}
{ "error": "invalid_grant", "error_description": "PKCE verification failed" }
```

Common error codes:

| Code                        | Meaning                                                     |
| --------------------------- | ----------------------------------------------------------- |
| `invalid_request`           | Missing or malformed parameter                              |
| `invalid_client`            | Unknown `client_id`                                         |
| `invalid_grant`             | Code/refresh expired, consumed, or PKCE failed              |
| `unauthorized_client`       | Client cannot use this grant type                           |
| `unsupported_grant_type`    | Only `authorization_code` and `refresh_token` are supported |
| `unsupported_response_type` | Only `code` is supported                                    |

## Reference implementations

* [MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk) handles all of this automatically when given just the server URL.
* [MCP Python SDK](https://github.com/modelcontextprotocol/python-sdk) ditto.
* Claude Desktop, Claude.ai, Cursor, Continue: built-in MCP OAuth support.
