Repository navigation
examples: agentgateway in front of the mesh, with a script that checks each case - #802
Conversation
The realm mesh has the agentmesh client with a groups claim and an audience mapper, so the access tokens an agent sends to agentgateway carry aud agentmesh; alice is in group devs and bob in group ops. The realm elsewhere is a second issuer the control plane does not trust. Tokens live 60 seconds, so the script can check expiry quickly.
Nodes may publish mcp://*, and calc-user, granting mcp://calc, is bound to group:devs only. The provider node publishes the calculator of ../calc-mcp as mcp://calc.
…t a node jwtAuth (strict) validates the caller's Keycloak access token and removes it from the request. The backend's oauthTokenExchange sends the validated token (jwt.rawToken.unredacted()) to the node's /oauth/token as an RFC 8693 subject token, without client authentication, and puts the exchanged credential in X-Mesh-Authentication for the node's /mesh/ proxy. A second route applies the same policies in front of a header echo.
Keycloak, the control plane, one router, the calc provider, the shared node, agentgateway v1.6.0 and a server that answers with the headers it received. agentgateway starts once Keycloak is healthy, since it fetches the realm's keys at startup. The agentmesh images are pinned to the commit the example was tested with and can be overridden; tokens are read from files under .run/, which is ignored.
run.sh brings the compose services up in order, then runs each case and prints whether it got the expected status, exiting non-zero on any mismatch: a call to mcp://calc through agentgateway as each user; no token, not a token, a token for another client and one from another realm, refused at agentgateway; the headers a backend receives; and one token through its expiry, including the minute after exp in which agentgateway still accepts it (the jsonwebtoken default leeway) and the control plane refuses the exchange. The admin, API and bootstrap tokens are generated at random into .run/ and handed to the components as files and to curl as header files, never as arguments.
The guide walks through the agentgateway example: access tokens that carry the mesh's audience, validating the caller with jwtAuth, exchanging the token at the node's /oauth/token, the calls and what a backend receives, and what happens around the token's expiry. It lists where each refusal is logged and what to change for a real deployment, and is linked from the guides index. The example's README points to it.
There was a problem hiding this comment.
Code Review
This pull request introduces a comprehensive example and guide for deploying agentgateway in front of an Agent Mesh. It includes a Docker Compose configuration, Keycloak realm imports, a test script (run.sh), and detailed documentation. The feedback recommends updating the Python image in compose.yaml from the pre-release python:3.14-slim to a stable version like python:3.13-slim to ensure reliability.
| # Answers with the request headers it received (echo.py), behind the same | ||
| # agentgateway policies as the mesh route. | ||
| echo: | ||
| image: python:3.14-slim |
There was a problem hiding this comment.
The Python image is pinned to python:3.14-slim. Python 3.14 is currently in pre-release/development and is not yet stable. It is highly recommended to use a stable, released version of Python such as python:3.13-slim or python:3.12-slim to ensure reliability and avoid potential image pull or runtime issues.
image: python:3.13-slimThere was a problem hiding this comment.
Python 3.14 is a stable release (3.14.0 on 7 October 2025; this image reports 3.14.8, release level final), and the repository's other Python images use it too (calc-mcp, chat-a2a, greeter-mcp, the e2e images). I pinned the echo image to the same digest those Dockerfiles use, python:3.14-slim@sha256:a2b82f3c…, in 7d0834d.
The header echo ran python:3.14-slim by tag. It now names the digest the repository's Python Dockerfiles already pin (calc-mcp, chat-a2a, greeter-mcp and the e2e images), so the example runs the image it was tested with.
Closes #801. Part of #734, the agentgateway checkbox.
What
development/examples/agentgateway/puts agentgateway v1.6.0 (standalone) in front of a sharedagentmesh-node, in Docker Compose, with Keycloak 26.4.0 as the identity provider. agentgateway validates each caller's access token (jwtAuth, strict), exchanges it at the node'sPOST /oauth/tokenwith its built-in RFC 8693oauthTokenExchange, and calls the node's/mesh/proxy with the exchanged credential inX-Mesh-Authentication.run.shbrings it up in order, runs each case of #801 and exits non-zero if any result differs:agentgateway holds no mesh credential and no client secret: the node exchanges a JWT subject token without client authentication, and the control plane verifies the token's issuer and audience.
jwtAuthremoves the caller's token from the request, and a header echo behind the same policies shows that a backend receives only the exchanged credential.A limitation, stated in the guide
agentgateway accepts a token for 60 seconds after its
exp(the default leeway of thejsonwebtokenlibrary; v1.6.0 has no setting for it), and the control plane allows none. In that minute the exchange is refused and agentgateway answers400 invalid request, which a client that refreshes on401does not treat as a reason to refresh. The script checks both sides of it.Guide
site/content/docs/guides/agentgateway.md, linked from the guides index, walks through the configuration step by step, lists where each refusal is logged, and what to change for a real deployment. The example's README points to it, as for the Keycloak and OpenShell examples.Tested
./run.shpasses all 16 checks from a clean state with the published images pinned incompose.yaml(ghcr.io/google/agentmesh-*:8de8bd76…,ghcr.io/agentgateway/agentgateway:v1.6.0).agentgateway --validate-onlyacceptsagentgateway.yaml;shellcheckv0.10.0 reports nothing onrun.sh.deploy-github-pages.yml; the guide renders, is listed in the guides index, and its links resolve.