Skip to main content

Keycloak MCP Server

Python 3.12+ Tests Coverage License: Apache 2.0 Open in GitHub Codespaces

An MCP server for the Keycloak Admin REST API where every call runs as the signed-in user. Users log in to Keycloak from their MCP client (Claude Code, Codex, …); the server forwards their own access token to the Admin API, so Keycloak enforces each user's existing roles. There is no shared admin password or service account.

MCP client ──login (OAuth 2.1 + PKCE)──▶ this server ──▶ Keycloak login page
     │                                        │ keeps the user's Keycloak token, encrypted
     └── tool call with an MCP-only token ──▶ │ ──Admin API, Bearer <user's token>──▶ Keycloak
                                                                    (decides: allowed / 403)

Why per-user identity

Most Keycloak MCP servers authenticate with one admin account or service account, so anyone who can reach the server acts with its full power, and the audit trail shows the shared account instead of the person. Here:

  • Least privilege for free: a user with only view-clients can list clients and gets a clean permission error for everything else.
  • Audit trails name the real user.
  • No admin password or client secret to store: the Keycloak client is public, with PKCE.
  • The MCP client never holds the Keycloak token: the FastMCP OAuth proxy issues it a token that only works on this server, and stores the Keycloak token encrypted.
  • No fallback identity: requests are never retried as a service account, and the SDK is prevented from silently logging in again.

Features

  • 20 read-only tools: realm settings; users, groups and memberships; clients, client scopes and protocol mappers; direct and effective role mappings and role holders; login and admin events; sessions; access-token previews; LDAP federation
  • Browser OAuth for any MCP client, with consent, PKCE, audience-bound JWT validation and encrypted per-user token storage, with no database
  • Bounded, safe output: pagination capped at 100, selected fields only; secrets, credentials and admin-event payloads are never returned
  • FastMCP + FastAPI, HTTP/SSE/streamable-HTTP transports, Pydantic settings, structured JSON logging
  • Container and OpenShift manifests, GitHub Actions CI

Current limits

  • Read-only: no create, update or delete tools yet.
  • One realm per server: the realm users sign in to is the realm the tools inspect.
  • Single instance: OAuth state is stored on local disk.

Quick Start

git clone https://github.com/drtinkerer/keycloak-mcp-server
cd keycloak-mcp-server
make install        # uv sync + pre-commit hooks + local configuration
make local          # starts server on localhost:5001

Verify in another terminal:

curl http://localhost:5001/health
Manual setup (without Make)
# Sync dependencies from the committed lockfile
uv sync --locked
uv run pre-commit install

# Configure and run
cp .env.example .env
uv run keycloak-mcp-server

# Verify
curl http://localhost:5001/health

All Python commands go through uv; no shell activation is needed. uv sync installs the default dev dependency group. uv.lock is committed for reproducible installs; use uv add to add dependencies and uv lock --upgrade-package <name> to update one. uv manages an ignored environment internally. See uv’s project guide.

Configure browser OAuth

Register a public OIDC client named keycloak-mcp in the target Keycloak realm. Enable authorization code flow with PKCE S256, refresh tokens, and an access-token audience mapper that adds the client's own ID (keycloak-mcp). Disable password grants and service accounts. Register this exact redirect URI:

http://localhost:5001/auth/callback

The authentication guide lists every client setting and includes a Terraform example.

Set these values in the server’s ignored .env file:

ENABLE_AUTH=True
MCP_HOST_ENDPOINT=http://localhost:5001
KEYCLOAK_SERVER_URL=https://keycloak.example.com
KEYCLOAK_REALM=myrealm
KEYCLOAK_OAUTH_CLIENT_ID=keycloak-mcp
KEYCLOAK_OAUTH_AUDIENCE=keycloak-mcp
KEYCLOAK_OAUTH_SIGNING_KEY=<random persistent signing key>
# KEYCLOAK_CA_BUNDLE=/path/to/trusted-ca-bundle.pem

The client uses public authorization code flow with PKCE; no Keycloak client secret is required. Leave KEYCLOAK_OAUTH_CLIENT_SECRET unset. For an existing confidential upstream client, supplying that variable selects client_secret_basic. The signing key below is internal MCP server state, separate from Keycloak credentials.

Generate the signing key once with uv run python -c 'import secrets; print(secrets.token_urlsafe(48))'. Keep it private and stable across restarts. Remove KEYCLOAK_DEV_ACCESS_TOKEN if set. The server fails at startup if OAuth configuration is incomplete. No PostgreSQL is required; encrypted OAuth state is stored in the ignored .keycloak-oauth/ directory.

Start the server with make local. The MCP client opens the browser, and the user signs in to Keycloak. Their existing Keycloak permissions determine which tools they can call; the client registration grants no admin roles. Target URL, realm, and OAuth client ID belong to the server configuration. Each user’s identity comes from the browser login. See the authentication guide for the flow, TLS trust, and troubleshooting.

Use with Codex

With the configured server running:

codex mcp add keycloak --url http://localhost:5001/mcp
codex mcp login keycloak
codex mcp list
codex mcp get keycloak
codex

Adding an OAuth server may start login automatically; run login if authentication is still needed or if the connection was already added with authentication disabled. The user approves the MCP connection and completes Keycloak login in the browser. No client secret or Keycloak access token goes into the Codex MCP configuration.

Inside a new Codex session, use /mcp to check the twenty tools, then ask:

Use the keycloak MCP server to find who has the realm-admin role.

The CLI saves the connection in ~/.codex/config.toml by default. Restart existing Codex sessions after changing MCP configuration. See the official Codex MCP documentation.

Log out or remove the connection:

codex mcp logout keycloak
codex mcp remove keycloak

Use with Claude Code

claude mcp add --transport http --scope user keycloak http://localhost:5001/mcp
claude mcp login keycloak
claude mcp list
claude mcp get keycloak
claude

In Claude, /mcp shows connection status and offers authentication. Use it if an older Claude CLI does not have mcp login. The user scope makes the connection available across projects. Start a new Claude session after changing the configuration. See the official Claude Code MCP documentation.

Log out or remove the connection:

claude mcp logout keycloak
claude mcp remove --scope user keycloak

Removing a connection does not stop the server. Press Ctrl+C in its terminal to stop it. With ENABLE_AUTH=False, discovery works without login, but browser OAuth routes are absent. Use the direct client to test the protocol without an LLM.

Configuration

Variable Default Description
MCP_HOST localhost Server bind address
MCP_PORT 5001 Server port (1024-65535)
MCP_TRANSPORT_PROTOCOL http Transport protocol (http, sse, streamable-http)
MCP_SSL_KEYFILE None SSL private key file path
MCP_SSL_CERTFILE None SSL certificate file path
ENABLE_AUTH False* Enable OAuth authentication (see Auth Guide)
MCP_HOST_ENDPOINT http://localhost:5001 Public OAuth origin, without /mcp
KEYCLOAK_SERVER_URL Unset Keycloak base URL, including /auth when used
KEYCLOAK_REALM Unset Target realm, e.g. myrealm
KEYCLOAK_OAUTH_CLIENT_ID Unset Registered public Keycloak client with PKCE
KEYCLOAK_OAUTH_CLIENT_SECRET Unset Optional; only set for a confidential upstream client
KEYCLOAK_OAUTH_SIGNING_KEY Unset Persistent random key of at least 32 characters
KEYCLOAK_OAUTH_AUDIENCE keycloak-mcp Required aud claim; add an audience mapper for it
PYTHON_LOG_LEVEL INFO Logging level

* ENABLE_AUTH is True in code but False in .env.example, so a fresh make install starts with tool discovery only.

Documentation

Guide Description
Architecture System diagrams, code structure, key components, MCP tools
Development Setup, running locally, testing, code quality
Deployment Podman, OpenShift, container configuration
CI/CD Workflows, pipeline features, running CI locally
Contributing Development workflow, commit conventions, PR process
Security Vulnerability reporting policy
Changelog Release history
Authentication OAuth setup, auth modes, troubleshooting
Tutorial Your First Tool in 5 Minutes
Examples FastMCP and LangGraph client examples

Contributing

See CONTRIBUTING.md for detailed guidelines.

# Fork, clone, and set up
git clone https://github.com/<your-username>/keycloak-mcp-server.git
cd keycloak-mcp-server
make install

# Create a branch, make changes, verify
git checkout -b feat/your-feature
make lint && make test && make pre-commit

# Commit and open a PR
git commit -m "feat: your descriptive message"
git push origin feat/your-feature

Acknowledgements

Started from redhat-data-and-ai/template-mcp-server (Apache 2.0), a FastMCP server template. The Keycloak integration, OAuth proxy setup and tools are new.

License

Apache 2.0

Metadata

Release files for keycloak-mcp-server 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for keycloak-mcp-server 0.1.0
File Size Uploaded
keycloak_mcp_server-0.1.0.tar.gz 248.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for keycloak-mcp-server 0.1.0
File Interpreter ABI Platform
keycloak_mcp_server-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 283.0 kB

Release files / keycloak_mcp_server-0.1.0.tar.gz

Download URL keycloak_mcp_server-0.1.0.tar.gz
Size 248.4 kB
Tags Source
SHA-256 checksum
How to use checksums
5d35287a35f22a1d499d3bc90a80c9b22a29cbd5a31ee3fef5effe5e891da985
BLAKE2b-256 checksum
How to use checksums
a46737340d53683d7b14f1bd59e7dd403140628a4c89c90efa501e804429ed2c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 11, 2026.

Transparency log

Release files / keycloak_mcp_server-0.1.0-py3-none-any.whl

Download URL keycloak_mcp_server-0.1.0-py3-none-any.whl
Size 34.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b09cf19cb16ab6f700a00daf2cfd2f7ca977ac0ff7e8ec6cf0feba6f139f8974
BLAKE2b-256 checksum
How to use checksums
c4555b41d61dab7d121dced2a5c090eaa219e2b594731e486ffc9220e85cf7f7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 11, 2026.

Transparency log

Release history Release notifications | RSS feed

0.1.1

2 release files

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page