feat(compose): provision Keycloak with the realm the http profile needs - #192
Open
adityamparikh wants to merge 3 commits into
Open
adityamparikh wants to merge 3 commits into
adityamparikh wants to merge 3 commits into
Conversation
adityamparikh
force-pushed
the
feat/keycloak-compose
branch
from
September 12, 2026 16:09
90530b5 to
b2a6d72
Compare
epugh
reviewed
Sep 16, 2026
Comment on lines
+89
to
+90
| healthcheck: | ||
| test: [ "CMD-SHELL", "exec 3<>/dev/tcp/localhost/9000 && echo -e 'GET /health/ready HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n' >&3 && cat <&3 | grep -q '\"status\": \"UP\"'" ] |
Contributor
There was a problem hiding this comment.
@adityamparikh can you jsut investigate this? It may be we don't have curl or wget available in the image? so we do this very interesting thing?
epugh
requested changes
Sep 16, 2026
epugh
left a comment
Contributor
There was a problem hiding this comment.
Check one thing on the keyclaok htealh check!
Comment on lines
+89
to
+90
| healthcheck: | ||
| test: [ "CMD-SHELL", "exec 3<>/dev/tcp/localhost/9000 && echo -e 'GET /health/ready HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n' >&3 && cat <&3 | grep -q '\"status\": \"UP\"'" ] |
Contributor
There was a problem hiding this comment.
something more like https://github.com/querqy/chorus/blob/main/docker-compose.yml#L299C1-L300C70
The http profile authenticates against Keycloak, but nothing started one. A developer had to run the container by hand and then script a realm, a client and an audience mapper before the server would boot — and the mapper is easy to miss, because skipping it produces a token that is issued normally and then refused with 401. compose.yaml now defines a keycloak service that imports keycloak/solr-mcp-realm.json, so the realm, both clients and the mapper exist before the server asks for a token. The import covers what the Quick Start created by hand: solr-mcp-service (confidential, service accounts) for machine-to-machine callers, solr-mcp-client (public) for MCP Inspector, and testuser. The credentials in it are development credentials, committed on purpose; a real deployment provisions its own. The healthcheck is load-bearing rather than decoration. The server resolves the issuer while building its JWT decoder and fails to boot if the realm is not yet answering, which is the ordering constraint keycloak.md warns about; declaring a healthcheck makes Spring Boot's compose support wait for the container instead. Keycloak's image ships neither curl nor wget, so the probe goes through bash's /dev/tcp against the management port. Verified by running the service from this compose file: the container reaches healthy, the realm resolves, and both grants return tokens carrying aud http://localhost:8080/mcp — client_credentials for the service client and the password grant for the imported test user. Signed-off-by: Aditya Parikh <aditya.m.parikh@gmail.com> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011nUD34DFfoJeyQRTquPy7a
The service comment said it was started only by the http profile, but it had no profiles key, so a plain docker compose up -d for STDIO users pulled Keycloak, bound 8180 and waited on its healthcheck. The service now sits in the http compose profile, which application-http.properties activates via spring.docker.compose.profiles.active. The Quick Start no longer says bootRun alone is enough while also requiring OAUTH2_ISSUER_URI, and it tells compose users to reuse solr-mcp-service instead of creating a client by hand. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CiUHyyXLTo9ATdgg8eRFZJ Signed-off-by: Aditya Parikh <aditya.m.parikh@gmail.com>
…ommendation The Keycloak container image (UBI 9 Minimal) intentionally ships without curl or wget, and health endpoints are exposed on the dedicated management port 9000. Switch the healthcheck probe to Keycloak's official Containerfile HEALTHCHECK pattern (https://www.keycloak.org/observability/health#_healthcheck) using bash socket redirection. Signed-off-by: Aditya Parikh <aditya.m.parikh@gmail.com> Co-authored-by: Junie <junie@jetbrains.com>
adityamparikh
force-pushed
the
feat/keycloak-compose
branch
from
September 24, 2026 14:46
5dbcaf9 to
0c75c1d
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The
httpprofile authenticates against Keycloak, but nothing started one. A developer had to run the container by hand and then script a realm, a client and an audience mapper before the server would boot. The mapper is easy to miss and fails quietly: Keycloak does not honour the RFC 8707resource=parameter, so without it a token is issued normally andvalidateAudienceClaim(true)answers 401.Change
compose.yamlgains akeycloakservice that importskeycloak/solr-mcp-realm.jsonat startup, so the realm, both clients and the mapper exist before the server asks for a token:solr-mcp-servicesolr-mcp-clienttestuser/testpasswordkeycloak.mdBoth clients carry the audience mapper for
http://localhost:8080/mcp. The credentials are development credentials, committed on purpose; a real deployment provisions its own.The service sits behind the
httpcompose profile, whichapplication-http.propertiesactivates throughspring.docker.compose.profiles.active, so a plaindocker compose up -dfor STDIO users does not pull Keycloak or wait on its healthcheck. Start it by hand withdocker compose --profile http up -d.The healthcheck is load-bearing. The server resolves the issuer while building its JWT decoder and fails to boot if the realm is not answering, so Spring Boot's compose support must wait for the container to be healthy. Keycloak's image ships neither
curlnorwget, so the probe uses bash's/dev/tcpagainst the management port.docs/security/keycloak.mdQuick Start points at the imported realm and tells compose users to reusesolr-mcp-serviceinstead of creating a client by hand. The manual walkthrough below it binds its own container to the same port, 8180, so anyone following that path should start the server withSPRING_DOCKER_COMPOSE_ENABLED=falserather than bringing up a second Keycloak.Verification
Against the running container: the realm's OpenID configuration resolves, a
client_credentialstoken onsolr-mcp-serviceand a password grant onsolr-mcp-clientboth carryaud: http://localhost:8080/mcp, and./gradlew buildpasses. Not covered: an end-to-end call against a running MCP server.🤖 Generated with Claude Code