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

# Configure OAuth 2.1 discovery

> Configure the Atoti MCP Server as an OAuth 2.1 resource server that publishes RFC 9728 Protected Resource Metadata, enabling MCP clients (Claude Code, Cursor, Cline, Gemini CLI) to drive browser-based PKCE authentication against a corporate identity provider such as Okta, Entra ID, or Keycloak.

<Info>
  ### Atoti Intelligence SDK

  This is part of the Atoti Intelligence SDK offer.
</Info>

This guide explains how to enable the OAuth 2.1 / PKCE browser-SSO flow on the Atoti MCP server,
so MCP clients (Claude Code, Claude Desktop, Cursor, Cline, VS Code's MCP support, Gemini CLI)
can drive authentication against your IdP without users copy-pasting Bearer tokens.

## What this enables

When this feature is enabled, the MCP server:

1. Publishes an [RFC 9728](https://datatracker.ietf.org/doc/html/rfc9728)
   **Protected Resource Metadata** document at `/.well-known/oauth-protected-resource`. The
   document tells clients which authorization server(s) to talk to and which scopes to request.
2. Returns `WWW-Authenticate: Bearer realm="mcp", resource_metadata="..."` on 401 responses
   from `/mcp/**` and `/sse`, so clients can discover the metadata document on first contact.

Compliant MCP clients then drive the standard
[OAuth 2.1 authorization code + PKCE](https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization)
flow against the IdP: the client opens the system browser at the IdP authorization endpoint, the
user signs in (with MFA, conditional access, whatever the IdP enforces), the IdP redirects back to
a loopback URI, and the client swaps the code for an access token. The token is stored in the OS
keychain and refreshed silently in the background. Users SSO once in a browser instead of
copy-pasting tokens into their MCP client configuration.

## Prerequisites

* The Atoti MCP server is already running. See
  [Atoti MCP server setup guide](./atoti-mcp-server-setup).
* An OAuth 2.0 authorization server (Okta, Entra ID, Auth0, Keycloak, Cognito, ...) is configured
  to issue access tokens for users who should be able to call MCP endpoints.
* The Atoti Spring Security stack is already configured to validate the IdP's JWTs (this is the
  same setup used by other Bearer-secured Atoti endpoints — no extra steps).

## Enable the feature

Add the following to your `application.yml`:

```yaml theme={"languages":{"custom":["/engine/python-sdk/0.9/languages/pycon.tmLanguage.json"]}}
atoti:
  server:
    endpoint:
      mcp:
        oauth2:
          enabled: true
          authorization-servers:
            - https://idp.example.com/realms/atoti
          scopes-supported:
            - mcp.read
            - mcp.write
```

`authorization-servers` is the only required setting when `enabled=true`. Startup fails with a
clear error if it is left empty.

## Property reference

| Property                                                    | Type            | Default                                 | Description                                                                                                                                                                                                           |
| ----------------------------------------------------------- | --------------- | --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `atoti.server.endpoint.mcp.oauth2.enabled`                  | boolean         | `false`                                 | Master switch. When `false`, none of the discovery beans are registered and MCP endpoints behave exactly as before.                                                                                                   |
| `atoti.server.endpoint.mcp.oauth2.resource`                 | string (URI)    | request-derived                         | Absolute URI identifying this MCP server as an OAuth 2.0 resource. When unset, derived from the request's context path at serve time. Set this explicitly if the server is behind a reverse proxy that rewrites URLs. |
| `atoti.server.endpoint.mcp.oauth2.authorization-servers`    | list of URIs    | `[]`                                    | URLs of trusted authorization servers. At least one is required when the feature is enabled.                                                                                                                          |
| `atoti.server.endpoint.mcp.oauth2.scopes-supported`         | list of strings | `[]`                                    | Optional OAuth 2.0 scopes advertised to clients. Omitted from the payload when empty.                                                                                                                                 |
| `atoti.server.endpoint.mcp.oauth2.bearer-methods-supported` | list of strings | `["header"]`                            | Supported Bearer token transport methods per RFC 6750.                                                                                                                                                                |
| `atoti.server.endpoint.mcp.oauth2.resource-documentation`   | string (URI)    | *unset*                                 | Optional URL pointing at human-readable documentation for the resource.                                                                                                                                               |
| `atoti.server.endpoint.mcp.oauth2.realm`                    | string          | `"mcp"`                                 | Realm used in the `WWW-Authenticate: Bearer realm="..."` challenge.                                                                                                                                                   |
| `atoti.server.endpoint.mcp.oauth2.well-known-path`          | string          | `/.well-known/oauth-protected-resource` | Path under which the metadata document is served. Override only if another resource server on the same origin already owns the default path.                                                                          |

## IdP setup

Concrete steps vary by IdP, but the broad shape is:

1. Configure your IdP to issue access tokens for the MCP client (Claude Desktop, Claude Code,
   Cursor, etc.). Most enterprise IdPs require the client to be pre-registered; some support
   [Dynamic Client Registration (RFC 7591)](https://datatracker.ietf.org/doc/html/rfc7591).
2. Set the resource/audience claim on the issued tokens to match
   `atoti.server.endpoint.mcp.oauth2.resource`. Atoti's JWT validator must already trust this
   audience.
3. Allow the redirect URI used by your client. Most MCP clients use a random loopback URI such
   as `http://127.0.0.1:<random-port>/callback`.

## Per-client UX

* **Claude Code, Claude Desktop, Cursor, Cline, VS Code MCP, Gemini CLI**: detect the
  `WWW-Authenticate` header automatically, open the system browser for the PKCE flow, store the
  resulting access and refresh tokens in the OS keychain, and refresh silently.
* **Older or stdio-only MCP clients**: use the
  [`mcp-remote`](https://github.com/geelen/mcp-remote) bridge — it performs the PKCE dance locally
  and proxies SSE to the remote MCP server.

## Reverse-proxy caveats

If the Atoti server sits behind a reverse proxy that rewrites the host or scheme:

* Either set `atoti.server.endpoint.mcp.oauth2.resource` explicitly to the public URL of the
  server, or
* Enable Spring Boot's `ForwardedHeaderFilter` (via `server.forward-headers-strategy=framework`)
  so the request-derived URL honours `X-Forwarded-*`.

Without one of these, the `resource` field and the `resource_metadata=` URL in the challenge
header will reflect the internal hostname instead of the public one.

## Limitations and out-of-scope features

In `external` mode the MCP server is only a resource server — it advertises discovery metadata and
validates Bearer tokens, but it does not issue them. The following are therefore out of scope **for
`external` mode** (they are the IdP's responsibility):

* **Dynamic Client Registration (RFC 7591).** Customers pre-register MCP clients in their IdP, or
  rely on the IdP's own DCR support.
* **`/.well-known/oauth-authorization-server`.** That endpoint is served by the IdP.

If you want Atoti itself to handle client registration and serve the authorization-server metadata,
use [self-issued mode](./configure-oauth2-self-issued) instead — it implements both.

The following are not implemented in either mode in this release:

* **Token Exchange (RFC 8693).** The MCP server does not exchange tokens for downstream
  services.
* **Per-error-code `WWW-Authenticate` parameters** (`error=invalid_token`, etc., per RFC 6750).
  The v1 challenge contains only `realm` and `resource_metadata`.

## Related reading

* [Atoti MCP server setup guide](./atoti-mcp-server-setup)
* [Connect with Claude](./connect-with-claude)
* [RFC 9728: OAuth 2.0 Protected Resource Metadata](https://datatracker.ietf.org/doc/html/rfc9728)
* [MCP authorization spec (2025-06-18)](https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization)
