Secure access to MCP servers BETA
Control access or route traffic based on verified claims in a JSON web token (JWT). To learn more about JWTs, see the JWT Overview.
About MCP auth
In this guide, you learn how to configure your agentgateway proxy to validate JWT tokens that are provided in an Authorization header. You then authorize access to specific MCP tools based on specific claims in the JWT.
The following diagram shows the components that are involved when performing JWT validation and authorization with MCP servers:
sequenceDiagram
autonumber
Client->>+Agentgateway: Send request with JWT
Agentgateway->>Agentgateway: Validate JWT token
Agentgateway->>Agentgateway: Authorize access to MCP server
Agentgateway->>+MCP: Forward request
- The MCP client, such as the MCP inspector tool, sends a request to the agentgateway proxy with the JWT token in the
Authorizationheader. - The agentgateway validates the JWT with the JWKS server that you define in a
glooJWTpolicy in the GlooTrafficPolicy resource. This policy is applied to the agentgateway proxy. - If the GlooTrafficPolicy further defines RBAC rules, such as to only grant access for JWT tokens with certain claims, agentgateway validates these claims and either grants or denies access.
- If successfully validated and authorized, the agentgateway proxy forwards the request to the MCP backend.
Before you begin
- Set up an agentgateway proxy.
- Follow the steps to connect to the remote GitHub MCP server via HTTPS.
Validate JWT tokens
You can configure your agentgateway proxy to validate JWT tokens that are sent by an MCP client in an Authorization header.
Create a GlooTrafficPolicy with your JWT validation rules and apply it to the agentgateway proxy that you created before you began. In this example, you use an inline, local JSON Web Key Set (JWKS) to verify the JWT.
kubectl apply -f- <<EOF apiVersion: gloo.solo.io/v1alpha1 kind: GlooTrafficPolicy metadata: name: jwt namespace: gloo-system spec: targetRefs: - group: gateway.networking.k8s.io kind: Gateway name: agentgateway glooJWT: beforeExtAuth: providers: selfminted: issuer: solo.io jwks: local: key: '{"keys":[{"kty":"RSA","kid":"solo-public-key-001","use":"sig","alg":"RS256","n":"AOfIaJMUm7564sWWNHaXt_hS8H0O1Ew59-nRqruMQosfQqa7tWne5lL3m9sMAkfa3Twx0LMN_7QqRDoztvV3Wa_JwbMzb9afWE-IfKIuDqkvog6s-xGIFNhtDGBTuL8YAQYtwCF7l49SMv-GqyLe-nO9yJW-6wIGoOqImZrCxjxXFzF6mTMOBpIODFj0LUZ54QQuDcD1Nue2LMLsUvGa7V1ZHsYuGvUqzvXFBXMmMS2OzGir9ckpUhrUeHDCGFpEM4IQnu-9U8TbAJxKE5Zp8Nikefr2ISIG2Hk1K2rBAc_HwoPeWAcAWUAR5tWHAxx-UXClSZQ9TMFK850gQGenUp8","e":"AQAB"}]}' EOFReview the following table to understand this configuration. For more information, see the API docs or the JWT guide for more examples.
Field Description issuerThe principal that issued the JWT, usually a URL or an email address. If specified, the issfield in the JWT of the incoming request must match this field, or else the request is denied. If omitted, theissfield in the JWT is not checked.jwksThe JSON Web Key Set (JWKS) to use to verify the JWT. In this example, a local JWKS is provided inline. To use JWTs with Solo Enterprise for agentgateway, make sure that the JWTs return Key ID ( kid) and expiration date (exp) values in the JWT header.Save the JWT tokens for the users Alice and Bob. You can optionally create other JWT tokens by using the JWT generator tool. Note that to use JWTs with agentgateway proxies, make sure that the JWTs return Key ID (
kid) and expiration date (exp) values in the JWT header.Save the JWT token for Alice. Alice works in the
devteam.eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InNvbG8tcHVibGljLWtleS0wMDEifQ.eyJpc3MiOiJzb2xvLmlvIiwib3JnIjoic29sby5pbyIsInN1YiI6ImFsaWNlIiwidGVhbSI6ImRldiIsImV4cCI6MjA3NDI3NDg4NCwibGxtcyI6eyJvcGVuYWkiOlsiZ3B0LTMuNS10dXJibyJdfX0.il5Rjsad65jpQR_pyRzBdEKFSj-ERmBf4K2VksvGvswWVv4n79lYERslr4KCECuiz9y_T-xUiQ9IkhW3YHzl5zo1kajhhIg7Nhnl1AvAqODbnF6wYpLRk0Npna_2T6lK3Yj54qQGi6vXG3IMRpo1_o2DrbdlKx2k_WFegCoQyyYazb4z3ZXfWvTiWqQDJA5wWcM3-jKzAWfNM8zgZWa-1BeAHDvpLcfWtuXEGSjkdCW0FQJOTjgIEqACnnXb2Jio0tWgelh9hDPILI-tvanj3iKCjpf3uF6g8QWSBNoVFfu7F1jJgj5Aj1sX8AV-CQVu2aQx3EHRZ1mL_3w3qSRWPwSave the JWT token for Bob. Bob works in the
opsteam.eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InNvbG8tcHVibGljLWtleS0wMDEifQ.eyJpc3MiOiJzb2xvLmlvIiwib3JnIjoic29sby5pbyIsInN1YiI6ImJvYiIsInRlYW0iOiJvcHMiLCJleHAiOjIwNzQyNzQ5NTQsImxsbXMiOnsibWlzdHJhbGFpIjpbIm1pc3RyYWwtbGFyZ2UtbGF0ZXN0Il19fQ.GF_uyLpZSTT1DIvJeO_eish1WDjMaS4BQSifGQhqPRLjzu3nXtPkaBRjceAmJi9gKZYAzkT25MIrT42ZIe3bHilrd1yqittTPWrrM4sWDDeldnGsfU07DWJHyboNapYR-KZGImSmOYshJlzm1tT_Bjt3-RK3OBzYi90_wl0dyAl9D7wwDCzOD4MRGFpoMrws_OgVrcZQKcadvIsH8figPwN4mK1U_1mxuL08RWTu92xBcezEO4CdBaFTUbkYN66Y2vKSTyPCxg3fLtg1mvlzU1-Wgm2xZIiPiarQHt6Uq7v9ftgzwdUBQM1AYLvUVhCN6XkkR9OU3p0OXiqEDjAxcg
Get the agentgateway address.
From the terminal, run the MCP Inspector command. Then, the MCP Inspector opens in your browser. If the MCP inspector tool does not open automatically, run
mcp-inspector.npx modelcontextprotocol/inspector#0.17.5From the MCP Inspector menu, try to connect to your agentgateway address as follows:
- Transport Type: Select
Streamable HTTP. - URL: Enter the agentgateway address, port, and the
/mcp-githubpath. If your agentgateway proxy is exposed with a LoadBalancer server, usehttp://<lb-address>:8080/mcp. In local test setups where you port-forwarded the agentgateway proxy on your local machine, usehttp://localhost:8080/mcp-github. - Click Connect.
Connection Error - Check if your MCP server is running and proxy token is correct

- Transport Type: Select
Check the agentgateway proxy logs and verify that you see 403 authentication failure log entries.
kubectl logs deploy/agentgateway -n gloo-systemExample output:
request gateway=default/agentgateway listener=http route=default/mcp src.addr=127.0.0.1:59068 http.method=POST http.host=localhost http.path=/mcp http.version=HTTP/1.1 http.status=403 error=authentication failure: no bearer token found duration=0msGo back to the MCP Inspector tool and expand the Authentication section. Enter the following details in the API Token Authentication card:
Header Name: Enter
Authorization.Bearer Token: Enter the JWT token for Alice.
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InNvbG8tcHVibGljLWtleS0wMDEifQ.eyJpc3MiOiJzb2xvLmlvIiwib3JnIjoic29sby5pbyIsInN1YiI6ImFsaWNlIiwidGVhbSI6ImRldiIsImV4cCI6MjA3NDI3NDg4NCwibGxtcyI6eyJvcGVuYWkiOlsiZ3B0LTMuNS10dXJibyJdfX0.il5Rjsad65jpQR_pyRzBdEKFSj-ERmBf4K2VksvGvswWVv4n79lYERslr4KCECuiz9y_T-xUiQ9IkhW3YHzl5zo1kajhhIg7Nhnl1AvAqODbnF6wYpLRk0Npna_2T6lK3Yj54qQGi6vXG3IMRpo1_o2DrbdlKx2k_WFegCoQyyYazb4z3ZXfWvTiWqQDJA5wWcM3-jKzAWfNM8zgZWa-1BeAHDvpLcfWtuXEGSjkdCW0FQJOTjgIEqACnnXb2Jio0tWgelh9hDPILI-tvanj3iKCjpf3uF6g8QWSBNoVFfu7F1jJgj5Aj1sX8AV-CQVu2aQx3EHRZ1mL_3w3qSRWPwClick Connect.
Verify that the connection now succeeds because a valid token was provided in an
Authorizationheader to your agentgateway proxy:

Repeat the previous step with the JWT token for Bob. Verify that you can also connect to your MCP server successfully.
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InNvbG8tcHVibGljLWtleS0wMDEifQ.eyJpc3MiOiJzb2xvLmlvIiwib3JnIjoic29sby5pbyIsInN1YiI6ImJvYiIsInRlYW0iOiJvcHMiLCJleHAiOjIwNzQyNzQ5NTQsImxsbXMiOnsibWlzdHJhbGFpIjpbIm1pc3RyYWwtbGFyZ2UtbGF0ZXN0Il19fQ.GF_uyLpZSTT1DIvJeO_eish1WDjMaS4BQSifGQhqPRLjzu3nXtPkaBRjceAmJi9gKZYAzkT25MIrT42ZIe3bHilrd1yqittTPWrrM4sWDDeldnGsfU07DWJHyboNapYR-KZGImSmOYshJlzm1tT_Bjt3-RK3OBzYi90_wl0dyAl9D7wwDCzOD4MRGFpoMrws_OgVrcZQKcadvIsH8figPwN4mK1U_1mxuL08RWTu92xBcezEO4CdBaFTUbkYN66Y2vKSTyPCxg3fLtg1mvlzU1-Wgm2xZIiPiarQHt6Uq7v9ftgzwdUBQM1AYLvUVhCN6XkkR9OU3p0OXiqEDjAxcg

Try to enter an invalid JWT token, such as
abcdefg. Verify that access to the MCP server is denied, because the JWT token could not be validated by your agentgateway proxy.

Authorize access based on JWT claims
You can limit access to the MCP server based on specific JWT claims with CEL-based RBAC rules.
Update the GlooTrafficPolicy to add your RBAC rules. In the following example, you use a CEL expression to only allow access to the MCP server if the JWT has the
sub=aliceclaim.kubectl apply -f- <<EOF apiVersion: gloo.solo.io/v1alpha1 kind: GlooTrafficPolicy metadata: name: jwt namespace: gloo-system spec: targetRefs: - group: gateway.networking.k8s.io kind: Gateway name: agentgateway glooJWT: beforeExtAuth: providers: selfminted: issuer: solo.io jwks: local: key: '{"keys":[{"kty":"RSA","kid":"solo-public-key-001","use":"sig","alg":"RS256","n":"AOfIaJMUm7564sWWNHaXt_hS8H0O1Ew59-nRqruMQosfQqa7tWne5lL3m9sMAkfa3Twx0LMN_7QqRDoztvV3Wa_JwbMzb9afWE-IfKIuDqkvog6s-xGIFNhtDGBTuL8YAQYtwCF7l49SMv-GqyLe-nO9yJW-6wIGoOqImZrCxjxXFzF6mTMOBpIODFj0LUZ54QQuDcD1Nue2LMLsUvGa7V1ZHsYuGvUqzvXFBXMmMS2OzGir9ckpUhrUeHDCGFpEM4IQnu-9U8TbAJxKE5Zp8Nikefr2ISIG2Hk1K2rBAc_HwoPeWAcAWUAR5tWHAxx-UXClSZQ9TMFK850gQGenUp8","e":"AQAB"}]}' rbac: policy: matchExpressions: - 'jwt.sub == "alice"' EOFGo back to the MCP inspector tool and expand the Authentication section. Enter the following details in the API Token Authentication card:
- Header Name: Enter
Authorization. - Bearer Token: Enter the JWT token for Bob.
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InNvbG8tcHVibGljLWtleS0wMDEifQ.eyJpc3MiOiJzb2xvLmlvIiwib3JnIjoic29sby5pbyIsInN1YiI6ImJvYiIsInRlYW0iOiJvcHMiLCJleHAiOjIwNzQyNzQ5NTQsImxsbXMiOnsibWlzdHJhbGFpIjpbIm1pc3RyYWwtbGFyZ2UtbGF0ZXN0Il19fQ.GF_uyLpZSTT1DIvJeO_eish1WDjMaS4BQSifGQhqPRLjzu3nXtPkaBRjceAmJi9gKZYAzkT25MIrT42ZIe3bHilrd1yqittTPWrrM4sWDDeldnGsfU07DWJHyboNapYR-KZGImSmOYshJlzm1tT_Bjt3-RK3OBzYi90_wl0dyAl9D7wwDCzOD4MRGFpoMrws_OgVrcZQKcadvIsH8figPwN4mK1U_1mxuL08RWTu92xBcezEO4CdBaFTUbkYN66Y2vKSTyPCxg3fLtg1mvlzU1-Wgm2xZIiPiarQHt6Uq7v9ftgzwdUBQM1AYLvUVhCN6XkkR9OU3p0OXiqEDjAxcg - Click Connect.
Verify that the connection fails with an error message similar to the following, because Bob’s JWT token does not have the
sub=aliceclaim.Connection Error - Check if your MCP server is running and proxy token is correct

- Header Name: Enter
Repeat the previous step with the JWT token for Alice. Verify that you can successfully connect to the MCP server, because the token has the
sub=aliceclaim.eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InNvbG8tcHVibGljLWtleS0wMDEifQ.eyJpc3MiOiJzb2xvLmlvIiwib3JnIjoic29sby5pbyIsInN1YiI6ImFsaWNlIiwidGVhbSI6ImRldiIsImV4cCI6MjA3NDI3NDg4NCwibGxtcyI6eyJvcGVuYWkiOlsiZ3B0LTMuNS10dXJibyJdfX0.il5Rjsad65jpQR_pyRzBdEKFSj-ERmBf4K2VksvGvswWVv4n79lYERslr4KCECuiz9y_T-xUiQ9IkhW3YHzl5zo1kajhhIg7Nhnl1AvAqODbnF6wYpLRk0Npna_2T6lK3Yj54qQGi6vXG3IMRpo1_o2DrbdlKx2k_WFegCoQyyYazb4z3ZXfWvTiWqQDJA5wWcM3-jKzAWfNM8zgZWa-1BeAHDvpLcfWtuXEGSjkdCW0FQJOTjgIEqACnnXb2Jio0tWgelh9hDPILI-tvanj3iKCjpf3uF6g8QWSBNoVFfu7F1jJgj5Aj1sX8AV-CQVu2aQx3EHRZ1mL_3w3qSRWPw

Next
Explore how to control access to tools with RBAC policies.
Cleanup
You can remove the resources that you created in this guide.
kubectl delete Backend github-mcp-backend -n gloo-system
kubectl delete BackendTLSPolicy github-mcp-backend-tls -n gloo-system
kubectl delete HTTPRoute mcp-github -n gloo-system
kubectl delete GlooTrafficPolicy jwt -n gloo-system