feat(compose): provision Keycloak with the realm the http profile needs - #192
Open
adityamparikh wants to merge 1 commit into
Open
feat(compose): provision Keycloak with the realm the http profile needs#192adityamparikh wants to merge 1 commit into
adityamparikh wants to merge 1 commit into
Conversation
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
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 runthe container by hand and then script a realm, a client and an audience mapper before the server
would boot.
The mapper is the part that is easy to miss, and it fails quietly:
Keycloak does not honour the RFC 8707
resource=parameter, sovalidateAudienceClaim(true)findsno matching
aud. Nothing in the token request hints at a problem.Change
compose.yamlgains akeycloakservice that importskeycloak/solr-mcp-realm.jsonat startup, sothe 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-servicesolr-mcp-clienttestuser/testpasswordkeycloak.mdBoth clients carry the audience mapper for
http://localhost:8080/mcp. The credentials aredevelopment credentials, committed on purpose; a real deployment provisions its own.
The healthcheck is load-bearing
keycloak.mddocuments an ordering constraint — "Verify the realm resolves BEFORE starting theserver… The server fails to boot if this is not a 200." The server resolves the issuer while
building its JWT decoder, so it cannot start before Keycloak is answering.
Declaring a healthcheck lets Spring Boot's compose support wait for the container instead of leaving
that to the developer. Keycloak's image ships neither
curlnorwget— I checked — so the probegoes through bash's
/dev/tcpagainst the management port.Verification
Run from this compose file (on a spare port, to avoid colliding with an already-running instance):
healthyvia the declared healthcheckrealms/solr-mcp/.well-known/openid-configuration200client_credentialsonsolr-mcp-serviceaud: ["http://localhost:8080/mcp", "account"]solr-mcp-clientastestuseraud: "http://localhost:8080/mcp"Not covered: an end-to-end call against a running MCP server. The test instance was on a spare port,
so its
isswould not match a server configured for:8180. The audience claim — what this changeexists to guarantee — is verified above.
🤖 Generated with Claude Code
https://claude.ai/code/session_011nUD34DFfoJeyQRTquPy7a