Faust-Systems

keycloak-mcp

Community Faust-Systems
Updated

Multi-host Keycloak MCP server (stdio): inspect and modify realm/client/protocol-mapper config across any number of Keycloak instances. Read-only by default, fail-closed production write guard, secrets never enter the model's context.

keycloak-mcp

An MCP (Model Context Protocol) server, stdio transport, that lets an AIassistant inspect and — behind explicit guards — modify Keycloak realm,client, and protocol-mapper configuration across multiple Keycloak hostsfrom a single server process.

Built on the official@keycloak/keycloak-admin-clientand the @modelcontextprotocol/sdk.Design derived from the MIT-licensedOctodet keycloak-mcp — see NOTICE.

Install

git clone https://github.com/Faust-Systems/keycloak-mcp.git
cd keycloak-mcp
npm install
npm run build     # tsc → dist/

Then create a config file (see Configuration) and register theserver with your MCP client (see Registering with Claude).

Hosts

Every tool takes a host argument: a string key naming an entry in theconfig file's hosts map (or discovered from the KC_<HOST>_* envfallback — see below). Host keys are entirely arbitrary and user-defined— there is no fixed list of hosts baked into this server. Point it atwhatever Keycloak instances you like, under whatever names make sense to you(prod-eu, staging, customer-a, ...).

Each host entry carries its own explicit production flag:

"prod-eu": { "url": "...", "adminUser": "", "adminPassword": "", "production": true }
  • production: true marks the host as production: writes against it arerefused unless allowProdWrite is also true in the config.
  • production: false marks it non-production: writes need only theper-call write=true.

[!IMPORTANT]Fail-closed rule: if a host's production flag is absent, null,or any non-boolean value, the host is treated as production. Only aliteral boolean false disarms the production write guard. This toolrotates live client secrets, so a typo or an omission in config must neversilently disable the safety gate — when in doubt, it defaults to the safer(more restrictive) behaviour.

The server authenticates per host with a password grant against theadmin-cli client on the master realm, and caches the admin clientper host (re-authenticating when the token expires). The target realm is aper-call argument on every tool.

Safety model

  • Read-only by default. Mutating tools (ensure_hardcoded_claim_mapper,delete_protocol_mapper) take a write boolean that defaults to false.Without write=true they return a dry-run plan and call no mutating API.
  • Production gate, fail-closed. Even with write=true, a write against aproduction host is refused unless allowProdWrite is true in the configfile (or, when using the env fallback, KC_ALLOW_PROD_WRITE=true). A hostis production whenever its production flag is true, absent, or anynon-boolean value — only an explicit production: false opts a host out ofthe gate. See Hosts.
  • No secrets, ever. Admin passwords and tokens are never logged orreturned; client secrets and registration access tokens are redacted fromget_client output.
  • Audit trail. Every successful write emits a structured JSON line tostderr: timestamp, host, realm, clientId, action, mapper name.
  • Idempotent verbs. ensure_hardcoded_claim_mapper reconciles toward adesired state (already-present / update-with-diff / create) instead ofblindly creating; genuine ambiguity (e.g. two mappers already emitting theclaim) is reported as a conflict and never auto-resolved.

Client secrets — the file-sink model

dump_client_secret and regenerate_client_secret handle real client secretvalues. To keep those values out of the AI model's context entirely, theserver never returns or logs the value:

  • The secret value is written directly to a local file by the serverprocess (outPath, mode 0600, exact bytes, no trailing newline).
  • The tool returns only metadata{ host, realm, clientId, outPath, byteLength } (plus rotated: true for a regeneration). The value is neverin the return object, any log line, the audit line, or any error message.
  • Keycloak admin-API failures are reduced to an HTTP status plus a genericmessage, so no response body can leak.
  • The server warns (does not fail) if outPath's directory isgroup/world-writable, since a secret file there could be exposed.
  • dump_client_secret is a read (no Keycloak mutation) so it is notwrite-gated — but it still writes a local file. regenerate_client_secretis a mutation: write=false (default) is a dry run, and rotating aproduction host requires allowProdWrite (see the fail-closed rule underHosts).

Configuration

All configuration — host URLs, admin credentials, and the production-writegate — comes from a single JSON config file. Nothing is hardcoded.

File location & precedence

The config path is resolved in this order:

  1. The environment variable KEYCLOAK_MCP_CONFIG, if set (explicit override).
  2. Otherwise the default ~/.config/keycloak-mcp/config.json.

The file takes precedence. If no file is found at the resolved path, theserver falls back to the legacy per-host environment variables (below) sonothing breaks — but when the file is present it is used exclusively and theenv vars are ignored.

File shape

See config.example.json. Copy it to ~/.config/keycloak-mcp/config.jsonand fill in your own host keys and credentials:

{
  "hosts": {
    "prod-eu": { "url": "https://keycloak.example.com",    "adminUser": "", "adminPassword": "", "production": true },
    "staging": { "url": "https://kc-staging.example.com",  "adminUser": "", "adminPassword": "", "production": false }
  },
  "allowProdWrite": false
}
  • Host keys ("prod-eu", "staging" above) are entirely arbitrary —name your hosts however makes sense to you. There is no fixed set ofhosts; add or remove entries freely.
  • production (per host) gates writes behind allowProdWrite. Fail-closedrule: if production is absent, null, or any non-boolean value, thehost is treated as production. Only a literal production: false opts ahost out. See Hosts.
  • allowProdWrite must be true to permit writes to any host whoseproduction flag resolves to production (per the rule above); a hostexplicitly marked production: false only needs the per-call write=true.
  • A host with a missing url, adminUser, or adminPassword fails only whenaddressed, with an error naming the missing field names (never values).The other hosts keep working.
  • Calling a tool with a host key that isn't in the config fails with anerror naming the requested host and listing the configured host keys(never values).

Protect the file: it holds admin credentials in the clear. Keep it atchmod 600 — the server prints a one-line warning to stderr (it does notrefuse) if the file is group- or world-readable.

Environment-variable fallback

Used only when no config file exists at the resolved path. See .env.example.

Host keys are discovered by scanning for a KC_<HOST>_URL variable —there is no fixed list. The host key is the captured part of the variablename, lowercased, with underscores left as-is: KC_PROD_EU_URL yields thehost key prod_eu. Hyphens in a host key are not expressible via envvars — use the config file if you need one.

variable purpose
KEYCLOAK_MCP_CONFIG override the config file path (else ~/.config/keycloak-mcp/config.json)
KC_<HOST>_URL the host's base URL — presence of this variable is what discovers the host
KC_<HOST>_ADMIN_USER / KC_<HOST>_ADMIN_PASSWORD admin credentials for that host
KC_<HOST>_PRODUCTION false (case-insensitive) marks the host non-production; absent or any other value fails closed to production
KC_ALLOW_PROD_WRITE true to allow writes to production hosts (fallback for allowProdWrite)

Build

npm install
npm run build     # tsc → dist/
npm test          # vitest unit tests

Registering with Claude (MCP config)

Because all credentials live in the config file, the MCP registration needsonly command + argsno env block:

{
  "mcpServers": {
    "keycloak": {
      "command": "node",
      "args": ["/absolute/path/to/keycloak-mcp/dist/index.js"]
    }
  }
}

The server reads ~/.config/keycloak-mcp/config.json by default. To point itat a different file, add a single override:

"env": { "KEYCLOAK_MCP_CONFIG": "/absolute/path/to/config.json" }

Keep allowProdWrite at false in the file to hold production read-only;set it to true only when a session must change production.

Tools

tool arguments what it does
list_clients host, realm clients of a realm: {clientId, id, name}
get_client host, realm, clientId full client representation (secrets redacted)
list_protocol_mappers host, realm, clientId the client's dedicated-scope mappers: id, name, type, config
ensure_hardcoded_claim_mapper host, realm, clientId, claimName, claimValue, addToAccessToken=true, addToIdToken=false, claimJsonType="String", write=false idempotently ensure an oidc-hardcoded-claim-mapper emits the claim
delete_protocol_mapper host, realm, clientId, mapperId, write=false delete one mapper by UUID
dump_client_secret host, realm, clientId, outPath write the client's current secret to a local 0600 file; returns metadata only
regenerate_client_secret host, realm, clientId, outPath, write=false rotate the client secret and write the new value to a local 0600 file; returns metadata only

Usage example — hardcoded realm claim

Goal: the my-app client in realm acme on the staging host (configuredwith "production": false, see Hosts) must emit a hardcodedrealm claim with value acme in access tokens.

  1. Dry run (default — nothing changes):

    ensure_hardcoded_claim_mapper with{ "host": "staging", "realm": "acme", "clientId": "my-app", "claimName": "realm", "claimValue": "acme" }

    → returns status: "dry-run", wouldDo: "create" with the exact mapper itwould create — or wouldDo: "update" with a per-key diff if a mapperalready exists with different config, or status: "already-present" ifeverything already matches.

  2. Apply: same call plus "write": true.

    → creates/updates the mapper and emits an audit line to stderr.

  3. Repeat against a production host (e.g. prod-eu) once verified — thosecalls additionally require "allowProdWrite": true in the config file.

Development

  • src/index.ts — server bootstrap + tool registration (stdio).
  • src/config.ts — config file loader (file → env fallback), perms warning.
  • src/hosts.ts — host registry, config validation, production write gate.
  • src/kc.ts — cached, re-authenticating admin client per host.
  • src/mappers.ts — pure plan/diff logic for the ensure tool (unit-tested).
  • src/secrets.ts — client-secret file-sink tools (value never returned/logged).
  • src/redact.ts — secret redaction.
  • src/audit.ts — stderr audit lines.
  • test/ — vitest unit tests for all pure logic.

License

MIT — see LICENSE. Derived from the design of Octodet's keycloak-mcp; seeNOTICE for attribution.

MCP Server · Populars

MCP Server · New

    firish

    Claude Code for Visual Studio

    Bring Claude Code to Visual Studio 2026: A native diff with accept/reject, a live debugger Claude can drive autonomously, Roslyn code navigation, and Test Explorer integration. The IDE half of Claude Code's integration protocol. Community-built, unofficial.

    Community firish
    uiNerd16

    @aicanvas/mcp

    Open-source React and Tailwind component marketplace. Install via the shadcn CLI or your AI editor over MCP, with reproduction prompts for Claude Code, Lovable, and V0.

    Community uiNerd16
    FootprintAI

    Containarium — Agent Runtime

    Open-source agent runtime — SSH-native isolation, eBPF egress policy, Kubernetes + LXC backends, GPU passthrough, MCP-native CLI

    Community FootprintAI
    openfate-ai

    @openfate/bazi-mcp

    OpenFate Bazi MCP server with deterministic Four Pillars calculation, True Solar Time, branch interactions, and reverse Bazi lookup.

    Community openfate-ai
    mohitagw15856

    🧠 PM Skills — 454 Professional Agent Skills for Claude, ChatGPT, Gemini, Cursor, Codex & Hermes

    In Anthropic's official Claude plugin directory · 400 professional Agent Skills (PRDs, launches, compliance, CVs & more) for Claude, ChatGPT, Gemini, Cursor & Codex. Try free in-browser, or 'npx pm-claude-skills add'.

    Community mohitagw15856