Docs menu

Reference

MCP

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.

TradeCatalog follows the MCP authorization spec: OAuth 2.1 with PKCE, protected resource metadata (RFC 9728) and authorization server metadata (RFC 8414).

EndpointsLink to this section#

WhatURL
MCP server (protected resource)https://tradecatalog.app/mcp
Protected resource metadatahttps://tradecatalog.app/.well-known/oauth-protected-resource/mcp
Authorization server metadatahttps://tradecatalog.app/.well-known/oauth-authorization-server
Authorization endpointhttps://tradecatalog.app/oauth/authorize
Token endpointhttps://tradecatalog.app/oauth/token
Dynamic client registrationhttps://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.

ScopeLink to this section#

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

Discovery flowLink to this section#

  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 clientLink to this section#

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.

AuthorizationLink to this section#

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 approveLink to this section#

  • 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 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.

TransportLink to this section#

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.

RevokingLink to this section#

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

Also publishedLink to this section#