ZuploZuplo
LoginStart for Free
  • Documentation
  • API Reference
Getting Started
Concepts
API Management
AI Gateway
MCP Gateway
    Overview
    Getting Started
      How it works
      Connect MCP clients
      Authentication
      Cross App Access
        OverviewQuickstartConfiguration reference
      Configuration
      Policies
      Observability
      ReferenceServer RegistryTroubleshooting
    MCP Server
    Developer Portal
    Development
    Deploying & Source Control
    Analytics
    Observability
    Networking & Infrastructure
    Account Management
    Programming API
    Build with AI
    Zuplo CLI
    Migration Guides
    Platform LimitsVersion Support PolicySecuritySupportTrust & ComplianceChangelog
    powered by Zuplo
    Cross App Access

    Cross App Access configuration reference

    Cross App Access is configured on the mcp-token-exchange-inbound policy. Setting authMode: "id-jag" and providing an idJag block makes the gateway act as the XAA requesting app: it mints an ID-JAG from your IdP and redeems it at an upstream resource authorization server. This is the configuration the quickstart uses.

    The authoritative source for these options is the policy's runtime schema. The generated mcp-token-exchange-inbound reference page predates id-jag mode; the options below reflect the runtime behavior.

    Gateway as requesting app

    Set authMode: "id-jag" on a mcp-token-exchange-inbound policy and provide an idJag block. Attach the policy to the upstream route after the inbound MCP OAuth policy.

    Code
    { "name": "id-jag-upstream", "policyType": "mcp-token-exchange-inbound", "handler": { "module": "$import(@zuplo/runtime/mcp-gateway)", "export": "McpTokenExchangeInboundPolicy", "options": { "displayName": "Upstream", "authMode": "id-jag", "idJag": { "scopes": ["mcp:tools"], "scopeDelimiter": " ", "idp": { "tokenUrl": "https://idp.example.com/token", "clientAuth": { "method": "client_secret_post", "clientId": "$env(IDP_CLIENT_ID)", "clientSecret": "$env(IDP_CLIENT_SECRET)", }, }, "resourceAs": { "tokenUrl": "https://upstream.example.com/token", "audience": "https://upstream.example.com", "resource": "https://upstream.example.com/mcp", "clientAuth": { "method": "client_secret_post", "clientId": "$env(RESOURCE_AS_CLIENT_ID)", "clientSecret": "$env(RESOURCE_AS_CLIENT_SECRET)", }, }, }, }, }, }

    idJag options

    OptionTypeDefaultDescription
    scopesstring[][]Scopes requested in both exchanges.
    scopeDelimiterstring" "Delimiter used to join scopes.
    idpobject—Where the gateway mints the ID-JAG (RFC 8693 token exchange).
    resourceAsobject—Where the gateway redeems the ID-JAG for an access token (RFC 7523).

    idp

    The identity provider that issues the ID-JAG.

    OptionTypeDescription
    tokenUrlstringThe IdP token endpoint.
    clientAuthobjectHow the gateway authenticates to the IdP (see below).

    resourceAs

    The upstream's resource authorization server that issues the access token.

    OptionTypeDescription
    tokenUrlstringThe resource authorization server's token endpoint.
    audiencestringRequired. The resource AS identifier; sent as the token-exchange audience and becomes the ID-JAG aud.
    resourcestringOptional RFC 8707 resource indicator. Defaults to the route's upstream URL.
    clientAuthobjectHow the gateway authenticates to the resource AS (see below).

    clientAuth

    Both idp.clientAuth and resourceAs.clientAuth take the same shape. The method selects how the gateway authenticates:

    Code
    // client_secret_post (or client_secret_basic) "clientAuth": { "method": "client_secret_post", "clientId": "...", "clientSecret": "$env(...)" }
    Code
    // private_key_jwt "clientAuth": { "method": "private_key_jwt", "clientId": "...", "privateKeyPem": "$env(...)", "algorithm": "RS256", "keyId": "...", "expiresInSeconds": 300 }
    OptionTypeDefaultApplies toDescription
    methodenum—allclient_secret_post, client_secret_basic, or private_key_jwt.
    clientIdstring—allThe OAuth client ID.
    clientSecretstring—secret methodsThe OAuth client secret.
    privateKeyPemstring—private_key_jwtPEM private key used to sign the client-assertion JWT.
    algorithmenumRS256private_key_jwtRS256/RS384/RS512/ES256/ES384/ES512.
    keyIdstring—private_key_jwtOptional kid header on the client assertion.
    audiencestring—private_key_jwtOptional audience override for the client assertion.
    expiresInSecondsnumber300private_key_jwtClient-assertion lifetime, max 3600.

    What the gateway does at request time

    On a tool call to the route, the gateway:

    1. Resolves the user's stored IdP identity assertion (bound during the inbound browser login). If absent or expired, it returns a connect-required error and refreshes the subject token when it can.
    2. Runs an RFC 8693 token exchange at idp.tokenUrl, requesting an ID-JAG audience-restricted to resourceAs.audience.
    3. Redeems the ID-JAG at resourceAs.tokenUrl as an RFC 7523 JWT-bearer grant to get the upstream access token.
    4. Caches the upstream token per user and forwards the tool call with Authorization: Bearer <upstream token>.

    Notes and limitations

    • The gateway issues opaque access tokens, not JWTs.
    • DPoP is not supported. Requests with a DPoP header are rejected.
    • XAA requires a prior inbound browser login so the gateway has an IdP identity assertion to exchange.

    Related

    • Overview — the protocol and the gateway's role.
    • Quickstart — a working id-jag configuration on the playground.
    • mcp-token-exchange-inbound policy — the base token-exchange policy reference.
    Edit this page
    Last modified on June 22, 2026
    QuickstartSet up the gateway
    On this page
    • Gateway as requesting app
      • idJag options
      • idp
      • resourceAs
      • clientAuth
      • What the gateway does at request time
    • Notes and limitations
    • Related
    JSON
    JSON
    JSON