Skip to main content
Glama
Keycloak Realm Inspector

Keycloak Realm Inspector

by atulram

Keycloak Realm Inspector

MCP-сервер, который является исключительно защищённым ресурсом OAuth 2.0 и предназначен для доклада на KeyConf 2026 о Client ID Metadata Documents (draft-ietf-oauth-clent-id-metadata-document-02).

Он ничего не выпускает — ни /authorize, ни /token, ни секрета клиента. Токены выпускает Keycloak; этот сервер проверяет их по JWKS Keycloak, обслуживает discovery по RFC 9728 и предоставляет три инструмента, которые делают CIMD видимым изнутри сервера ресурсов.

Три стороны, намеренно разделённые

Keycloak

сервер авторизации. --features=cimd, KC_HTP_RELATIVE_PATH=/auth, порт 8080

MCP-клиент

публикует свой документ метаданных по URL, которым управляет. Это URL и есть его client_id. Обслуживается на порту 9000

Это сервер

защищённый ресурс. Пор 9001

Это сервер не размещает, не читает и не загружает документ метаданных клиента. Здесь нет маршрута /client-metadata.json и нет локальной копии. Keycloak загружает это URL по сети во время /authorize. В этой кодовой базе URL появляется только как непрозрачная строка в утверждении azp — в этом весь смысл демо и причина, по которой документ обслуживается отдельным процессом на отдельном порту.

Related MCP server: Akeyless MCP Interop (OpenID AIIM)

Запуск

Предполагается, что Keycloak запущен, realm создан, а клиентская политика CIMD уже применена. Клиент, его документ метаданных и вспомогательный инструмент для токенов находятся рядом с этим репозиторием в ../cimd-demo/auth-server/.

# terminal 1 — the client's document. Its own party, its own port.
cd ../cimd-demo/auth-server && python -m http.server 9000

# terminal 2 — the resource server
pip install -r requirements.txt
python server.py                     # http://localhost:9001

# terminal 3 — get a token, then point an MCP client at :9001/mcp
cd ../cimd-demo/auth-server && python get-token.py

Поток

  1. Клиент вызывает /mcp без токена и получает 401 с WWW-Authenticate: Bearer ..., resource_metadata="…".

  2. Клиент переходит по этой ссылке на /.well-known/oauth-protected-resource/mcp и узнаёт, какой сервер авторизации выпускает действительные токены.

  3. Клиент аутентифицируется напрямую в Keycloak, используя URL своего документа метаданных как client_id. Keycloak загружает этот URL и материализует клиента.

  4. Клиент повторяет запрос к /mcp с bearer-токеном.

RFC 9728 §3.1 помещает сегмент well-known между хостом и путём ресурса, поэтому документ обнаружения находится по адресу /.well-known/oauth-protected-resource/mcp. Обращение к пути без этого сегмента возвращает 404.

Инструменты

  • whoami() — sub, preferred_username, azp, scope, exp, iss из токена вызывающего, плюс registered_via_cimd. Читает только токен; без обращения к Keycloak, поэтому работает, даже если учётные данные администратора неверны.

  • list_clients(only_cimd=False) — клиенты realm; client_id в форме URL сортируются в начало, чтобы вызывающий клиент оказался на первой строке.

  • get_client_metadata(client_id) — собственное представление клиента в Keycloak. Это производное представление Keycloak, а не опубликованный документ.

CIMD определяется по client_id, начинающемуся с http:// или https://. Отличительный признак — client_id в форме URL.

get_client_metadata удаляет secret и registrationAccessToken перед возвратом. Оба являются действующими bearer-учётными данными, а этот вывод попадает на проектор и в запись.

Два известных ограничения

Индикаторы ресурса (RFC 8707). audience= присутствует, но закомментирован в JWTVerifier. Ограничение токена по аудитории потребовало бы, чтобы клиент отправлял resource в /authorize, а реализация CIMD в Keycloak 26.7 этот параметр пока не поддерживает (keycloak#45106, keycloak#45284). Поэтому токен несёт обычную для Keycloak аудиторию account, и этот сервер не может ограничивать аудиторию. Намеренно оставлено видимым.

Проверка scope не выполняется. required_scopes не задано, поэтому принимается любой токен из realm с корректной подписью и не истёкшим сроком действия — включая токен, полученный через password grant от admin-cli. Это удобно на сцене: вызовите whoami() с токеном администратора (azp — непрозрачная строка), а затем с CIMD-токеном (azp — URL): тот же инструмент, тот же сервер, разница в одном поле. Это также означает, что нужно точно знать, какой токен у вас в руках.

Конфигурация

Всё через os.getenv, с localhost по умолчанию.

Переменная

По умолчанию

KEYCLOAK_BASE_URL

http://localhost:8080/auth

KEYCLOAK_REALM

cimd-demo

KEYCLOAK_ISSUER

{base}/realms/{realm}

KEYCLOAK_JWKS_URI

{issuer}/protocol/opend-connect/certs

MCP_BASE_URL

http://localhost:9001

PORT

9001

KEYCLOAK_ADMIN

admin

KEYCLOAK_ADMIN_PASSWORD

admin

KEYCLOAK_ISSUER и KEYCLOAK_JWKS_URI настраиваются по отдельности, а не выводятся друг из друга: в Kubernetes эмитент — это публичный URL, поэтому iss совпадает с тем, что видят клиенты, тогда как запрос JWKS должен идти к внутрикластерному Service.

Версия fastmcp зафиксирована точно. API аутентификации меняется — resource_server_url стал base_url, AccessToken.claims появился в 2.11.3 — а allowed_client_redirect_uris не является параметром RemoteAuthProvider ни в одном релизе, вопреки документации. Он относится к OAuthProxy, который это сервер не использует.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      A
      quality
      B
      maintenance
      Enables MCP-capable clients to inspect and manage Keycloak realms, users, clients, roles, and groups with layered security modes, realm allowlisting, protected realms, delete gating, dry-run, and audit logging.
      8
      74 npm
      1
      MIT
    • F
      license
      B
      quality
      B
      maintenance
      Enables MCP clients to authenticate through OIDC and call protected tools while rejecting unauthenticated requests.
      26
      -