For the complete documentation index, see llms.txt. Markdown versions of all docs pages are available by appending .md to any docs URL.
Set up Descope
Configure Descope as an OAuth identity provider for MCP authentication with agentgateway.
Secure your Model Context Protocol (MCP) servers with OAuth 2.0 authentication by using agentgateway and Descope as the identity provider.
About this guide
In this guide, you configure the agentgateway proxy to protect a static MCP server with MCP auth by using a Descope MCP Server as the authorization server. Agentgateway includes a native Descope provider that adapts to Descope’s agentic identity endpoints. When you set provider: Descope, agentgateway does the following:
- Serves authorization server metadata from Descope’s OpenID Connect discovery document.
- Rewrites your agentic issuer to the project-level JWKS URL, because Descope publishes signing keys per project rather than per MCP server.
- Proxies Dynamic Client Registration through the gateway, so that browser-based MCP clients are not blocked by cross-origin restrictions.
Unlike Auth0 and Okta, Descope supports RFC 8707 resource indicators, so agentgateway does not need to work around the missing resource parameter.
For more information about MCP auth, see the About MCP auth page.
Before you begin
- Set up an agentgateway proxy.
- Follow the steps to set up an MCP server with a fetch tool.
- Install the experimental channel Gateway API.
kubectl apply --server-side -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.6.0/experimental-install.yaml
Set up Descope
Create an MCP Server and a client in Descope, and collect the values that agentgateway needs.
Create a project in the Descope Console. Note your Project ID from Project Settings.
Create an MCP Server to represent your MCP gateway. Set the MCP Server URL to the public URL that agentgateway exposes, typically ending with
/mcp, and define the scopes that your server enforces.From the MCP Server’s Connection Information section, copy the Issuer URL. Descope agentic issuers take the form
https://api.descope.com/v1/apps/agentic/<project-id>/<server-id>. Note the server ID from the end of that URL.Create a Client for the MCP clients that connect through the gateway, and note its Client ID.
Assign a role such as
Tenant Adminto the users or clients that need access to your MCP server. You use this role in the authorization rule that you configure later.Save the values as environment variables.
export DESCOPE_PROJECT_ID=<your-project-id> export DESCOPE_SERVER_ID=<your-mcp-server-id> export DESCOPE_CLIENT_ID=<your-client-id> export DESCOPE_MCP_SERVER_URL=https://mcp.example.com/mcpVariable Description DESCOPE_PROJECT_IDYour Descope Project ID, found under Project Settings. Descope publishes signing keys at the project level, so this value determines the JWKS path. DESCOPE_SERVER_IDThe MCP server ID from the end of your issuer URL. DESCOPE_CLIENT_IDThe Client ID of the Descope Client that you created. DESCOPE_MCP_SERVER_URLYour MCP server’s public URL, which must match the MCP Server URL in Descope. Descope sets the audclaim of its tokens to this value.
Create the JWKS backend
Create an AgentgatewayBackend that points to the Descope API, and a BackendTLSPolicy that originates a TLS connection to it. The JWT authentication policy uses this backend to fetch Descope’s public keys for token signature validation.
Create an AgentgatewayBackend for the Descope API endpoint.
kubectl apply -f- <<EOF apiVersion: agentgateway.dev/v1alpha1 kind: AgentgatewayBackend metadata: name: descope-jwks spec: static: host: api.descope.com port: 443 EOFCreate a BackendTLSPolicy that originates a TLS connection to the
descope-jwksbackend by using well-known trusted CA certificates.kubectl apply -f- <<EOF apiVersion: gateway.networking.k8s.io/v1 kind: BackendTLSPolicy metadata: name: descope-jwks spec: targetRefs: - name: descope-jwks kind: AgentgatewayBackend group: agentgateway.dev validation: hostname: api.descope.com wellKnownCACertificates: System EOF
Configure MCP auth
With your MCP backend configured, create an AgentgatewayPolicy that enforces Descope authentication and authorization for the MCP backend.
Create an AgentgatewayPolicy with the
Descopeprovider. The policy validates tokens that Descope issues and uses a Common Expression Language (CEL) rule to require theTenant Adminrole.kubectl apply -f - <<EOF apiVersion: agentgateway.dev/v1alpha1 kind: AgentgatewayPolicy metadata: name: mcp-descope-authn spec: # Target the HTTPRoute to apply authentication at the route level targetRefs: - group: gateway.networking.k8s.io kind: HTTPRoute name: mcp traffic: jwtAuthentication: mode: Strict providers: # The Descope agentic issuer for your MCP server - issuer: "https://api.descope.com/v1/apps/agentic/${DESCOPE_PROJECT_ID}/${DESCOPE_SERVER_ID}" # Descope sets the aud claim to your MCP server URL audiences: - "${DESCOPE_MCP_SERVER_URL}" jwks: remote: backendRef: name: descope-jwks kind: AgentgatewayBackend group: agentgateway.dev port: 443 # Descope publishes keys per project, not per MCP server jwksPath: "/${DESCOPE_PROJECT_ID}/.well-known/jwks.json" mcp: # Use the native Descope provider provider: Descope # Short-circuit Dynamic Client Registration with a pre-registered client clientId: "${DESCOPE_CLIENT_ID}" resourceMetadata: resource: http://localhost:8080/mcp scopesSupported: - openid - profile bearerMethodsSupported: - header # Allow only tokens that carry the Tenant Admin role authorization: action: Allow policy: matchExpressions: - '"Tenant Admin" in jwt.roles' EOFReview the following table to understand this configuration. For more information about the
traffic.jwtAuthenticationfield, see the API docs.Setting Description providers[].issuerThe Descope agentic issuer URL for your MCP server. This value must match the issclaim in the token.providers[].audiencesYour MCP server URL, which must match the audclaim that Descope mints.providers[].jwks.remote.backendRefThe descope-jwksbackend that points toapi.descope.com.providers[].jwks.remote.jwksPathThe project-level JWKS path. Descope publishes keys at /<project-id>/.well-known/jwks.jsonrather than under the agentic issuer.mcp.providerThe identity provider. Set to Descopeto enable the native Descope behavior.mcp.clientIdThe Client ID of your Descope Client. Agentgateway answers Dynamic Client Registration requests with this value instead of proxying them to Descope. mcp.resourceMetadataMCP OAuth resource metadata for discovery. Includes the resource identifier, supported scopes, and bearer token methods. authorization.policy.matchExpressionsCEL rules that authorize the claims in the verified JWT. This example requires the Tenant Adminrole. Requests that present a valid token without that role are denied with a 403 HTTP response code.Note
Where the roles claim appears depends on your Authorization Claims Configuration. With the default Descope JWT, roles are in
jwt.tenants["<your-tenant-id>"].roles. With the No Tenant Reference claim format, roles are injwt.roles, which is what this rule uses.Note
Setting
clientIdis recommended for Descope. Descope’s Dynamic Client Registration endpoint requires a management key that MCP clients do not have, so registration requests that the gateway proxies to Descope fail. If you prefer to let clients register dynamically, use CIMD instead.Verify that the policy was accepted.
kubectl get AgentgatewayPolicy mcp-descope-authn -o yamlIn the
statussection, confirm that theAcceptedandAttachedconditions areTrue.Note
The control plane fetches the JWKS when it translates the policy. If your project ID is wrong, the policy is accepted but the control plane logs
jwks keyset ... isn't availableand the policy does not program on the data plane. Check the control plane logs if authentication does not take effect.Update the HTTPRoute that routes incoming traffic to the MCP server to include the OAuth discovery paths. This way, the agentgateway proxy can serve the resource and authorization server metadata during the MCP auth flow.
kubectl apply -f - <<EOF apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: mcp spec: parentRefs: - group: gateway.networking.k8s.io kind: Gateway name: agentgateway-proxy namespace: agentgateway-system rules: - filters: # Enable CORS for browser-based MCP clients - type: CORS cors: allowCredentials: true allowHeaders: - Origin - Authorization - Content-Type allowMethods: - "*" allowOrigins: - "*" exposeHeaders: - Origin - Mcp-Session-Id maxAge: 86400 backendRefs: - group: agentgateway.dev kind: AgentgatewayBackend name: mcp-backend matches: # Main MCP endpoint to connect to the MCP server - path: type: PathPrefix value: /mcp # Path to access resource server metadata - path: type: PathPrefix value: /.well-known/oauth-protected-resource/mcp # Path to access authorization server metadata, including the # gateway-proxied client registration endpoint - path: type: PathPrefix value: /.well-known/oauth-authorization-server/mcp EOF
Verify MCP auth
Get the address of the agentgateway proxy.
export INGRESS_GW_ADDRESS=$(kubectl get svc -n agentgateway-system agentgateway-proxy \ -o jsonpath="{.status.loadBalancer.ingress[0]['hostname','ip']}") echo "Gateway address: $INGRESS_GW_ADDRESS"Send an unauthenticated request to the MCP endpoint. Verify that the request is rejected with a 401 HTTP response code and a
WWW-Authenticateheader that points MCP clients to the protected resource metadata.curl -i http://$INGRESS_GW_ADDRESS:80/mcp -X POST \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{}},"id":1}'Example output:
HTTP/1.1 401 Unauthorized www-authenticate: Bearer resource_metadata="http://localhost:8080/.well-known/oauth-protected-resource/mcp"Verify that the gateway serves the protected resource metadata.
curl -s http://$INGRESS_GW_ADDRESS:80/.well-known/oauth-protected-resource/mcp | jqVerify that the gateway serves Descope’s authorization server metadata, and that the registration endpoint points back at the gateway.
curl -s http://$INGRESS_GW_ADDRESS:80/.well-known/oauth-authorization-server/mcp \ | jq '{issuer, jwks_uri, authorization_endpoint, registration_endpoint}'
Connect an MCP client
Point your MCP client at the gateway’s MCP endpoint, such as http://localhost:8080/mcp. The client discovers the authorization server through the gateway, registers against the pre-registered client, and redirects the user through Descope’s consent flow.
Role-based authorization
The policy that you created gates the MCP endpoint on the Tenant Admin role, which Descope includes in its tokens according to your Authorization Claims Configuration. Authentication alone is not enough: any caller that Descope issues a token to for your MCP server passes JWT validation, including clients that authorize themselves rather than a user. The authorization rule denies those tokens with a 403 HTTP response code.
Because MCP authentication runs at the route level, every claim in the verified token is also available to other route-level policies, such as rate limiting and transformations. For more information about the rules that you can write, see Authorization.
To authorize individual tools instead of the whole MCP endpoint, use an MCP authorization policy. For more information, see Tool access.
Clean up
You can remove the resources that you created in this guide.kubectl delete AgentgatewayPolicy mcp-descope-authn
kubectl delete backendtlspolicy descope-jwks
kubectl delete AgentgatewayBackend descope-jwks