MCP Clients¶
Nodexa exposes MCP-compatible tools through the admin interface. MCP clients connect to the admin origin, not directly to the backend.
Endpoint¶
Use this endpoint for the built-in Nodexa tools:
For local testing:
Do not add /api to this path. The admin interface proxies /mcp-gateway/* to the backend and keeps the backend URL private.
Authentication¶
MCP clients authenticate with standard OAuth bearer tokens. The gateway publishes OAuth Protected Resource Metadata at:
The Keycloak client used by MCP clients is:
This client should be a public OAuth client using Authorization Code + PKCE.
Keycloak Setup¶
Create the MCP OAuth client in the same Keycloak realm used by the Nodexa backend.
For Nodexa Cloud, the realm issuer is:
In Keycloak Admin Console:
- Select the
nodexa-cloudrealm. - Open Clients.
- Create or update a client with Client ID
nodexa-mcp. - Use OpenID Connect.
- Set Client authentication to Off. This must be a public client.
- Enable Standard flow.
- Disable Direct access grants for production.
- Set PKCE Code Challenge Method to
S256. - Add valid redirect URIs:
For local Claude Code testing, the redirect URI uses a random localhost port, for example:
The wildcard redirect above allows those dynamic callback ports.
If Keycloak still rejects the redirect during testing, add the exact callback URI shown in the browser error, for example:
Set Web origins to:
or explicitly:
Protocol Mappers¶
The access token issued to MCP clients must contain the same organization and audience claims that the backend validates.
Add a Group Membership mapper:
| Setting | Value |
|---|---|
| Mapper type | Group Membership |
| Token claim name | groups |
| Add to access token | On |
| Full group path | On |
This lets the backend derive the organization from group paths such as:
Add an Audience mapper so tokens issued to nodexa-mcp are accepted by the backend:
| Setting | Value |
|---|---|
| Mapper type | Audience |
| Included Client Audience | nodexa-cloud-backend |
| Add to access token | On |
The backend should also accept the MCP client as an additional audience:
OIDC_CLIENT_ID=nodexa-cloud-backend
OIDC_EXTRA_AUDIENCES=nodexa-mcp
OIDC_VALIDATE_AUDIENCE=true
OIDC_GROUPS_CLAIM=groups
If your backend client ID is different, use that client ID in both the audience mapper and OIDC_CLIENT_ID.
Claude Code¶
Remove any old Nodexa MCP server entry, then add the new gateway endpoint:
claude mcp remove nodexa -s local
claude mcp add-json nodexa '{"type":"http","url":"http://localhost:4000/mcp-gateway/nodexa","oauth":{"clientId":"nodexa-mcp"}}' -s local
For production, replace the URL with your admin origin:
claude mcp add-json nodexa '{"type":"http","url":"https://your-admin.example.com/mcp-gateway/nodexa","oauth":{"clientId":"nodexa-mcp"}}' -s local
Verify the configured URL:
On first use, Claude Code should start the OAuth flow and redirect you to Keycloak.
Quick HTTP Check¶
Without a token, the endpoint should return 401 Unauthorized and include a WWW-Authenticate header pointing to the metadata URL:
curl -i -X POST http://localhost:4000/mcp-gateway/nodexa \
-H "Content-Type: application/json" \
--data '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}'
Troubleshooting¶
Keycloak shows client not found¶
This means Keycloak could not find an enabled client with:
in the configured realm. Check that the client exists in the nodexa-cloud realm and that the client ID is exactly nodexa-mcp.
Keycloak shows invalid redirect_uri¶
Add the callback URI used by the MCP client. Claude Code uses a random localhost port:
If you previously configured http://localhost:*/callback, replace it with http://localhost:*. Keycloak wildcard matching is safest when the * is at the end of the URI pattern. You can also add the exact URI from the error page temporarily, for example http://localhost:56963/callback.
The backend rejects the token¶
Check the access token claims. It must include:
groups, with the user's organization group path- an audience accepted by the backend, usually
nodexa-cloud-backendand/ornodexa-mcp
Decode the token locally:
OAuth metadata points to the wrong host¶
The metadata response should use the admin origin, not the backend origin:
If it shows localhost:3001, the request is reaching the backend directly instead of the admin proxy.