Governing Remote MCP Servers: An OAuth Protected Resource Metadata (PRM) Proxy Pattern

Source: MuleSoft Blog•

Governing Remote MCP Servers: An OAuth Protected Resource Metadata (PRM) Proxy Pattern

Remote MCP servers use OAuth 2.0 with the discovery pattern defined in RFC 9728 (Protected Resource Metadata, or PRM). RFC 9728 requires that the resource field in the PRM document match the URL the client actually called — byte for byte. That single rule is what makes a reverse proxy in front…

Remote MCP servers use OAuth 2.0 with the discovery pattern defined in RFC 9728 (Protected Resource Metadata, or PRM). RFC 9728 requires that the resource field in the PRM document match the URL the client actually called — byte for byte. That single rule is what makes a reverse proxy in front of a remote MCP server break: the client calls the proxy URL, the upstream vendor’s PRM advertises the vendor URL, the two strings do not match, and the OAuth flow aborts before it starts.

This post walks through the mismatch, why raw passthrough cannot work, and a small PRM-rewriting service on MuleSoft Omni Gateway that resolves it while leaving the vendor’s authorization server untouched. The result is that standards-conformant MCP clients (VS Code, Claude Code, Cursor) can talk to Figma, GitHub, Atlassian, Linear, Notion, and internal MCP servers through a single enterprise-controlled path without the client rejecting the handshake.

Note : On the vendors named. Figma, GitHub, Atlassian, Linear, Sentry, Notion, and AWS are named as illustrative examples of vendors that have publicly shipped remote MCP endpoints as of this writing. This is not an exhaustive list, nor is the pattern vendor-specific. It applies to any HTTP MCP server that advertises Protected Resource Metadata (PRM) per RFC 9728.

What the proxy pattern delivers

  • A single gateway path in front of each remote or internal MCP server that becomes the attach point for whatever gateway policies the enterprise already runs on outbound API traffic.
  • Native OAuth UX preserved — the browser-based authorization code + PKCE flow runs directly between the client and the vendor’s authorization server (e.g., api.figma.com, github.com/login/oauth).
  • Onboarding of a new approved MCP server as a configuration change rather than a new development project.
  • Compatibility with standards-conformant MCP clients (VS Code, Claude Code, Cursor).

How Remote MCP Auth Works (RFC 9728)

A remote MCP server is an HTTP endpoint (usually SSE / streamable) that speaks JSON-RPC per the MCP spec. When an unauthenticated client tries to talk to it, the server issues an OAuth 2.0 challenge using the pattern defined in RFC 9728 (Protected Resource Metadata).

The full handshake from a VS Code (IDE) → Omni Gateway → MCP Server (Example Figma MCP Remote server) trace looks like this:

The Three Handshake Phases

Phase A — Challenge: The client (e.g., VS Code) sends an unauthenticated JSON-RPC initialization request. The gateway rejects it with 401 Unauthorized containing a WWW-Authenticate: Bearer header, pointing the client to discovery.

Phase B — Discovery and OAuth Dance: The client follows the discovery link to fetch the Protected Resource Metadata (PRM) and Authorization Server Metadata. It then runs a browser-based authorization code + PKCE flow directly against the real authorization server (e.g., api.figma.com) to retrieve a Bearer token.

Phase C — Authenticated MCP Traffic: The client attaches the access token to all subsequent JSON-RPC requests (opening SSE streams, listing prompts, discovering tools).

The Technical Hurdle: Strict PRM Matching

RFC 9728 defines a validation rule to prevent phishing and credential harvesting: the resource string inside the PRM payload must match the requested URL byte-for-byte.

This alignment prevents an attacker from spoofing resource metadata or tricking an AI client into running an OAuth flow against an unauthorized or malicious authorization server. It also means a central gateway cannot simply pass through an upstream vendor’s raw PRM response unaltered.

Why Raw Passthrough Fails

When a proxy forwards an upstream vendor’s native PRM response, a URL mismatch breaks authentication:

The client evaluates the response against the address it actually called https://mulesoft-omni-gateway.cloudhub.io/figma/ and aborts execution:

Architecture & Configurations

To resolve the metadata mismatch, the PRM response must be dynamically rewritten in flight so the resource field reflects the enterprise proxy hostname while leaving vendor authorization targets intact.

This architecture uses three API instances on Anypoint API Manager (fronted by Flex Gateway) paired with a Mule 4 application to handle metadata transformations.

Omni Gateway Route Mapping

Note: Pure vendor proxies pass headers (Authorization, Accept, Content-Type, Mcp-Protocol-Version, Mcp-Session-Id) and JSON-RPC payloads directly to upstreams without transformation.

Client-Side Configuration (mcp.json)

Developers configure their local AI tools using enterprise gateway endpoints:

Mule PRM Transformation Service

  • Mule App Name: mcp-oauth-prm-metadata
  • Listener path: /* (The gateway strips the /.well-known/oauth-protected-resource/ prefix before passing the path)
  • Method: GET
  • Logic: Intercepts outgoing PRM metadata requests, rewrites the resource field to match the enterprise proxy URI expected by the client, and returns the modified metadata payload.

Complete DataWeave Script

var host = attributes.headers["x-forwarded-host"] default "mulesoft-omni-gateway.cloudhub.io"var upstream = ((attributes.requestPath splitBy "/") filter (!isEmpty($)))[0] default ""

var registry = { "figma": { resource_name: "Figma MCP Server (via customer proxy)", authorization_servers: ["https://api.figma.com"], scopes_supported: ["mcp:connect"] }, "github": { resource_name: "GitHub MCP Server (via customer proxy)", authorization_servers: ["https://github.com/login/oauth"], scopes_supported: [ "repo", "read:org", "read:user", "user:email", "read:packages", "write:packages", "read:project", "project", "gist", "notifications", "workflow", "codespace" ] }}

var entry = registry[upstream]---if (entry == null) { error: "unknown_upstream", upstream: upstream }else { resource: "https://" ++ host ++ "/" ++ upstream ++ "/", resource_name: entry.resource_name, authorization_servers: entry.authorization_servers, scopes_supported: entry.scopes_supported, bearer_methods_supported: ["header"] }

Securing the PRM Rewrite Service Itself

The PRM discovery endpoint is intentionally unauthenticated — RFC 9728 requires that a client be able to reach /.well-known/oauth-protected-resource before it has any token. That does not mean the endpoint is untrusted terrain. The response tells the client which authorization server to talk to; if an attacker could influence that response, they could redirect an OAuth flow at a rogue authorization server. Concrete hardening this service relies on:

  • Static registry, no reflection of untrusted input. The authorization_servers and scopes_supported values are drawn from an in-code registry keyed by upstream slug. Nothing from the request body, query string, or arbitrary headers is echoed into these fields. The only request-derived value in the response is the host used to build the resource, which is sourced from the trusted x-forwarded-host header set by the gateway (see next bullet).
  • Trusted x-forwarded-host only. Because x-forwarded-host is honored, the endpoint must be reachable only through the Flex Gateway, which sets that header authoritatively. Direct exposure of the CloudHub URL (mcp-oauth-prm-metadata.cloudhub.io) must be blocked at the network layer or by requiring a gateway-issued client credential on the internal hop, so an attacker cannot inject an attacker-controlled x-forwarded-host value directly.
  • HTTPS only, HSTS. TLS is enforced by the gateway; a downgrade to HTTP would let a network attacker rewrite the response and defeat every other control listed here.
  • Corporate VPN / network-only reachability. Just as SSO restricts who can authenticate, this restricts from where the endpoint can be reached at all. Because the PRM endpoint is intentionally unauthenticated, network-level restriction is the most important single defense — an off-network client cannot complete a TCP handshake to the gateway, let alone enumerate registered upstreams. Typical enforcement layers, in order of strictness: Private DNS + private network. The gateway is fronted by a private endpoint (e.g., AWS PrivateLink, Azure Private Endpoint, or a CloudHub VPC with no public ingress). Its DNS name resolves only inside the corporate network / VPN split-tunnel; off-network clients get NXDOMAIN or an unroutable address. IP allowlisting. The gateway or its front-door WAF accepts connections only from the corporate VPN egress ranges and known office CIDRs. Device attestation / mTLS from managed devices. Layered on top of the above: the gateway requires a client certificate provisioned by MDM to the developer’s laptop, so an attacker who somehow lands on the VPN still cannot reach the gateway from an unmanaged device.
  • Private DNS + private network. The gateway is fronted by a private endpoint (e.g., AWS PrivateLink, Azure Private Endpoint, or a CloudHub VPC with no public ingress). Its DNS name resolves only inside the corporate network / VPN split-tunnel; off-network clients get NXDOMAIN or an unroutable address.
  • IP allowlisting. The gateway or its front-door WAF accepts connections only from the corporate VPN egress ranges and known office CIDRs.
  • Device attestation / mTLS from managed devices. Layered on top of the above: the gateway requires a client certificate provisioned by MDM to the developer’s laptop, so an attacker who somehow lands on the VPN still cannot reach the gateway from an unmanaged device.

The effect is two independent gates on this endpoint: network reachability (VPN / corporate network) and response integrity (the static-registry and x-forwarded-host controls above). Compromising one does not bypass the other.

Moving Forward

The OAuth PRM Proxy pattern gives security teams a single point of visibility over agentic AI tool traffic without asking developers to adopt fragmented authentication flows or custom infrastructure.

Centralizing remote MCP traffic through Anypoint Omni Gateway lets enterprises adopt the growing ecosystem of AI developer tools under the same audit, allowlist, and DLP controls already applied to other outbound traffic.

Enterprise security, IT, and engineering teams can apply the OAuth PRM Proxy pattern to scale AI agent connectivity under existing policy controls.

What this article says