adityamparikh opened a new pull request, #211: URL: https://github.com/apache/solr-mcp/pull/211
## Problem Someone following the security guides could configure an IdP, start the server in HTTP mode with security on, and still be unable to call a single tool from an MCP client. Two facts the docs got wrong or left out: 1. **The audience is derived from the request, not fixed.** `McpServerOAuth2Configurer` builds the expected `aud` per request from the scheme, host and port the client used plus `/mcp`. The Auth0 guide and the site page told readers to use `https://solr-mcp-api` as the API identifier, which produces tokens the server rejects with `The aud claim is not valid`. The Auth0 guide also stated that *no audience validation is performed*, listed a `login/oauth2/code/auth0` callback that a resource server does not have, and showed a stale issuer default. 2. **The server never answers an anonymous `/mcp` request with 401.** `/mcp` is `permitAll` and `@PreAuthorize` denies inside each tool with an HTTP 200 `Access Denied` result. Claude Code, the MCP Inspector and `mcp-remote` start OAuth only on a 401/403, so the client guides' "handles the OAuth2 flow automatically" never happens: the client reports **Connected**, lists every tool, and every call is denied, with no browser opening. The path that works is a bearer header. ## Changes - `docs/security/http.md`: how the audience is derived and how to read it from the running server; a new *Connecting an MCP client to a secured server* section with the observed status matrix and the header syntax for Claude Code, Inspector (CLI and web), `mcp-remote`, VS Code, Cursor and Zed; corrected production guidance for the empty issuer default. - `docs/security/keycloak.md`: audience spelling note (`localhost` vs `127.0.0.1`); a *Connecting an MCP Client* section that raises the 300 s token lifetime for a trial, gives the Claude Code and Inspector commands, and explains that `claude mcp list` says Connected either way so only a tool call proves the gate. - `docs/security/auth0.md`: rewritten around the resource-URI identifier, with run, verify and connect steps; phantom callback and the "no audience validation" claim removed. - `docs/clients/*.md` and the `docs/site/.../clients/*.md` mirror: the automatic-OAuth sentences replaced with header configuration per client (VS Code `inputs` + `headers`, Cursor `${env:…}`, Claude Desktop and JetBrains via `mcp-remote --header`, Inspector `--header`). - `docs/site/content/pages/mcp/security.md`, `dev-docs/DEPLOYMENT.md`, `scripts/get-auth0-token.sh` comments: brought in line. ## Verification Run on 2026-09-16 against Keycloak 26.0 (realm created with the guide's Quick Start block), Solr 9 and Claude Code 2.1.273, server started from the fat JAR with `PROFILES=http HTTP_SECURITY_ENABLED=true OAUTH2_ISSUER_URI=http://localhost:8180/realms/solr-mcp`: | Check | Result | |---|---| | Anonymous `initialize`, `tools/list` | 200 | | Anonymous `tools/call list-collections` | 200, `Access Denied`, `isError: true` | | With token, `tools/call list-collections` | `["books","films"]` | | Same token sent to `http://127.0.0.1:8080/mcp` | 401 `The aud claim is not valid` | | Garbage token on `tools/list` | 401 `Malformed token` | | `/actuator/metrics` with / without token | 200 / 401 | | Inspector CLI `--header` / without | tool result / `Access Denied` | | Claude Code `claude mcp add … --header`, then a `list-collections` call via `claude -p` | `["books","films"]` | | Claude Code with the URL only | `✔ Connected`, tool call returns `Access Denied`, no OAuth prompt | | `PUT /admin/realms/solr-mcp {"accessTokenLifespan":3600}` | 204, next token `exp - iat = 3600` | The Auth0 steps follow the same server-side rules but were not re-run against a live tenant. Interactive browser login (`claude mcp login`, the Inspector's OAuth flow) needs an IdP client registration and is stated as out of scope rather than described. `./gradlew build` (JDK 25): 421 tests, 0 failures, 0 skipped; `rat` and `spotlessCheck` pass. ## Overlap with open PRs - #200 adds a paragraph a few lines above the sections this PR rewrites in the same client guides; the hunks are adjacent, not identical. - #191 and #192 change other parts of `keycloak.md` (Spring AI client step, compose-based Quick Start). The section added here sits between them. - #210 adds a bullet to the *Forbidden* list in `http.md`, which this PR does not touch. 🤖 Generated with [Claude Code](https://claude.com/claude-code) -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
