Skip to content
agentgateway has joined the Agentic AI FoundationLearn more

For the complete documentation index, see llms.txt. Markdown versions of all docs pages are available by appending .md to any docs URL.

Page as Markdown

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

  1. Set up an agentgateway proxy.
  2. Follow the steps to set up an MCP server with a fetch tool.
  3. 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.

  1. Create a project in the Descope Console. Note your Project ID from Project Settings.

  2. 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.

  3. 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.

  4. Create a Client for the MCP clients that connect through the gateway, and note its Client ID.

  5. Assign a role such as Tenant Admin to the users or clients that need access to your MCP server. You use this role in the authorization rule that you configure later.

  6. 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/mcp
    VariableDescription
    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 aud claim 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.

  1. 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
    EOF
  2. Create a BackendTLSPolicy that originates a TLS connection to the descope-jwks backend 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.

  1. Create an AgentgatewayPolicy with the Descope provider. The policy validates tokens that Descope issues and uses a Common Expression Language (CEL) rule to require the Tenant Admin role.

    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'
    EOF

    Review the following table to understand this configuration. For more information about the traffic.jwtAuthentication field, see the API docs.

    SettingDescription
    providers[].issuerThe Descope agentic issuer URL for your MCP server. This value must match the iss claim in the token.
    providers[].audiencesYour MCP server URL, which must match the aud claim that Descope mints.
    providers[].jwks.remote.backendRefThe descope-jwks backend that points to api.descope.com.
    providers[].jwks.remote.jwksPathThe project-level JWKS path. Descope publishes keys at /<project-id>/.well-known/jwks.json rather than under the agentic issuer.
    mcp.providerThe identity provider. Set to Descope to 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 Admin role. 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 in jwt.roles, which is what this rule uses.

    Note

    Setting clientId is 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.

  2. Verify that the policy was accepted.

    kubectl get AgentgatewayPolicy mcp-descope-authn -o yaml

    In the status section, confirm that the Accepted and Attached conditions are True.

    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 available and the policy does not program on the data plane. Check the control plane logs if authentication does not take effect.

  3. 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

  1. 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"
  2. Send an unauthenticated request to the MCP endpoint. Verify that the request is rejected with a 401 HTTP response code and a WWW-Authenticate header 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"
  3. Verify that the gateway serves the protected resource metadata.

    curl -s http://$INGRESS_GW_ADDRESS:80/.well-known/oauth-protected-resource/mcp | jq
  4. Verify 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
Was this page helpful?
Agentgateway assistant

Ask me anything about agentgateway configuration, features, or usage.

Note: AI-generated content might contain errors; please verify and test all returned information.

Tip: one topic per conversation gives the best results. Use the + button in the chat header to start a new conversation.

Switching topics? Starting a new conversation improves accuracy.
↑↓ navigate select esc dismiss

What could be improved?

Your feedback helps us improve assistant answers and identify docs gaps we should fix.

Need more help? Join us on Discord: https://discord.gg/y9efgEmppm

Want to use your own agent? Add the Solo MCP server to query our docs directly. Get started here: https://search.solo.io/.