keycloak-mcp

keycloak-mcp

An MCP server that lets an AI assistant inspect and modify Keycloak realm, client, and protocol-mapper configuration across multiple Keycloak hosts.

Category
访问服务器

README

keycloak-mcp

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

Built on the official @keycloak/keycloak-admin-client and the @modelcontextprotocol/sdk. Design derived from the MIT-licensed Octodet 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 the server with your MCP client (see Registering with Claude).

Hosts

Every tool takes a host argument: a string key naming an entry in the config file's hosts map (or discovered from the KC_<HOST>_* env fallback — see below). Host keys are entirely arbitrary and user-defined — there is no fixed list of hosts baked into this server. Point it at whatever 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 are refused unless allowProdWrite is also true in the config.
  • production: false marks it non-production: writes need only the per-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 a literal boolean false disarms the production write guard. This tool rotates live client secrets, so a typo or an omission in config must never silently 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 the admin-cli client on the master realm, and caches the admin client per host (re-authenticating when the token expires). The target realm is a per-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 a production host is refused unless allowProdWrite is true in the config file (or, when using the env fallback, KC_ALLOW_PROD_WRITE=true). A host is production whenever its production flag is true, absent, or any non-boolean value — only an explicit production: false opts a host out of the gate. See Hosts.
  • No secrets, ever. Admin passwords and tokens are never logged or returned; client secrets and registration access tokens are redacted from get_client output.
  • Audit trail. Every successful write emits a structured JSON line to stderr: timestamp, host, realm, clientId, action, mapper name.
  • Idempotent verbs. ensure_hardcoded_claim_mapper reconciles toward a desired state (already-present / update-with-diff / create) instead of blindly creating; genuine ambiguity (e.g. two mappers already emitting the claim) 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 secret values. To keep those values out of the AI model's context entirely, the server never returns or logs the value:

  • The secret value is written directly to a local file by the server process (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 never in 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 generic message, so no response body can leak.
  • The server warns (does not fail) if outPath's directory is group/world-writable, since a secret file there could be exposed.
  • dump_client_secret is a read (no Keycloak mutation) so it is not write-gated — but it still writes a local file. regenerate_client_secret is a mutation: write=false (default) is a dry run, and rotating a production host requires allowProdWrite (see the fail-closed rule under Hosts).

Configuration

All configuration — host URLs, admin credentials, and the production-write gate — 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, the server falls back to the legacy per-host environment variables (below) so nothing breaks — but when the file is present it is used exclusively and the env vars are ignored.

File shape

See config.example.json. Copy it to ~/.config/keycloak-mcp/config.json and 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 of hosts; add or remove entries freely.
  • production (per host) gates writes behind allowProdWrite. Fail-closed rule: if production is absent, null, or any non-boolean value, the host is treated as production. Only a literal production: false opts a host out. See Hosts.
  • allowProdWrite must be true to permit writes to any host whose production flag resolves to production (per the rule above); a host explicitly marked production: false only needs the per-call write=true.
  • A host with a missing url, adminUser, or adminPassword fails only when addressed, 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 an error naming the requested host and listing the configured host keys (never values).

Protect the file: it holds admin credentials in the clear. Keep it at chmod 600 — the server prints a one-line warning to stderr (it does not refuse) 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 variable name, lowercased, with underscores left as-is: KC_PROD_EU_URL yields the host key prod_eu. Hyphens in a host key are not expressible via env vars — 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 needs only 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 it at 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 (configured with "production": false, see Hosts) must emit a hardcoded realm 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 it would create — or wouldDo: "update" with a per-key diff if a mapper already exists with different config, or status: "already-present" if everything 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 — those calls 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; see NOTICE for attribution.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选