# OAuth for MCP clients

> How MCP clients discover, register and get tokens for the TradeCatalog MCP server with OAuth 2.1.

This page is for people building an MCP client or debugging a connection. If you only want to connect Claude or ChatGPT, see [Connect an AI app](/docs/mcp).

TradeCatalog follows the [MCP authorization spec](https://modelcontextprotocol.io/specification/latest/basic/authorization): OAuth 2.1 with PKCE, protected resource metadata (RFC 9728) and authorization server metadata (RFC 8414).

## Endpoints

| What                            | URL                                                                 |
| ------------------------------- | ------------------------------------------------------------------- |
| MCP server (protected resource) | `https://tradecatalog.app/mcp`                                      |
| Protected resource metadata     | `https://tradecatalog.app/.well-known/oauth-protected-resource/mcp` |
| Authorization server metadata   | `https://tradecatalog.app/.well-known/oauth-authorization-server`   |
| Authorization endpoint          | `https://tradecatalog.app/oauth/authorize`                          |
| Token endpoint                  | `https://tradecatalog.app/oauth/token`                              |
| Dynamic client registration     | `https://tradecatalog.app/oauth/register`                           |
| Connected apps (for people)     | `https://tradecatalog.app/oauth/connections`                        |

The protected resource metadata is at the path-specific address for `/mcp`, as RFC 9728 describes for a resource with a path. There’s deliberately nothing at `/.well-known/oauth-protected-resource` on its own.

## Scope

There’s one scope, `tradecatalog:read`. It’s read-only, and every grant gets it.

## Discovery flow

1. The client calls `https://tradecatalog.app/mcp` without a token.
2. The server answers `401 Unauthorized` with a `WWW-Authenticate: Bearer` header pointing at the protected resource metadata.
3. The client reads the protected resource metadata to find the authorization server, `https://tradecatalog.app`.
4. The client reads the authorization server metadata for the endpoints above.

```bash
curl https://tradecatalog.app/.well-known/oauth-protected-resource/mcp
```

```json
{
  "resource": "https://tradecatalog.app/mcp",
  "authorization_servers": ["https://tradecatalog.app"],
  "scopes_supported": ["tradecatalog:read"],
  "bearer_methods_supported": ["header"],
  "resource_name": "TradeCatalog MCP server"
}
```

## Registering a client

Use either:

- **Client ID metadata documents** (preferred): use an `https` URL you control as your `client_id`, serving your client’s metadata. People approving the connection see that URL’s host as the publisher.
- **Dynamic client registration** (RFC 7591) at `/oauth/register`.

Anyone can register a client under any name, so the approval page shows people where the approval is sent (the redirect host) and, for URL client ids, the publisher. The name alone is marked as unverified.

## Authorization

Use the authorization code flow with **PKCE (`S256`)**:

1. Send the person’s browser to `/oauth/authorize` with `response_type=code`, `client_id`, `redirect_uri`, `code_challenge`, `code_challenge_method=S256`, `state` and `scope=tradecatalog:read`.
2. If they aren’t signed in, TradeCatalog asks them to sign in with an emailed code (or Google or Microsoft, where enabled), then brings them back.
3. They see which app is asking, the account they’re signed in as, and what it can do, then choose **Allow** or **Deny**.
4. **Allow** redirects to your `redirect_uri` with `code` and `state`. **Deny** redirects with `error=access_denied`.
5. Exchange the code at `/oauth/token` with your `code_verifier`. You get an access token and a refresh token.

Send the access token on every MCP request:

```http
POST /mcp HTTP/1.1
Host: tradecatalog.app
Authorization: Bearer <access token>
Content-Type: application/json
Accept: application/json, text/event-stream
```

Access tokens are short-lived. Use the refresh token at `/oauth/token` to get a new one.

## Who can approve

- Any signed-in TradeCatalog user: supplier staff or buyers.
- Not demo visitors (guest sessions). They’re sent to sign in properly.
- There’s no way to create an account through this flow, and no machine-only (client credentials) access. For server-to-server access, use the [REST API](/docs/api) with an API key.

The grant carries only the person’s identity. Each tool call re-checks what they can see, so if they lose access to a workspace, the AI app loses it too.

## Transport

The server is **stateless** Streamable HTTP. It doesn’t issue session ids; each request stands alone, and it answers with JSON rather than an event stream.

## Revoking

People see and disconnect approved apps at `/oauth/connections`. Disconnecting revokes the grant and its tokens straight away.

## Also published

- [`/auth.md`](/auth.md): a short summary of this page for agents.
- [`/.well-known/mcp/server-card.json`](/.well-known/mcp/server-card.json): the MCP server card with the tool list.
