Skip to content

Reference

health_check()

Seven keys are present on every call:

Key Meaning
status healthy / degraded / error
service Always keycloak-mcp
version Package version
keycloak_url Configured URL (empty string when unset)
realm Configured realm (empty string when unset)
keycloak_version Always null — not exposed by a cheap call
auth unknown / ok / missing-env / error

detail is added only on degraded or error, with the reason: a missing environment variable, or the exception type (and HTTP status, when the backend returned one) for a genuine authentication failure. Internal URLs and raw HTTP payloads are deliberately kept out of it.

Lightweight by design: it acquires one Client Credentials token — the lightest possible proof the service account can talk to the realm — and scans nothing else. Safe to call at session start or after a tool-call timeout.

Tool index

Tool Purpose
count_users Total user count in the realm
search_users(query, max_results=20) Partial-match search: username, email, first/last name
get_user(username) Full detail for an exact username
get_user_sessions(username) Active sessions, timestamps in local time
logout_user(username) Writes. Ends all active sessions for one user
set_user_enabled(username, enabled) Writes. Enable/disable; disabling does not end existing sessions
reset_password(username, password, temporary=False) Writes. Reset one user's password
reset_passwords_batch(csv_text, temporary=False) Writes. Bulk reset from username,password CSV
get_user_credentials(username) Configured credential types; an otp entry means TOTP/HOTP is set up
get_totp_users(max_users=0) Realm-wide TOTP adoption. N+1 (one call per user); bounded by max_users or KEYCLOAK_MAX_USERS
list_user_groups(username) Groups a user belongs to
list_users_by_group(group_name, max_results=100) Members of a group
get_brute_force_status(username) Whether a user is currently locked by brute-force detection
get_realm_security_defenses() Brute-force policy and thresholds, password policy, browser security headers
get_login_failures_by_ip(date_from, date_to, top=20) Failure counts ranked by source IP
get_ip_activity(ip_address, event_types, date_from, date_to, max_timeline=200) Exhaustive per-IP investigation (structured JSON)
detect_login_loops(date_from, date_to, threshold=10, window_seconds=60, top=20) Users logging in faster than threshold per window_seconds
get_events(event_type, username, client_id, ip_address, date_from, date_to, max_results=50) Filtered event search; username resolved to user ID internally
get_login_stats(date_from, date_to) Success/failure totals, fully paginated
get_login_stats_by_hour(date_from, date_to) Logins bucketed by hour of day, local time
get_login_stats_by_client(date_from, date_to) Logins bucketed by client (SP)
get_password_update_events(date_from, date_to, max_results=100) UPDATE_PASSWORD history
get_admin_events(operation_types, resource_types, resource_path, date_from, date_to, max_results=50, max_repr=500) Admin-driven changes; get_events never sees these
get_user_attribute_history(username, date_from, date_to, max_results=100, max_repr=500) Admin events scoped to one user
get_session_stats() Active session count per client
get_client_sessions(client_id, max_results=100) Active sessions for one client
list_clients() SAML and OIDC clients in the realm
get_realm_roles() Realm-level roles
daily_brief(since_hours=18, ip_failure_threshold=50) Morning summary: login stats, brute-force IPs, sessions, password updates, admin events

See Security investigation for what the write tools actually do, how to read get_ip_activity's output, and the deadline / partial-result mechanism shared by the heavy scans.

daily_brief

One Markdown report covering login stats, IPs over ip_failure_threshold failures (flagged WARNING), active sessions, password updates, and admin events, all within since_hours (default 18 — roughly the previous afternoon for a 09:00 morning run). A backend connection failure renders the whole report as ## CRITICAL — <ExceptionType> rather than a partial brief with gaps unaccounted for.

get_admin_events and get_user_attribute_history accept max_repr, which controls how much of the KeyCloak "representation" payload (the changed object, as JSON text) is included: positive truncates to that many characters (default 500), 0 omits it, negative includes it in full.

get_events vs. get_admin_events

get_events sees user events — logins, logouts, password changes performed by the user themself. Actions performed by an administrator, or by a service account writing a custom attribute, do not appear there at all; get_admin_events is the endpoint that does. get_user_attribute_history is get_admin_events pre-scoped to one user, useful for confirming when an automated pipeline last wrote something like a provisioning_flag attribute.

CLI

keycloak-mcp            # start the MCP server (stdio; default, no arguments)
keycloak-mcp --version  # print version and exit
keycloak-mcp --help     # show usage and required environment variables
keycloak-mcp --check    # verify environment and authentication, then exit

Exit codes for --check: 0 success, 1 a required environment variable is missing, 2 authentication failed.