<#import "/templates/guide.adoc" as tmpl>
<#import "/templates/links.adoc" as links>

<@tmpl.guide
title="Integrating with Model Context Protocol (MCP)"
priority=115
summary="Using {project_name} as an authorization server for Model Context Protocol (MCP) servers.">

There are currently four versions of the Model Context Protocol (MCP) specification:

* 2025-11-25 (latest version)
* 2025-06-18
* 2025-03-26
* 2024-11-05 (initial version)

The initial version (2024-11-05) does not cover authorization, as such is not covered in this guide.

This guide shows you the following:

* Which MCP version {project_name} supports.
* How to set up {project_name} as an authorization server in MCP.

However, the guide does not cover everything you need to know. Therefore, you are recommended to read the authorization section of the relevant MCP version as well.

== Standards Compliance MCP requires

According to the https://modelcontextprotocol.io/specification/draft/basic/authorization#standards-compliance[MCP specification], there are several standards regarding an authorization server in MCP. The following table shows:

* Which MCP version requires an authorization server to support which standards in which level (MUST, SHOULD, MAY).
* With which standards {project_name} complies.

[%autowidth]
|======
|Standard |2025-11-25 |2025-06-18 |2025-03-26 |{project_name}

| https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-14[The OAuth 2.1 Authorization Framework (Internet Draft)]
| MUST
| MUST
| MUST
| Supported

| https://datatracker.ietf.org/doc/html/rfc8414[OAuth 2.0 Authorization Server Metadata (RFC 8414)]
| MUST
| MUST
| MUST
| Supported

| https://datatracker.ietf.org/doc/html/rfc8707[Resource Indicators for OAuth 2.0 (RFC 8707)]
| MUST
| MUST
| -
| Not supported

| https://datatracker.ietf.org/doc/html/rfc7591[OAuth 2.0 Dynamic Client Registration Protocol (RFC 7591)]
| MAY
| SHOULD
| SHOULD
| Supported

| https://datatracker.ietf.org/doc/html/draft-ietf-oauth-client-id-metadata-document-00[OAuth Client ID Metadata Document (Internet Draft)]
| SHOULD
| -
| -
| Supported

|======

WARNING: The OAuth Client ID Metadata Document support in {project_name} is an experimental feature. It may introduce breaking changes in future versions of {project_name}.

The MCP specification adopts https://datatracker.ietf.org/doc/html/rfc9728[OAuth 2.0 Protected Resource Metadata (RFC 9728)]. The standard is for an MCP server and not for an authorization server like {project_name}. Therefore, it is not included in the above table.

== MCP version compliance

In this guide, as criteria for compliance, "{project_name} supports MCP" means that {project_name} meets all MUST and SHOULD requirements by MCP.

According to these criteria, the following table shows which MCP version {project_name} supports.

[%autowidth]
|===
|MCP Version |Conformance

| https://modelcontextprotocol.io/specification/2025-03-26/basic/authorization[2025-03-26]
| Supported

| https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization[2025-06-18]
| Partially Supported without Resource Indicators for OAuth 2.0

| https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization[2025-11-25]
| Partially Supported without Resource Indicators for OAuth 2.0

|===

== Setup

=== For MCP 2025-03-26

No special setup is required.

=== For MCP 2025-06-18 and 2025-11-25

==== Token Audience Binding and Validation

To gain security benefit, the MCP specification requires an access token to be bound with its audience. In order to do so, the MCP specification requires the following:

* An MCP client MUST include the `resource` parameter defined in https://datatracker.ietf.org/doc/html/rfc8707[Resource Indicators for OAuth 2.0 (RFC 8707)] in an authorization request and token request. The parameter's value MUST identify an MCP server that the MCP client intends to use the token with.

* An MCP server MUST validate that tokens presented to them were specifically issued for their use.

The MCP specification does not describe how to do this binding. One method for the binding is to set a value of `resource` parameter to an `aud` claim in an access token. However, {project_name} cannot recognize `resource` parameter.

The Keycloak community is planning to support Resource Indicators for OAuth 2.0 (RFC 8707) to {project_name} to make {project_name} recognize and process the `resource` parameter as the MCP specification expects. Until this support is completed, you can use OAuth 2.0's `scope` parameter instead of the `resource` parameter. To show the binding, please consider the following situation:

* An MCP server's URL is `+https://example.com/mcp+`
* The MCP supports the following three scopes: `mcp:tools`, `mcp:prompts` and  `mcp:resources`.
* To get an access token for accessing the MCP server, an MCP client sends to {project_name} an authorization request whose `resource` parameter value is `+https://example.com/mcp+` and `scope` parameter includes any combination of the three scopes.
* We want {project_name} to issue an access token whose `aud` claim's value is MCP server's URL, namely `+https://example.com/mcp+`.

To make {project_name} issue such the access token, we could configure {project_name} as follows:

* Add a client scope `mcp:tools` whose type is `Optional`.
* Add to the client scope a new `Audience` mapper whose `Included Custom Audience` field is `+https://example.com/mcp+`.
* Add a client scope `mcp:prompts` whose type is `Optional`.
* Add to the client scope a new `Audience` mapper whose `Included Custom Audience` field is `+https://example.com/mcp+`.
* Add a client scope `mcp:resources` whose type is `Optional`.
* Add to the client scope a new `Audience` mapper whose `Included Custom Audience` field is `+https://example.com/mcp+`.

Please not that the client scope's `Included Custom Audience` field needs to be the same as the authorization request's `resource` parameter value and the MCP server's URL.

With the configuration, if the MCP client send to {project_name} an authorization request whose `resource` parameter value is `+https://example.com/mcp+` and `scope` parameter includes `mcp:resources`, `mcp:tools` and `mcp:prompts`, {project_name} can issue the following access token:

```json
{
  ...
  "aud": "https://example.com/mcp",
  "scope": "mcp:resources mcp:tools mcp:prompts"
  ...
}
```

=== MCP Inspector integration

If you want to use https://github.com/modelcontextprotocol/inspector[MCP Inspector], an official debugging tools for MCP server, with {project_name} as an authorization server, you need to do an appropriate setup regarding CORS on {project_name}'s' client registration endpoint because MCP Inspector executes JavaScript downloaded from the MCP Inspector's backend server to register an MCP client dynamically to {project_name}.

You need to do an appropriate setup for Client Registration's anonymous access policies as follows:

* Allowed Client Scopes: Needs to include scopes supported by an MCP server.
* Allowed Registration Web Origins: Needs to include web origin of MCP inspector's backend server.
* Trusted Hosts: Needs to include hostname or IP address of the machine that sends a dynamic client registration request to {project_name}, namely the machine your browser runs on.

=== For MCP 2025-11-25

==== Client Registration

According to https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization#client-registration-approaches[Client Registration Approaches] section of the MCP specification, the following three client registration mechanisms are supported and you can choose based on your scenario:

* Client ID Metadata Documents: When client and server have no prior relationship (most common)
* Pre-registration: When client and server have an existing relationship
* Dynamic Client Registration: For backwards compatibility or specific requirements

{project_name} supports OAuth Client ID Metadata Document. To use Client ID Metadata Documents, you need to enable the feature and set up a client policy so that {project_name} processes the `client_id` parameter formatted as a URL and fetches the client metadata from that URL.

WARNING: The OAuth Client ID Metadata Document support is an experimental feature in {project_name}. As such, it may introduce breaking changes in future versions of {project_name}. To enable it, start {project_name} with `--features=cimd`.

===== Setting up the client profile for OAuth Client ID Metadata Document

To process an authorization request whose `client_id` metadata is a URL pointing to a Client ID Metadata Document, you need to create the profile including `client-id-metadata-document` executor.

To configure the executor, create a client policy profile in the {project_name} Admin Console:

. Navigate to *Realm Settings* -> *Client Policies* -> *Profiles* tab.
. Click *Create client profile*.
. Give the profile a name such as `cimd-profile` and click *Save*.
. Click *Add executor* and select `client-id-metadata-document` from the list.
. Configure the executor with the following options:
+
* *Allow http scheme*: If `ON`, allows `http` scheme for the Client ID URL and Client Metadata URLs (e.g., `client_uri`, `logo_uri`, `tos_uri`, `policy_uri`, `jwks_uri`). This should only be `ON` in a development environment and must be `OFF` in a production environment.
* *Trusted domains*: A list of domain patterns (wildcard) that the executor accepts for the Client ID URL and Client Metadata URL properties. For example, use `*.example.org` to accept any subdomain of `example.org`. If empty, all domains are denied.
* *Restrict same domain*: If `ON`, the executor verifies that the Client ID URL and Redirect URI in an authorization request, as well as URL-valued properties of the client metadata, are all under the same trusted domain.
* *Required properties*: A list of client metadata properties that must be present in the Client ID Metadata Document. If the fetched document does not include all the listed properties, the request is rejected.
* *Only Allow Confidential Client*: If `ON`, the executor only accepts a Client Metadata Document representing a confidential client. In this case, the client metadata must include either a `jwks` or `jwks_uri` property and must use `private_key_jwt` or `tls_client_auth` as the token endpoint authentication method.
+
. Click *Save*.

===== Setting up the client policy for OAuth Client ID Metadata Document

To trigger the profile created above when the `client_id` parameter in an authorization request is a URI matching a specified scheme (e.g., `https`), you need to create the policy including `client-id-uri` condition.

To configure the condition, create a client policy in the {project_name} Admin Console:

. Navigate to *Realm Settings* -> *Client Policies* -> *Policies* tab.
. Click *Create client policy*.
. Give the policy a name such as `cimd-policy` and click *Save*.
. Under *Conditions*, click *Add condition* and select `client-id-uri` from the list.
. Configure the condition with the following options:
+
* *URI scheme*: A list of URI schemes to match against the `client_id` parameter (e.g., `https`). In a production environment, only `https` should be used.
* *Trusted domains*: A list of domain patterns (wildcard) that the condition accepts for the host part of the `client_id` URI. If domains are filled, the condition evaluates to true only when the host part of the `client_id` matches one of the domains. If not filled, the condition evaluates to false regardless. For example, use `*.example.org` to accept any subdomain of `example.org`.
+
. Click *Save*.
. Under *Associated client profiles*, add the `cimd-profile` profile created in the previous step.
. Click *Save*.

With this configuration, when an MCP client sends an authorization request with a `client_id` value that is an `https` URL matching a trusted domain, {project_name} fetches the Client ID Metadata Document from that URL and uses the metadata to process the request.

===== System-wide settings for the Client ID Metadata Document executor

The `client-id-metadata-document` executor has the following system-wide settings that control caching and metadata size limits. These settings cannot be configured through the Admin Console. Instead, they are configured as SPI options when starting {project_name}.

* *min-cache-time*: The minimum time (in seconds) that a fetched Client ID Metadata Document is cached. Default: `300` (5 minutes).
* *max-cache-time*: The maximum time (in seconds) that a fetched Client ID Metadata Document is cached. Default: `259200` (3 days).
* *upper-limit-metadata-bytes*: The maximum size (in bytes) of a Client ID Metadata Document that {project_name} accepts. Default: `5000` (5 KB).

To configure these settings, use the `--spi-client-policy-executor--client-id-metadata-document--<property>=<value>` command-line option when starting {project_name}. For example:

```bash
bin/kc.[sh|bat] start --spi-client-policy-executor--client-id-metadata-document--min-cache-time=600 --spi-client-policy-executor--client-id-metadata-document--max-cache-time=86400 --spi-client-policy-executor--client-id-metadata-document--upper-limit-metadata-bytes=10000
```

=== Visual Studio Code desktop integration

https://code.visualstudio.com/[Microsoft Visual Studio Code] (VS Code) desktop is an MCP client that supports OAuth Client ID Metadata Document. When VS Code desktop connects to an MCP server that requires authorization, it sends an authorization request with a `client_id` parameter that is an `https` URL hosted on `vscode.dev` (e.g., `+https://vscode.dev/mcp-client+`). {project_name} fetches the Client ID Metadata Document from this URL and uses the metadata to process the request.

VS Code desktop uses localhost callbacks for the OAuth redirect. It starts a local HTTP server and uses a redirect URI such as `+http://127.0.0.1:<port>/callback+`. Because the redirect URI is on `127.0.0.1` rather than on the `vscode.dev` domain, the *Restrict same domain* option in the client profile executor must be set to `OFF`.

To configure {project_name} for VS Code desktop's MCP client, follow the steps below.

NOTE: VS Code desktop is a public client that uses PKCE (Proof Key for Code Exchange) for OAuth. It does not use a client secret.

==== Starting {project_name} with the CIMD feature

Start {project_name} with the `cimd` feature flag enabled:

```bash
bin/kc.[sh|bat] start --features=cimd
```

==== Setting up the client profile for VS Code desktop

. Navigate to *Realm Settings* -> *Client Policies* -> *Profiles* tab.
. Click *Create client profile*.
. Give the profile a name such as `vscode-cimd-profile` and click *Save*.
. Click *Add executor* and select `client-id-metadata-document` from the list.
. Configure the executor with the following options:
+
* *Allow http scheme*: `OFF`
* *Trusted domains*: `vscode.dev`, `127.0.0.1`
* *Restrict same domain*: `OFF` (VS Code desktop uses a localhost redirect URI such as `+http://127.0.0.1:<port>/callback+`, which is not on the same domain as `vscode.dev`)
* *Only Allow Confidential Client*: `OFF` (VS Code desktop is a public client)
+
. Click *Save*.

==== Setting up the client policy for VS Code desktop

. Navigate to *Realm Settings* -> *Client Policies* -> *Policies* tab.
. Click *Create client policy*.
. Give the policy a name such as `vscode-cimd-policy` and click *Save*.
. Under *Conditions*, click *Add condition* and select `client-id-uri` from the list.
. Configure the condition with the following options:
+
* *URI scheme*: `https`
* *Trusted domains*: `vscode.dev`
+
. Click *Save*.
. Under *Associated client profiles*, add the `vscode-cimd-profile` profile created in the previous step.
. Click *Save*.

With this configuration, when VS Code desktop sends an authorization request, {project_name} recognizes the `client_id` as a URL on `vscode.dev`, fetches the Client ID Metadata Document, and uses a localhost callback to complete the OAuth flow.


</@tmpl.guide>