How to Configure Keycloak for the MCP Server

This guide shows you how to create the two Keycloak clients the Axual Model Context Protocol (MCP) Server needs, so it can authenticate users against Keycloak and obtain a JSON Web Token (JWT) access token on their behalf.

Type

How-to guide

Goal

Give the MCP Server the Keycloak clients it authenticates through.

Audience

Platform Operator with admin access to the tenant’s Keycloak realm.

When to use

Use this guide before deploying the MCP Server. Its values reference the client id and secret created here.

The MCP Server is bound to one tenant, so these clients are created in that tenant’s realm. A multi-tenant governance installation repeats this per tenant.

Contents

The sections below cover each task in this guide:

Prerequisites

Confirm the following before you begin.

Access and permissions required

You need the following access and permissions:

  • Admin access to the tenant’s Keycloak realm.

Resources that must exist before starting

The following must already exist:

  • The tenant the MCP Server will serve.

  • The URL the MCP endpoint will be reachable on, for example mcp-tenant1.example.org. The client configuration below references it, so decide it now.

Create the Keycloak clients

The MCP Server authenticates users through two Keycloak clients: a bearer-only client that represents the server, and a second client that obtains the access tokens.

  1. Log in to Keycloak as administrator and switch to the tenant’s realm.

  2. Click on Clients in the left-side menu.

    Keycloak Menu
  3. Create a new client mcp-axual as per the screenshot below. This is the bearer-only client representing the MCP Server. An access token issued for the MCP Server carries the audience claim (aud) set to mcp-axual.

    Create Keycloak Client
  4. Ensure Client authentication is off and no Authentication flows are enabled. This makes the client bearer-only, so it cannot obtain access tokens itself. The client created in the next step does that.

    Create Keycloak Client Capability Config
  5. Create a new client mcp-oauth-proxy. AI clients obtain their access tokens through it. In Valid redirect URIs, enter <MCP_SERVER_BASE_URL>/auth/callback, scheme included. If the MCP Server is served at mcp-tenant1.example.org, the redirect URI is https://mcp-tenant1.example.org/auth/callback. Keycloak rejects a redirect URI with no scheme, and this value must match MCP_OAUTH_SERVER_BASE_URL in How to Deploy the Axual MCP Server exactly.

    Keycloak Client Details
  6. Under Capability Config, enable Client authentication and tick Standard flow under Authentication flow.

    Keycloak Client Details
  7. Switch to the Client scopes tab, then click on the mcp-oauth-proxy-dedicated link.

    Keycloak Client Scopes
  8. Switch to the Scope tab and turn off the Full scope allowed setting.

    Keycloak Client Scopes
  9. Switch to the Mappers tab. Click on the Configure a new mapper button.

    Keycloak Client Mappers
  10. In the popup menu, select Audience.

    Keycloak Client Mapper Options
  11. Fill the form as per the screenshot below. Click on Save.

    Keycloak Client Mappers
  12. Switch back to the Client scopes tab, then mark every scope Optional except email and profile. These should be set to Default.

    Keycloak Client Optional scopes
  13. Switch to the Credentials tab and copy the value of Client Secret. The deployment guide needs it.

    Keycloak Client Credentials
  14. To enforce PKCE, switch to the Advanced tab, then click on Advanced settings.

    Keycloak Client Advanced Settings
  15. Locate the setting Proof Key for Code Exchange Code Challenge Method and set it to S256. This forces the client to present a code challenge on every Authorization Code flow.

    Keycloak Client Mappers

Both clients now exist in the tenant’s realm, which completes the Keycloak setup for the MCP Server.

Verify the clients

Both clients are configured correctly when mcp-oauth-proxy can obtain a token that mcp-axual accepts. Request one with the client credentials you set above.

Replace every <VALUE> placeholder with your own value before running a command.
curl -s -X POST \
  "https://<GOVERNANCE_URL>/auth/realms/<TENANT>/protocol/openid-connect/token" \
  -d grant_type=client_credentials \
  -d client_id=mcp-oauth-proxy \
  -d client_secret=<MCP_OAUTH_CLIENT_SECRET> | jq -r '.access_token'

A token comes back when the client exists, client authentication is on and the secret is right. An invalid_client error means one of those three is wrong.

Decode the token and confirm it carries the mcp:user scope and names mcp-axual in its audience. Keycloak still issues a token without them, and the MCP Server refuses it later, which surfaces as a deployment problem rather than a Keycloak one.

Next steps