Skip to main content

MCP authorization for enterprise API gateways: scoped identities, OAuth discovery, and enforcement

The MCP authorization specification requires OAuth 2.1 and protected-resource metadata. Here's what enterprise API gateway operators need to configure.

  • mcp
  • ai-agents
  • security
  • oauth
  • enterprise
Zerq team

Most enterprise API gateway teams that connected their first AI tool to an internal API via MCP used the same auth approach: a static token pasted into a config file. It worked. It demonstrated the value of the integration quickly. Then it stayed in production because nobody scheduled time to revisit it.

The April 2026 update to the MCP Authorization Specification changes the pressure to act. The spec now formalizes OAuth 2.1 and RFC 9728 Protected Resource Metadata as the standard authentication model for MCP servers. For platform teams running a production MCP gateway, this is not an abstract protocol concern. It is the spec catching up to what security and compliance teams have already been asking for: per-agent identity, explicit scope, and an audit trail that holds up under review.

This post covers what the MCP authorization specification actually requires, how Zerq's MCP surfaces implement it, and the step-by-step configuration your team needs to run auditable, scoped AI agent traffic in a regulated environment.

Why static tokens fail at the enterprise identity layer

The problem with static tokens for MCP is not that a single token is insecure in isolation. It is that static tokens do not give you identity at the agent level. When two agents share one API key, you have one identity in the audit trail, not two.

Most general-purpose gateways, including Kong, AWS API Gateway, and Azure APIM, will proxy MCP traffic without complaint. They enforce authentication at the credential level, not the agent level. The request log shows that credential api-key-b78f3a called POST /mcp/tools/call. It does not show that this was the accounts-payable automation agent, operating under a policy that should only allow read access to the invoices collection. Any mutating call it makes looks identical to a read call in the log.

The second failure mode is revocation. When a static token is shared across agents, a compromise forces you to rotate the credential and re-deploy every downstream integration simultaneously. In an environment with change control requirements, that is a meaningful operational cost, and it pushes teams toward longer token lifetimes, which makes the original problem worse.

The MCP Authorization Specification addresses this by requiring MCP servers to expose /.well-known/oauth-protected-resource (RFC 9728), pointing AI clients to the authorization server where they should request a scoped token for this specific resource. Tokens are per-agent, issued by the enterprise IdP, and carry scopes that the gateway can enforce at the request level.

How Zerq's enterprise API gateway implements MCP authorization

Zerq runs two MCP surfaces, each with its own auth model suited to its role:

Gateway MCP (/mcp by default) is the surface AI agents use to discover and call published APIs. Authentication uses Zerq's profile model (the same credential system REST clients use), with Authorization: Bearer, X-Client-ID, and X-Profile-ID headers identifying each agent's specific access contract. Profile auth supports token, JWT, OIDC, or mTLS, so you can bind it to your enterprise IdP rather than managing separate static secrets.

Management MCP (/api/v1/mcp) is the surface platform engineers and AI assistants use to configure the API catalog. Authentication here is pure OIDC: Authorization: Bearer <OIDC access token> with no client/profile headers. This surface exposes RFC 9728 Protected Resource Metadata discovery so any OAuth-capable MCP client can find the authorization server automatically.

OAuth discovery on Management MCP

Zerq serves the RFC 9728 discovery document at GET /api/v1/.well-known/oauth-protected-resource, with an alias at GET /.well-known/oauth-protected-resource. The response looks like this:

{
  "resource": "https://gateway.example.com/api/v1/mcp",
  "authorization_servers": [
    "https://auth.example.com/realms/api-platform"
  ],
  "scopes_supported": [
    "openid",
    "email",
    "profile",
    "roles",
    "audience"
  ],
  "bearer_methods_supported": ["header"],
  "resource_name": "Zerq Management MCP"
}

The resource field is populated from MCP_MANAGEMENT_PUBLIC_URL (or BACKEND_PUBLIC_URL + MCP_MANAGEMENT_PATH if the dedicated variable is not set). The authorization_servers field comes from OIDC_ISSUER_PUBLIC_URL or OIDC_ISSUER_URL. A correctly configured instance points directly to your Keycloak, Okta, Entra, or any OIDC-compliant IdP.

From here, the MCP client runs a standard OAuth 2.0 authorization code flow or client credentials flow against your IdP, receives a scoped access token, and presents it as a Bearer token to Management MCP. Zerq validates the token against the configured issuer and enforces role-based access via the roles claim, which maps to the platform roles viewer, modifier, auditor, or admin. The Management MCP documentation covers the RBAC model in detail.

Setting up a scoped AI agent identity for Gateway MCP

For Gateway MCP, the identity model is the client/profile pair. Here is the full setup sequence for a production agent:

  1. Create a client in the management UI under Clients → New Client. Give it a name that identifies the agent and environment, for example claude-ops-agent-prod. This generates the X-Client-ID value.

  2. Add a profile to the client (client detail → Add Profile). Fill in:

    FieldValue
    Nameprod-readonly
    ActiveEnabled
    Auth typeoidc (to bind to your IdP) or token for a managed static token
    Allowed methodsGET (blocks all mutating methods for read-only agents)
    IP restrictionsAgent egress CIDR, if known and stable
  3. Copy the credentials from the profile detail page: X-Client-ID, X-Profile-ID, and the full Authorization: Bearer <token> header string. The token value is shown once.

  4. Assign the client to collections: on each collection the agent needs, assign this client. Through list_collections, the agent will only see collections it has been explicitly assigned to. Scope is enforced at discovery, not just at execution.

  5. Attach a rate limit policy to the profile to cap the agent's throughput independently of human traffic.

  6. Test the identity and verify deny behavior before go-live:

# Step 1: initialize the MCP session
curl -i https://gateway.example.com/mcp \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $AGENT_TOKEN" \
  -H "X-Client-ID: claude-ops-agent-prod" \
  -H "X-Profile-ID: prod-readonly" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "initialize",
    "params": {
      "protocolVersion": "2024-11-05",
      "capabilities": {"tools": {}}
    }
  }'

# Step 2: list available tools using the session ID from step 1
curl -i https://gateway.example.com/mcp \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $AGENT_TOKEN" \
  -H "X-Client-ID: claude-ops-agent-prod" \
  -H "X-Profile-ID: prod-readonly" \
  -H "Mcp-Session-Id: $SESSION_ID" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'

# Step 3: verify scope enforcement — this POST should return 405
curl -i -X POST https://gateway.example.com/mcp \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $AGENT_TOKEN" \
  -H "X-Client-ID: claude-ops-agent-prod" \
  -H "X-Profile-ID: prod-readonly" \
  -H "Mcp-Session-Id: $SESSION_ID" \
  -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"execute_endpoint","arguments":{"endpoint_id":"orders-v1-delete","body":{}}}}'
  1. Confirm log entries show the client and profile identifiers for both the allowed call and the denied call. A 405 with client_id: claude-ops-agent-prod in the log confirms scope enforcement is working before any human sees the integration in production.

What the audit trail shows for AI agent traffic

Every execute_endpoint call through Gateway MCP creates a request log entry. The log includes method, path, status, latency, IP, and user agent, along with the X-Client-ID and X-Profile-ID values that identify the specific agent and its access contract.

A request log entry for a scoped agent looks like this:

{
  "request_id": "req_01HXYZ9W4K2P3Q7M",
  "timestamp": "2026-05-28T14:22:11.304Z",
  "client_id": "claude-ops-agent-prod",
  "profile_id": "prod-readonly",
  "method": "POST",
  "path": "/invoices/v1/list",
  "status": 200,
  "latency_ms": 87,
  "ip": "10.0.4.12",
  "user_agent": "Claude/3.5 MCP-client/1.0"
}

An agent that attempts a call outside its assigned collection returns 403, and the request log captures the denied attempt with the full client and profile identity. A security investigation can answer "did this agent ever attempt to access a collection it was not assigned to?" with a single filter on client_id and status=403.

Configuration changes made through Management MCP (creating a client, updating a policy, or toggling a proxy) appear in the platform audit log with actor_type: service, the actor's OIDC subject claim, and the full request and response body. This means the same audit trail covers both human and AI-originated configuration changes, and a compliance team can distinguish a platform engineer running a manual update from an automation agent running a scheduled sync. The Zerq observability and security documentation covers the full audit field schema and SIEM export options.

What this looks like at a capital markets firm

A capital markets team recently had three active AI integrations sharing a single service account token. Their audit trail showed one actor for all API activity. They could not answer a basic security question: did the ops automation agent change a rate limit policy last week?

After migrating to dedicated client/profile identities through Zerq, they have three distinct actors in the log:

  • A read-only market data agent assigned to the market-data collection, GET-only profile, with a rate limit that prevents accidental data dumps.
  • A write-capable order entry agent assigned to the orders collection, POST allowed, restricted to the execution service's IP CIDR.
  • A platform ops agent connected to Management MCP via OIDC with the modifier role, used to automate routine catalog updates.

When the market data agent hit a burst ceiling during a batch sync, the 429 entries in the request log immediately showed which client and profile triggered it. Before the migration, that diagnosis required parsing unstructured logs by hand.

The Management MCP OAuth discovery endpoint allowed their Cursor integration to pick up the authorization server from /.well-known/oauth-protected-resource without any manual token configuration. The platform team changed nothing in how Cursor connects. It followed the RFC 9728 discovery flow automatically, received an OIDC token from the enterprise IdP, and authenticated to Management MCP the same way any human admin does.

Closing

The MCP Authorization Specification is not a new idea for enterprise platform engineers. It is OAuth applied to a new transport. What it changes is the expectation: production MCP deployments in regulated environments should have per-agent identity, explicit scope, and a traceable audit trail. Zerq delivers this through the same identity model you already use for REST traffic. There is no separate credential path, no separate audit stream, and no additional enforcement layer to operate.

A gateway that enforces scope at the profile level, exposes OAuth discovery metadata on the management surface, and records every tool call with the agent's identity gives compliance teams and security teams what they actually need. The goal is not connectivity. It is accountability.


Zerq is an enterprise API gateway built for regulated industries — one platform for API management, AI agent access, compliance audit, and developer portal, running entirely in your own infrastructure. See how it works or request a demo to walk through your specific requirements.