This guide is the advanced companion to the beginner Weather Agent demo. It keeps the same weather agent and MCP tool images, but turns on the full platform story:
- Outbound token exchange from the agent to the weather tool (RFC 8693), so the tool receives an access token minted for its audience.
- AuthBridge on the tool as well as the agent, so JWT validation happens at the tool’s Envoy ingress (ext_proc) before traffic reaches the MCP server.
- Audience alignment: the token exchange
target_audienceis the weather tool’s SPIFFE-based Keycloak client ID (the same string written to/shared/client-id.txtby client-registration). That matches what AuthBridge expects for inbound JWTaud, so you can demonstrate ingress verification without custom application code in the tool.
The beginner demo-ui.md is unchanged: new users can follow it without Keycloak scope tuning or token exchange.
For a UI-driven walkthrough that also covers GitHub PATs and privileged scopes, see GitHub Issue Agent demo-ui. This advanced weather demo is smaller in scope but hits the same exchange + validate pattern with a trivial MCP backend.
- Agent identity — SPIFFE registration with Keycloak for
weather-service-advanced. - Inbound validation on the agent — same as the beginner demo.
- Transparent token exchange — when the agent calls the tool, AuthBridge exchanges the caller’s token for one whose audience includes the tool’s SPIFFE client ID.
- Inbound validation on the tool — AuthBridge on the tool pod validates
iss, signature (JWKS), andaudbefore the MCP process sees the request. Logs show[Inbound]/Token validatedonenvoy-proxy(or the combinedauthbridgecontainer when that feature gate is enabled). - No GitHub tokens or PATs — only Keycloak, SPIRE, and public weather APIs.
On current platforms, three things are required together for the full demo:
-
AgentRuntime— The mutating webhook skips injection unless a matchingagent.kagenti.dev/v1alpha1AgentRuntimeexists for the Deployment. Applyk8s/agentruntime-weather-tool-advanced.yamlandk8s/agentruntime-weather-service-advanced.yaml(the deploy script applies them when the CRD is present) and restart the Deployments so new pods are admitted after the CR exists. -
AgentRuntime
spec.typefor the tool — Ifspec.type: tool, the operator can relabel the pod tokagenti.io/type: tool, and theinjectToolsfeature gate is off by default, so no AuthBridge is injected. This demo usesspec.type: agentfor the tool’sAgentRuntimeso the pod keepskagenti.io/type: agent(the image is stillweather_tool; only the API classification changes). -
kagenti.io/client-registration-inject: "true"label (not annotation) on the pod template so the legacykagenti-client-registrationsidecar runs and registers the SPIFFE client in Keycloak. Without it, the operator’s default is operator-managed registration andsetup_keycloak ... --wait-tool-clientmay never see the tool client.
┌─────────────────────────────────────────────────────────────────────────────┐
│ Kubernetes (e.g. namespace team1) │
│ │
│ ┌────────────────────────────────────────────────────────────────────────┐ │
│ │ weather-service-advanced (agent + AuthBridge + SPIRE) │ │
│ │ Inbound: validate user JWT (aud = agent SPIFFE) │ │
│ │ Outbound: match host weather-tool-advanced-mcp → token exchange │ │
│ └───────────────────────────────┬────────────────────────────────────────┘ │
│ │ Bearer: exchanged token (aud ⊇ tool SPIFFE)│
│ ▼ │
│ ┌────────────────────────────────────────────────────────────────────────┐ │
│ │ weather-tool-advanced (MCP + AuthBridge + SPIRE) │ │
│ │ Inbound: validate exchanged JWT at Envoy / ext_proc │ │
│ │ mcp container: streamable HTTP MCP (port 8000) │ │
│ └────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
Unlike the GitHub MCP tool, the weather tool listens on port 8000. AuthBridge’s
gRPC ext_proc listener uses 9090 inside the pod, so there is no port clash
with the pattern described for github-tool on 9090 in the
GitHub Issue demo-ui.
Same platform assumptions as GitHub Issue demo-ui prerequisites:
- Kagenti installed with Keycloak (
keycloaknamespace), SPIRE, and the admission webhook that injects AuthBridge. - Target namespace (default
team1) with installer-providedauthbridge-config,envoy-config,spiffe-helper-config, andkeycloak-admin-secret. - Python 3.10+ and
pip install -r AuthBridge/requirements.txtfor the Keycloak helper script. - For chat-style verification: an LLM (Ollama on the host with the model referenced in the Deployment, or OpenAI with keys in a Secret) and outbound port 11434 excluded on the agent (see below).
To avoid colliding with an existing beginner weather-service /
weather-tool, this demo uses:
| Kind | Name |
|---|---|
| Deployments | weather-tool-advanced, weather-service-advanced |
| Services | weather-tool-advanced-mcp (port 8000), weather-service-advanced (8080→8000) |
| ServiceAccounts | weather-tool-advanced, weather-service-advanced |
SPIFFE IDs (trust domain localtest.me):
- Agent:
spiffe://localtest.me/ns/team1/sa/weather-service-advanced - Tool:
spiffe://localtest.me/ns/team1/sa/weather-tool-advanced
If you change namespace or ServiceAccount names, update
k8s/configmaps-advanced.yaml (host, target_audience) and re-run
setup_keycloak_weather_advanced.py with matching -n, -a, and -t.
From the AuthBridge directory (repository path AuthBridge/):
cd AuthBridge
python -m venv venv
source venv/bin/activate
pip install -r requirements.txtPort-forward Keycloak if you use the default public URL:
kubectl port-forward service/keycloak-service -n keycloak 8080:8080Run setup after the tool pod has registered in Keycloak, or pass
--wait-tool-client so the script waits for the tool SPIFFE client:
python demos/weather-agent/setup_keycloak_weather_advanced.py \
-n team1 \
-a weather-service-advanced \
-t weather-tool-advanced \
--wait-tool-clientRe-run once after the agent is running so optional exchange scopes attach to the agent’s dynamic client (same pattern as the GitHub demo).
The script:
- Ensures realm default scope
agent-team1-weather-service-advanced-audadds the agent SPIFFE to access-tokenaud(UI / alice tokens). - Adds optional scope
weather-tool-exchange-audwith an audience mapper to the tool SPIFFE (used during token exchange). - Enables
standard.token.exchange.enabledon the tool (and agent) Keycloak clients once they exist.
kubectl apply -f AuthBridge/demos/weather-agent/k8s/configmaps-advanced.yamlIf you are not using team1, edit metadata.namespace and the target_audience
SPIFFE string first.
kubectl apply -f AuthBridge/demos/weather-agent/k8s/weather-tool-advanced.yaml
kubectl rollout status deployment/weather-tool-advanced -n team1 --timeout=300s
python demos/weather-agent/setup_keycloak_weather_advanced.py \
-n team1 --wait-tool-client
kubectl apply -f AuthBridge/demos/weather-agent/k8s/weather-service-advanced.yaml
kubectl rollout status deployment/weather-service-advanced -n team1 --timeout=420s
python demos/weather-agent/setup_keycloak_weather_advanced.py -n team1The sample agent manifest sets LLM_API_BASE to host.docker.internal:11434 and
annotates the pod with kagenti.io/outbound-ports-exclude: "11434". If you
import through the UI instead, set Outbound Ports to Exclude to 11434 the
same way as in demo-ui.md.
You can mirror the GitHub Issue UI flow with these substitutions:
- Tool: enable AuthBridge sidecar injection and SPIRE; image
ghcr.io/kagenti/agent-examples/weather_tool; streamable HTTP; Service name pattern ending in-mcpthat matches thehostentry inconfigmaps-advanced.yaml. - Agent: image
ghcr.io/kagenti/agent-examples/weather_service;MCP_URLpointing at the advanced MCP service;authproxy-routeslist shape as in Kagenti version notes.
If the UI backend cannot yet express the route list, apply
k8s/configmaps-advanced.yaml with kubectl and avoid duplicating routes in the UI.
kubectl get pods -n team1 | grep advanced
# Expect 4/4 (or combined sidecar variant) for both workloads when injection is on.kubectl logs deployment/weather-tool-advanced -n team1 -c envoy-proxy 2>&1 | grep "\[Inbound\]"
# or -c authbridge when combinedSidecar is enabledkubectl logs deployment/weather-service-advanced -n team1 -c envoy-proxy 2>&1 | grep -E "Resolver|exchange|Injecting token"Follow demo-ui.md — Step 6, but use:
- Service:
weather-service-advanced:8080 - SPIFFE / client lookup:
spiffe://localtest.me/ns/team1/sa/weather-service-advanced
The script deploy_and_verify_advanced.sh applies the manifests, runs the
Keycloak setup (including waiting for the tool client), waits for rollouts, and
verifies end-to-end without relying on an LLM:
- Password grant for user alice (create
alice/alice123if missing — the setup script creates her). - RFC 8693 token exchange using the agent Keycloak client credentials as
the authenticated client and alice’s token as the
subject_token, withaudienceset to the tool SPIFFE and scopeopenid weather-tool-exchange-aud. - HTTP
POSTtohttp://weather-tool-advanced-mcp:8000/mcpwith a minimal JSON-RPCinitializebody. The tool uses streamable HTTP; sendAccept: application/json, text/event-stream(otherwise the MCP server often returns 406 even when AuthBridge already accepted the JWT). HTTP 401 is a hard failure; a 2xx response means the JWT was accepted and the initialize handshake completed. - The same
initializerequest without anAuthorizationheader must return 401 (AuthBridge rejects the call before the MCP app runs).deploy_and_verify_advanced.shchecks this as a negative test.
Run from anywhere:
./AuthBridge/demos/weather-agent/deploy_and_verify_advanced.shEnvironment variables:
| Variable | Purpose |
|---|---|
NAMESPACE |
Target namespace (default team1) |
SKIP_DEPLOY=1 |
Only run verification (resources must already exist) |
KC_INTERNAL |
Keycloak base URL inside the cluster (default http://keycloak-service.keycloak.svc:8080) |
KC_USER_CLIENT_ID |
Realm public client for password grant (default weather-advanced-e2e, created by the Keycloak script; the kagenti UI client often has direct access grants disabled) |
KC_USER_CLIENT_SECRET |
Set only if the password client is confidential |
KEYCLOAK_ADMIN_USERNAME / KEYCLOAK_ADMIN_PASSWORD |
For admin REST calls from the verify pod |
The script also warns if it cannot find obvious inbound/outbound log markers
(lines differ slightly when combinedSidecar is enabled).
kubectl delete deployment -n team1 \
weather-service-advanced weather-tool-advanced --ignore-not-found
kubectl delete svc -n team1 \
weather-service-advanced weather-tool-advanced-mcp --ignore-not-found
kubectl delete sa -n team1 \
weather-service-advanced weather-tool-advanced --ignore-not-foundKeycloak clients for the SPIFFE IDs can be removed from the admin console if you no longer need them.
| Symptom | Likely cause | Mitigation |
|---|---|---|
Tool pod CrashLoopBackOff (container mcp) |
The weather_tool image runs as UID 1001; a securityContext that forces a different UID (e.g. only runAsNonRoot: true with a project-assigned user on OpenShift, or a mismatch with chowned /app in the image) prevents uv run from reading the app tree |
The manifests set runAsUser / runAsGroup / fsGroup: 1001 to match the upstream Dockerfile. Re-apply weather-tool-advanced.yaml. If it still fails, run kubectl logs -n team1 deploy/weather-tool-advanced -c mcp --previous and kubectl describe pod for OOMKilled or CreateContainerError. |
invalid_scope / 503 on agent |
Optional exchange scope not on agent client | Re-run setup_keycloak_weather_advanced.py after the agent is running |
invalid audience / 401 on agent |
Default agent audience scope missing on UI client | Re-run setup; log out and back in to the UI |
| 401 on tool MCP | Wrong target_audience or scope mapper |
target_audience must equal tool SPIFFE; scope weather-tool-exchange-aud must map that audience |
| Token exchange denied | Tool Keycloak client missing standard.token.exchange.enabled |
Re-run setup with --wait-tool-client after the tool pod registers |
No [Inbound] log line |
Combined sidecar logging format | Grep for Token validated or increase log window |
See also the operational table in the AuthBridge testing skill used for this repo.
| File | Role |
|---|---|
| k8s/configmaps-advanced.yaml | authproxy-routes for token exchange |
| k8s/weather-tool-advanced.yaml | Tool Deployment + Service + SA |
| k8s/weather-service-advanced.yaml | Agent Deployment + Service + SA |
| setup_keycloak_weather_advanced.py | Keycloak realm tuning |
| deploy_and_verify_advanced.sh | One-shot deploy + CI-style verification |