关于 KeyCloak MCP
KeyCloak MCP 将 MCP 客户端连接到 KeyCloak Admin REST API,用于身份管理和安全调查。它涵盖用户、会话、事件、客户端及 Realm 配置,并通过服务账户而非人工登录进行身份验证。
主要功能
- 01搜索并查看用户及凭据
- 02重置密码并管理用户状态
- 03检查登录事件、失败记录和 IP 活动
- 04查看组、客户端和活动会话
- 05汇总安全与配置变更
适用场景
- 01调查密码喷洒活动和登录失败
- 02复核 Realm 配置及管理员变更
- 03检查组成员关系和用户会话状态
MCP 相关
通过 Admin REST API 向 MCP 客户端提供 KeyCloak 管理和安全工具。使用服务账户通过 Client Credentials Grant 认证,不会创建用户会话。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
33收录的工具列表 · 更新于 1 周前
此工具无需参数。
详细介绍
keycloak-mcp
English | 日本語
适用于 KeyCloak Admin REST API 的 MCP(Model Context Protocol)服务。
文档:https://shigechika.github.io/keycloak-mcp/
通过服务账号(客户端凭据授权)进行身份验证,因此无需人工密码或 TOTP。此外,该服务兼容 Infinispan——它绝不会创建用户会话,也不会访问 userinfo 端点。
功能
用户
| 工具 | 描述 |
|---|---|
count_users | realm 中的用户总数 |
search_users | 按部分匹配搜索(用户名 / 邮箱 / 姓名) |
get_user | 获取指定用户名的完整详情 |
reset_password | 重置一位用户的密码 |
reset_passwords_batch | 从 CSV 批量重置(每行格式为 username,password;密码留空时会自动生成) |
get_user_sessions | 获取一位用户的活动会话,时间戳采用本地时间 |
logout_user | 结束一位用户的所有活动会话 |
set_user_enabled | 启用或停用一位用户;停用后会阻止所有登录(保留自定义属性) |
MFA / 凭据
| 工具 | 描述 |
|---|---|
get_user_credentials | 获取一位用户配置的凭据类型;出现 otp 条目表示已设置 TOTP/HOTP |
get_totp_users | 统计整个 realm 的 TOTP 使用情况:拥有 otp 凭据的用户数量、占比,以及(可选)用户列表。会枚举用户并读取每位用户的凭据(N+1;通过 max_users 限制) |
群组
| 工具 | 描述 |
|---|---|
list_user_groups | 获取用户所属的群组 |
list_users_by_group | 获取群组成员 |
安全
| 工具 | 描述 |
|---|---|
get_brute_force_status | 查询用户当前是否因暴力破解检测而被锁定 |
get_realm_security_defenses | realm 级安全策略:是否启用了暴力破解检测及其阈值、密码策略和浏览器安全标头 |
get_config_changes | 查看 realm 配置自保存的每日快照以来有哪些变化(参见 配置快照)。只读;baseline_date 用于选择较早的快照。 |
get_login_failures_by_ip | 按来源 IP 列出失败情况(设置 KEYCLOAK_SITES_INI 后会标注站点) |
get_ip_activity | 全面调查一个来源 IP:成功 / 失败次数、受影响的用户 / 客户端和时间线。返回结构化 JSON。 |
spray_check | 检测密码喷洒攻击并内置泄露列表:每个拥有 ≥ 10 个不同用户且登录成功率 < 20 % 的外部 IP 都会被视为喷洒来源,其成功登录(证据元组 time / ip / username / user_id / client_id)即为潜在泄露事件。每行都包含 confidence:high 表示确认泄露;low 表示在共享出口(学校 NAT、家庭线路、VDI)上触发了流量规则——可通过 user_success_rate(成功登录的不同用户数 ÷ 不同用户数,≥ 0.2)、failure_concentration(集中在同一用户名上的失败占比,≥ 0.5)或匹配 KEYCLOAK_KNOWN_EGRESS 来识别。此类登录需向所有者核实,不应公开。top_failed_users 和 not_found_domains 分别显示持续登录失败的用户,以及导致 user_not_found 的拼错域名。返回固定结构的 JSON;窗口被截断时 complete: false。 |
detect_login_loops | 标记在短时间内登录次数过多的用户(重定向循环) |
事件
| 工具 | 描述 |
|---|---|
get_events | 按类型、用户名、客户端、IP 和日期范围筛选。用户名会在内部解析为用户 ID。失败事件包含 KeyCloak 的 error 字段(例如 invalid_user_credentials)。 |
get_login_stats | 登录成功 / 失败总数,分页遍历所有结果 |
get_login_stats_by_hour | 按一天中的小时统计登录次数(本地时间) |
get_login_stats_by_client | 按客户端 / SP 统计登录次数 |
get_password_update_events | UPDATE_PASSWORD 历史记录 |
管理员事件
get_events 只能查看用户事件。由管理员触发的操作,或由服务账户写入自定义属性的操作,都不会显示在其中。管理员事件端点可弥补这一不足。
| 工具 | 说明 |
|---|---|
get_admin_events | 按操作(CREATE / UPDATE / DELETE / ACTION)、资源类型(USER / CLIENT / ROLE / GROUP / …)、资源路径和日期范围筛选 |
get_user_attribute_history | 限定于单个用户的 UPDATE/ACTION 事件,便于追踪自动化流水线写入自定义属性(例如 provisioning_flag)的时间 |
这两个工具都接受 max_repr,用于控制表示载荷:正数 = 截断至 N 个字符(默认 500),0 = 不包含,负数 = 完整包含。
会话与客户端
| 工具 | 说明 |
|---|---|
get_session_stats | 每个客户端的活跃会话数 |
get_client_sessions | 单个客户端(SP)的活跃会话 |
list_clients | Realm 中的 SAML 和 OIDC 客户端 |
get_client | 单个客户端的配置,包括其身份验证流覆盖设置(每个 SP 的 MFA 开关)。省略 attributes 和 protocolMappers,因此工具输出不会包含客户端密钥或 SAML 签名材料。 |
get_realm_roles | Realm 级别的角色 |
每日巡检
| 工具 | 说明 |
|---|---|
health_check | 报告正在运行的服务版本,并验证 KeyCloak 后端是否可访问以及服务账户能否进行身份验证。轻量级(一次令牌请求;不扫描用户/事件/会话)。返回固定结构的字典,包含 status、auth 和配置的 URL/realm。该工具自身的说明会列举这些字段可能取值,且只有此处会列出。 |
daily_brief | 一次性晨间健康检查:登录统计、暴力破解 IP、喷洒检查(规则与 spray_check 相同,包含已泄露账户和证据)、活跃会话、密码更新、管理员事件和配置更改(针对角色、角色映射、流、客户端范围、协议映射器、组件和 Realm 的管理员事件,以及设置 KEYCLOAK_STATE_DIR 时的快照比较;两者均标记为 [CONFIG]),以及加入了带有角色的组的情况(当该组授予 realm-management 角色(只读的 query-*/view-* 除外)时标记为 [PRIVILEGE]),以上内容汇总为一份 Markdown 摘要。失败次数超过 ip_failure_threshold(默认 50)的 IP 和喷洒来源会标记为 WARNING;API 错误会显示为 CRITICAL。since_hours 控制回溯时间范围(默认 18 h)。 |
设置
# uv
uv pip install keycloak-mcp
# pip
pip install keycloak-mcp
从源码安装:
git clone https://github.com/shigechika/keycloak-mcp.git
cd keycloak-mcp
# uv
uv sync
# pip
pip install -e .
配置
| 变量 | 说明 | 默认值 |
|---|---|---|
KEYCLOAK_URL | 基础 URL,例如 https://keycloak.example.com | 必填 |
KEYCLOAK_REALM | Realm 名称 | master |
KEYCLOAK_CLIENT_ID | 服务账户客户端 ID | 必填 |
KEYCLOAK_CLIENT_SECRET | 客户端密钥 | 必填 |
KEYCLOAK_SITES_INI | 用于将 IP 映射到站点标签的 INI 文件(见下文) | 未设置 |
KEYCLOAK_KNOWN_EGRESS | 以逗号分隔的已知共享出口点(VDI、VPN 集中器、合作伙伴代理)的 CIDR。spray_check 会将匹配的 IP 标记为 known_egress: true,并将其标记为 confidence: low;这些 IP 永远不会被排除 | 未设置 |
KEYCLOAK_DEFAULT_DATE_FROM_HOURS | 未提供 date_from 时,事件工具默认回溯的时间范围。设为 0 可扫描全部历史记录(大型 Realm 可能导致工具卡住)。 | 24 |
KEYCLOAK_DEADLINE | 耗时较长的事件/TOTP 工具每次调用的墙上时钟时间预算(秒)。如果较宽的时间范围/大型 Realm 会超出预算,工具会停止并返回已披露的部分结果(⚠️ 警告),而不是超出客户端约 60 秒的网关超时并对 KeyCloak 造成压力。每个请求的超时也会限制为剩余时间,且截止时间过后不会重试,因此单个缓慢的分页请求也不会让调用超时。设为 0 或负数可禁用。 | 45 |
KEYCLOAK_MAX_EVENTS | 事件工具获取事件时每次分页的上限(也限制缓慢的高偏移分页深度)。超过上限时,结果将是已披露的部分结果。设为 0 或负数可禁用。 | 200000 |
KEYCLOAK_MAX_USERS | 当 get_totp_users 的 max_users 参数为 0 时,扫描用户数的默认上限(每个用户需要调用一次凭据接口)。设为 0 或负数可禁用(扫描整个 Realm,仅受 KEYCLOAK_DEADLINE 限制)。 | 5000 |
KEYCLOAK_USER_ATTRIBUTE_WHITELIST | 以逗号分隔的自定义用户属性键,允许 get_user 显示这些属性。默认未设置:get_user 只会返回用户名/姓名/电子邮件/启用状态/创建时间,因为用于解析用户名的搜索端点返回的是不含任何 attributes 的简要表示。将某个键加入白名单后,get_user 会额外执行一次按 ID 查询,并在属性值存在时附加该值。其他内容仍不会出现在工具输出中。作为安全措施,名称看起来像凭据的白名单键(包含 password、secret、token 等)会被报告为已屏蔽,而不会显示——但这并非保证,因为名称不符合该模式列表的凭据属性仍会被显示。 | 未设置 |
KEYCLOAK_GROUP_WRITE_ROOT | 启用选择加入的组更改工具(plan_group_changes、apply_group_changes),并将其限制在此绝对路径下的组中(例如 /Staff)。未设置、为空或格式错误时,根本不会注册这两个工具。仅在部署时由服务器前置人工审批步骤的情况下设置此项。 | 未设置 |
KEYCLOAK_PROTECTED_GROUPS | 以 ; 分隔的完整组路径;组更改工具会拒绝向这些组添加成员或从中移除成员(包括其后代组)。如果某个条目格式错误,组更改工具会被禁用,而不是默默丢弃该保护规则。 | 未设置 |
KEYCLOAK_GROUP_BATCH_MAX | 每次组更改调用的最大行数。 | 30 |
KEYCLOAK_GROUP_MODE | multi(KeyCloak 默认值:用户可以属于多个组)或 single(用于添加一个组会替换所有其他成员关系的部署,例如每个用户只保留一个组的事件监听器)。任何其他值都会禁用组工具。参见组更改。 | multi |
KEYCLOAK_STATE_DIR | 每日配置快照的目录(<dir>/config-snapshots,目录权限为 0700,文件权限为 0600)。未设置时不创建快照;daily_brief 仍会报告配置管理员事件。参见配置快照。 | 未设置 |
KEYCLOAK_GROUP_DEADLINE | 组更改工具的时间预算(秒)。更改 30 个用户的批次需要超过默认的 45 秒;如果服务器前没有超时约为 60 秒的环节(例如,由客户端直接启动服务器),请调高此值。未设置时使用 KEYCLOAK_DEADLINE。 | 未设置 |
KEYCLOAK_SESSION_PROBES | 独立连接数;get_user_sessions 会合并这些连接返回的会话列表(set_user_enabled 返回的剩余会话数也会合并这些结果)。在负载均衡器后,如果 KeyCloak 节点不共享用户会话(没有分布式 Infinispan 缓存),每个节点只会列出自己的会话。 | 8 |
KEYCLOAK_LOGOUT_CLEAN_PROBES | logout_user 会持续探测——每次探测都使用独立连接列出会话,并在连接到的节点列出会话时于该节点登出用户——直到连续有这么多次探测未发现会话。即使首次探测未列出任何会话,也会发送登出请求:它还会在数据库中设置用户的 notBefore,所有节点都会检查该值,且工具会报告此操作。新连接会到达哪个节点由负载均衡器决定:使用源 IP 会话保持时,探测可能永远无法到达某些节点,因此能在所有节点生效的是 notBefore。如果会话是共享的,请将此项和 KEYCLOAK_SESSION_PROBES 均设为 1。 | 10 |
KEYCLOAK_LOGOUT_MAX_PROBES | logout_user 探测次数的上限;如果达到上限时仍发现会话,工具会如实告知(⚠️),而不会报告成功。与 get_user_sessions 和 set_user_enabled 的会话列表一样,这些探测也受 KEYCLOAK_DEADLINE 限制;预算耗尽或请求失败时,工具会提示验证未完成(⚠️),并报告迄今已完成的操作。 | 40 |
KeyCloak 客户端设置
- 在 KeyCloak 管理控制台中创建一个新客户端。
- 开启 Client authentication 和 Service account roles。
- 为其授予
view-users、view-events、view-clients,以及仅在需要重置密码时才授予manage-users。
写入操作
默认情况下,四个工具会更改状态。其他工具都只读取数据。启用群组更改(见下文)后,apply_group_changes 会成为第五个工具。
| 工具 | 管理 API 调用 |
|---|---|
reset_password | PUT /users/{id}/reset-password |
reset_passwords_batch | 对每行 CSV 执行一次相同的调用 |
set_user_enabled | PUT /users/{id},并切换 enabled 状态 |
logout_user | GET /users/{id}/sessions + POST /users/{id}/logout,并在新连接上重复执行(参见 KEYCLOAK_LOGOUT_CLEAN_PROBES) |
这四个工具都需要 Service Account 上的 manage-users。不授予该角色,服务就会保持只读:这四个工具会返回 403 错误,其他所有工具则继续正常工作,因此可以将某个 realm 交给 AI 助手进行调查,而无需授予任何修改权限。只有在任务涉及账户恢复或限制访问时,才授予 manage-users。
群组更改(选择启用)
plan_group_changes 和 apply_group_changes 可在群组间转移用户(例如员工调动)。只有设置了 KEYCLOAK_GROUP_WRITE_ROOT 时,这两个工具才会启用,因此,共享该二进制文件但没有审批步骤的部署不会暴露它们。
| 工具 | 管理 API 调用 |
|---|---|
plan_group_changes | 只读:GET /users?username=...&exact=true、GET /group-by-path/...、GET /groups/{id}/role-mappings、GET /roles-by-id/{id}/composites、GET /clients/{id}、GET /users/{id}/groups |
apply_group_changes | 先执行 PUT /users/{id}/groups/{groupId},再执行 DELETE /users/{id}/groups/{groupId} |
plan_group_changes会按精确名称/路径解析每位用户和每个群组,并为每行返回当前直接所属的群组、将执行的操作和状态。其digest涵盖已解析的 ID、每位用户当前所属的群组以及策略。apply_group_changes会重新规划;除非每行仍可执行且digest与已批准的值一致,否则不会进行任何更改。因此,自规划以来任何人更改了成员关系,整个批次都会中止。它会先添加再移除,在添加前立即重新审核目标群组,在每次写入前重新读取成员关系,并在写入后读回;遇到首个不匹配、错误或时间预算耗尽(KEYCLOAK_GROUP_DEADLINE)时便会停止。如果剩余时间不足以完成某位用户的所有操作,就不会开始处理该用户,因此停止时不会出现已添加但尚未移除的情况。applied的值为yes、no、partial或unknown(写入后无法读回)。- 设置
KEYCLOAK_GROUP_MODE=single时,移动操作只执行一次添加:服务端会自行移除旧群组,因此不会发送 DELETE,并且读回结果必须仅显示目标群组。每行都必须指定要添加的群组(仅移除的行会导致用户不属于任何群组)。如果省略remove,则会填入用户当前所属的群组,以便审批者查看哪些内容会被移除;规划时以及添加前都会像审核remove一样审核该群组。用户如果属于多个群组,就违反了服务端规则,因此会被拒绝(请手动修复)。如果规划无法恢复用户先前的状态(用户原本没有群组,或服务端实际上没有替换群组),则会将其列入manual_restore,而不是reverse。如果你的 KeyCloak 行为如此,请启用此模式;在multi模式下,这种服务端行为会使每次移动都因并发更改而停止;在single模式下,如果服务端保留旧群组,则移动会停止并提示模式不匹配。 - 拒绝的目标:根群组以外的任何目标、根群组本身,以及受保护群组及其后代。
- 带有角色的群组不会被拒绝:如果审批者可以在管理控制台中执行相同操作,那么将某人转入部门的管理员群组就是普通的调动。每行规划都会列出
gained_roles和lost_roles:即此次移动对用户通过所有群组成员关系持有的角色造成的变化(通过共同祖先或其他群组保留的角色不算增减;不会读取直接映射给用户的角色)。add_roles和remove_roles分别表示加入和移出各个指定群组时会获得或失去的角色。Realm 角色按名称显示,客户端角色显示为client:role;会包含从祖先群组继承的角色以及复合角色中的所有角色(任意深度)。请向审批者展示新增和失去的角色。它们属于digest的一部分;添加前还会重新读取目标群组的角色(同一次调用中,每个群组最多每 5 秒读取一次),因此审批后附加到群组(或添加到复合角色)的角色会阻止执行。如果无法显示某行的角色变化(例如当前群组无法按路径解析,群组名称中包含/),则会拒绝该行。 在 1.3.0 中更改:早期版本会拒绝任何具有角色映射的群组。如果你依赖此行为,请在升级前将这些群组列入KEYCLOAK_PROTECTED_GROUPS。 - 成员关系可能在没有任何角色的情况下授予访问权限(例如服务提供商会检查的 SAML/OIDC 群组映射器、授权服务群组策略)。无论角色如何,都必须将这些工具绝不能写入的群组列入
KEYCLOAK_PROTECTED_GROUPS。每个受保护路径都必须存在于 KeyCloak 中:如果其中任一路径无法解析(已重命名、移动或大小写不同),在修正配置之前,所有规划都会被拒绝。 digest表示规划之后没有任何内容发生变化,但不能证明有人批准了该规划:任何调用plan_group_changes的调用方都能获得有效的 digest。请在apply_group_changes前设置审批环节,例如让客户端自行保存 digest,并在每次调用前征求人员批准。每项批准都应视为一次性:如果成员关系恢复到规划时的状态(例如执行reverse后),旧 digest 会再次匹配。- 结果包含
reverse,即撤销已生效更改的操作。它只是新规划和新审批的提案,不会自动应用。恢复成员关系无法撤销错误期间已经发生的访问。 写入需要manage-users。根据 KeyCloak 版本,读取组及其角色映射可能还需要query-groups。展开复合角色会读取 realm 角色(view-realm),而命名客户端角色会读取客户端(view-clients)。运行一次plan_group_changes进行确认。
配置快照(可选)
设置 KEYCLOAK_STATE_DIR 后,每天首次运行 daily_brief 时会存储一份 realm 配置快照,并且每次运行 daily_brief 都会将当前配置与更早日期的最新快照进行比较。发生变更时会显示一条 [CONFIG] 警告,并列出变更字段;get_config_changes 会列出所有变更。
- 涵盖内容:realm 设置(暴力破解检测、密码和 OTP 策略、会话和令牌有效期、流程绑定)、事件设置、realm 角色及各复合角色包含的内容、身份验证流程及其执行项、必需操作、客户端(重定向 URI、允许的流程、流程覆盖项、属性、协议映射器)、客户端范围、默认客户端范围、组件,以及服务账号有权读取时的身份提供商。组角色映射和客户端角色不包含在快照中;系统会改为报告其管理事件。
- 组成员关系不属于配置,也不包含在快照中。取而代之的是,简报会列出回溯窗口内加入具有角色的组的成员(角色可以是组自身的角色,也可以是通过复合角色继承的祖先组角色),并注明角色、用户和添加者。如果某个组授予
realm-management角色(query-*/view-*除外,例如realm-admin、manage-users等),则该成员变更标记为[PRIVILEGE];在 master realm 中,授予<realm>-realm管理客户端角色或adminrealm 角色时也一样。系统会通过组 ID 读取该组的当前状态,并沿parentId追溯其角色,因此事件发生后组名变更或组移动不会导致其被忽略。无法读取其组的成员变更会单独显示为[CONFIG]警告;此项检查最多占用 Configuration 部分 8 秒的预算。 - 不会存储机密:键名包含
secret、password、privateKey、apiKey、credential、token等类似字符串的值(get_user也使用这些标记)会替换为<redacted>,因此机密变更不会显示为配置变更。这些键下的true/false和数字属于设置,会保持可见,但本身就是机密的字段(...password、...secret)除外。这是针对常见名称的安全措施,并非保证。长值(证书、公钥)会以哈希形式存储。 - 只有完整读取的配置才会被存储。如果时间预算用尽,简报会说明已跳过比较,并保留之前的基线。
- 快照仅由
daily_brief写入,且每天只写入第一份,因此当天发生的变更会在第二天早上再次报告。系统会保留最近 30 份快照。 - 读取配置需要
view-realm和view-clients;身份提供商还需要view-identity-providers。如果缺少该权限,相应部分会从比较中排除,简报也会说明这一点。
验证设置
设置环境变量后,运行 --check,在将其接入 MCP 客户端之前确认身份验证有效:
export KEYCLOAK_URL=https://keycloak.example.com
export KEYCLOAK_REALM=my-realm
export KEYCLOAK_CLIENT_ID=keycloak-mcp
export KEYCLOAK_CLIENT_SECRET=your-secret
keycloak-mcp --check
# HTTP Request: POST https://keycloak.example.com/realms/my-realm/protocol/openid-connect/token "HTTP/1.1 200 OK"
# OK: authenticated to https://keycloak.example.com/admin/realms/my-realm
退出代码:0 表示成功,1 表示配置错误(缺少变量),2 表示身份验证错误。
IP 到站点的标记(可选)
如果希望工具输出中的 IP 地址带有站点名称标签,可将 KEYCLOAK_SITES_INI 指向一个 INI 文件。get_user_sessions、get_events 和 get_login_failures_by_ip 等工具会自动读取该文件;任何不在所声明范围内的地址都会标记为 external。不设置该变量时,IP 地址将按原样显示。
请参阅 sites.ini.example。最简文件:
[hq]
name = HQ (Tokyo)
ipv4 = 192.0.2.0/24, 198.51.100.0/24
ipv6 = 2001:db8:1::/48
[vpn]
name = VPN
ipv4 = 10.0.0.0/8, 172.16.0.0/12
每个 [section] 对应一个站点。name 是显示标签(未设置时使用 section 名称)。ipv4 / ipv6 接受以逗号分隔的 CIDR;单个主机使用 /32 或 /128。匹配按文件顺序进行——请将具体范围放在宽泛范围之前。
用法
Claude Code(插件)
此仓库同时也是一个单插件市场,因此 Claude Code 可以为你安装 服务:
/plugin marketplace add shigechika/keycloak-mcp
/plugin install keycloak-mcp@keycloak-mcp
此插件会启动 uvx keycloak-mcp,并读取 配置 中描述的相同环境变量;启动 Claude
Code 前请先导出这些变量。KEYCLOAK_REALM 默认使用 master,KEYCLOAK_SITES_INI 可以
不设置。
运行 Claude Code 的进程必须能在 PATH 中找到 uvx——登录
shell 通常已包含它,但通过 GUI 启动的应用可能没有;如果插件无法启动,请在系统范围内安装
uv。
Claude Code(手动)
在 .mcp.json 中:
{
"mcpServers": {
"keycloak-mcp": {
"type": "stdio",
"command": "keycloak-mcp",
"env": {
"KEYCLOAK_URL": "https://keycloak.example.com",
"KEYCLOAK_REALM": "my-realm",
"KEYCLOAK_CLIENT_ID": "keycloak-mcp",
"KEYCLOAK_CLIENT_SECRET": ""
}
}
}
}
Claude Desktop
在 claude_desktop_config.json 中:
{
"mcpServers": {
"keycloak-mcp": {
"command": "keycloak-mcp",
"env": {
"KEYCLOAK_URL": "https://keycloak.example.com",
"KEYCLOAK_REALM": "my-realm",
"KEYCLOAK_CLIENT_ID": "keycloak-mcp",
"KEYCLOAK_CLIENT_SECRET": ""
}
}
}
}
Codex
codex mcp add keycloak-mcp \
--env KEYCLOAK_URL=https://keycloak.example.com \
--env KEYCLOAK_REALM=my-realm \
--env KEYCLOAK_CLIENT_ID=keycloak-mcp \
--env KEYCLOAK_CLIENT_SECRET=your-secret \
-- keycloak-mcp
任何其他能够启动 stdio 服务的 MCP 客户端也采用相同方式:将
keycloak-mcp 设为命令,并在其环境中设置 KEYCLOAK_* 变量。
从 shell 启动
export KEYCLOAK_URL=https://keycloak.example.com
export KEYCLOAK_REALM=my-realm
export KEYCLOAK_CLIENT_ID=keycloak-mcp
export KEYCLOAK_CLIENT_SECRET=your-secret
keycloak-mcp
CLI
keycloak-mcp --version # Print version and exit
keycloak-mcp --help # Show usage and required environment variables
keycloak-mcp --check # Verify env vars and authentication, then exit
keycloak-mcp # Run the MCP STDIO server (default)
不带参数的模式是常规模式——MCP 客户端就是这样启动它的。
每日喷洒攻击报告(批处理)
spray_check 会检查截至当前时刻的滚动时间窗口,适用于巡查,但无法按日归档。为此,可通过计划任务对一个日历日运行相同的分析:
keycloak-mcp spray-report --date 2026-09-17 --tz Asia/Tokyo > 2026-09-17.json
- 时间窗口为
--tz指定时区中的[DATE 00:00, DATE+1 00:00)(默认:主机时区)。请使用 KeyCloak 服务日志所用的时区,因为dateFrom/dateTo会按该时区解读。事件会根据两端的时间戳筛选至此窗口。 - 一个 JSON 文档会写入 stdout;错误会写入 stderr,并以非零状态码退出(2 = 配置错误,1 = 获取/认证错误);发生错误时不会向 stdout 写入任何内容。
- 输出采用
spray_check的结构,其中external_ips包含所有至少有一个事件带有用户名的外部 IP(min_report_users=1);此外还有external_totals,用于统计所有外部 IP 的 LOGIN / LOGIN_ERROR 数量——包括失败事件没有用户名、因此不会出现在行中的 IP;以及fetch_complete、resolve_complete、coverage(first_event、last_event、head_gap_seconds、tail_gap_seconds)、schema、keycloak_mcp_version和sites_ini_sha256。只有在两个*_complete标志都为 true 且两端间隔都不大时,才应将当天视为最终结果;一端出现间隔通常意味着--tz与 KeyCloak 服务的时区不同。 - 如果
KEYCLOAK_SITES_INI未产生任何范围(此时每个 IP 都会被计为外部 IP),且未提供--allow-no-sites,则拒绝运行;如果KEYCLOAK_KNOWN_EGRESS中存在非 CIDR 格式的条目,也会拒绝运行。 - 此命令使用自身的限制(
--deadline 900、--max-events 1000000、--max-resolves 2000),而不是为 HTTP 网关后的工具调用设置的KEYCLOAK_DEADLINE/KEYCLOAK_MAX_EVENTS。 - 输出包含源 IP 和用户名。请勿将其用于任何公开发布的内容。
开发
git clone https://github.com/shigechika/keycloak-mcp.git
cd keycloak-mcp
# uv
uv sync --dev
uv run pytest -v
uv run ruff check .
# pip
python3 -m venv .venv
.venv/bin/pip install -e . && .venv/bin/pip install pytest pytest-cov respx ruff
.venv/bin/pytest -v
.venv/bin/ruff check .
实时冒烟测试
pytest 会对照固定数据检查逻辑,但无法告诉你某个工具是否已停止返回真实数据。scripts/smoke_test.py 会针对配置的 realm 测试所有已注册工具,并在结果为空、格式错误或出错时失败:
# needs KEYCLOAK_URL / KEYCLOAK_CLIENT_ID / KEYCLOAK_CLIENT_SECRET
uv run python scripts/smoke_test.py
uv run python scripts/smoke_test.py --only login_stats --traceback
- 只读。 所有会更改状态的工具(
reset_password、logout_user、set_user_enabled、reset_passwords_batch)都会按名称跳过,并且有测试确保这一点。报告只输出工具名称和状态,不会输出负载,也会遮盖服务端生成的错误文本(KeyCloak 会引用它收到查询的用户名);--traceback仍会在操作者自己的终端上显示完整文本。 - 用于标识真实用户、群组或 IP 的参数会在运行时发现,绝不会写入
scripts/smoke_probes.py。 - CI 会检查较简单的一半:注册的工具若没有探测规格,就会导致构建失败(
tests/test_smoke_probes.py),因此每次添加工具都会促使我们思考“如何确认它能正常工作?”。 scripts/smoke_harness.py是引擎,不包含任何 KeyCloak 相关知识:它在共用该引擎的服务之间保持一致,因此修复引擎问题后,只需同步该文件,无需修改此副本。
许可证
MIT
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。