Skip to main content
Workflows Library MCP Directory Realtime AI News Sponsor Tier Subscribe
Front Page / AI Tools / Deep Dive

Build an Enterprise-Managed Authorization MCP Server with Zero-Touch OAuth

The MCP 2026-07-28 spec is stateless and the Enterprise-Managed Authorization extension is stable, so your IdP can authorize agents with zero per-user browser consent. This dispatch builds a TypeScript FastMCP server wired to Okta, Entra ID, or Keycloak.

Deepak Bagada

Deepak Bagada

CEO, SaaSNext

Aug 19, 2026 Published
|
Aug 19, 2026 Updated
|
9 Minutes Reading Time
Core Takeaways for Founders & Builders
  • MCP went stateless on 2026-07-28, which unblocked the stable Enterprise-Managed Authorization extension for zero-touch OAuth.
  • The enterprise IdP (Okta, Entra ID, Keycloak) authorizes agents on behalf of users, eliminating per-user browser consent.
  • RFC 7591 dynamic client registration plus RFC 8693 token exchange and RFC 7662 introspection make the flow fully automated.
  • A TypeScript FastMCP server with per-request bearer-token validation slots directly into the Claude Desktop mcpServers config.

By Deepak Bagada, CEO at SaaSNext & Principal AI Architect.

Build an Enterprise-Managed Authorization MCP Server with Zero-Touch OAuth

On July 28, 2026, the Model Context Protocol (MCP) crossed a threshold every platform team had been waiting for: the specification officially went stateless. Servers no longer hold sessions between requests, and every tool invocation is a self-contained, independently auditable unit. That change unblocked the extension enterprises have needed most — Enterprise-Managed Authorization — which is now stable and ready for production. Instead of pushing every employee through a browser consent screen, the organization's identity provider (Okta, Entra ID, or Keycloak) authorizes AI agents on behalf of users. Zero per-user interaction, zero consent fatigue, full audit trail.

This dispatch walks you through building a production-grade Enterprise-Managed Authorization MCP server in TypeScript with FastMCP: inputSchema definitions, OAuth 2.1/2.0 dynamic client registration, token exchange, and a ready-to-paste Claude Desktop mcpServers config. For the broader tooling picture, browse the MCP Directory or the Workflows desk. Spec-trackers should follow our latest AI news desk.

Why the Stateless Spec Changes Everything

Before the July 2026 revision, MCP servers routinely kept session state: which agent connected, which scopes it held, which token authenticated it. That model collapses in production. Agents reconnect constantly, clients restart mid-conversation, and load balancers spray requests across replicas. A stateless server stores nothing between calls. Every JSON-RPC message — initialize, tools/list, tools/call — carries everything the server needs: an access token in the Authorization header plus the request parameters themselves.

Three concrete wins:

  1. Horizontal scaling. Any replica can serve any request because there is no shared session store to synchronize.
  2. Deterministic audits. Each call is a complete, self-contained record — who called, with which scopes, against which tool, with what result.
  3. Zero-touch OAuth becomes possible. Because the token travels with each request, an enterprise IdP can issue, validate, and revoke credentials without any server-side session machinery.

The stateless model is also the security baseline for Enterprise-Managed Authorization: nothing sensitive lives on the MCP server that an attacker could exfiltrate with a single stray SQL query.

What Is Enterprise-Managed Authorization?

Enterprise-Managed Authorization is the MCP extension that replaces interactive OAuth consent with policy-driven delegation. In a classic MCP deployment, a developer runs a server and each user clicks through an authorization screen in the browser. That is fine for public tools; it is unworkable for a 40,000-person enterprise where the CISO needs to prove exactly who can make an agent call orders.release_payment.

With Enterprise-Managed Authorization, the flow is inverted:

  • The host (the MCP client, e.g. Claude Desktop or an internal gateway) contacts the enterprise IdP.
  • The IdP — not the user — decides whether the agent may act, based on group membership, device posture, and policy.
  • The server validates each incoming token against the IdP and enforces scopes per tool.

The extension leans on a stack of IETF standards that are all battle-tested in production identity systems:

Standard Role in the flow
RFC 7591 Dynamic Client Registration The server registers itself with the IdP at startup and receives client_id / client_secret without a developer-portal visit
RFC 8693 Token Exchange A host token is exchanged for a down-scoped token bound to the MCP resource
RFC 7662 Token Introspection The server checks a token's active state and scopes when local validation fails
RFC 9068 JWT Profile IdP-issued access tokens become standardized JWTs the server verifies with JWKS
OAuth 2.1 The 2026 OAuth refresh — PKCE mandatory, redirects tightened, secrets optional for public clients

The zero-touch promise

"Zero-touch" means no per-user browser consent. An admin provisions an app registration once; users never see an approval screen. When a new hire joins, their agent automatically inherits the group-based policy. When they leave, revocation is instant because the IdP stops issuing tokens. This is the pattern enterprises already run for every other workload — MCP has just caught up.

The Zero-Touch OAuth Flow, Step by Step

  1. Dynamic client registration. On startup the MCP server calls the IdP's RFC 7591 endpoint and receives client_id, client_secret, and allowed redirect URIs. This replaces manual app registration in a dev portal — a requirement for fleets of servers spinning up per environment.
  2. Agent request. The user asks Claude Desktop to use a tool. The host requests an access token from the IdP, presenting its own credentials plus the user's context.
  3. Token exchange. The host exchanges its token for a down-scoped, audience-bound token where aud is the MCP server's resource URI and scope is limited to the tools the agent may invoke.
  4. Validation. The MCP server verifies the JWT signature against the IdP's JWKS, checks iss, aud, exp, and enforces per-tool scopes. No server-side session is created.
  5. Stateless execution. The tool runs, the response streams back, and nothing about the caller is persisted.

The entire dance uses the existing Authorization: Bearer <token> header, so the MCP wire protocol stays untouched — the spec change was about removing state, not adding new verbs.

Building the Server (TypeScript + FastMCP)

1. Project setup

Create the project and install the SDK:

npm init -y
npm install @modelcontextprotocol/sdk jose zod
npm install -D typescript tsx

package.json scripts for the dev/build loop:

{
  "name": "enterprise-auth-mcp",
  "version": "1.0.0",
  "type": "module",
  "engines": { "node": ">=22" },
  "scripts": {
    "dev": "tsx src/server.ts",
    "start": "node dist/server.js",
    "typecheck": "tsc --noEmit"
  },
  "dependencies": {
    "@modelcontextprotocol/sdk": "^2.0.0",
    "jose": "^6.0.0",
    "zod": "^4.0.0"
  }
}

2. inputSchema definitions

FastMCP accepts a plain JSON Schema object for each tool's inputSchema. Define them once, export them, and reference them in tool registrations:

const exchangeTokenSchema = {
  type: "object",
  properties: {
    audience: {
      type: "string",
      description: "The MCP resource URI the exchanged token must be bound to"
    },
    scopes: {
      type: "array",
      items: { type: "string" },
      minItems: 1,
      description: "Down-scoped scopes, e.g. orders.read or catalog.write"
    },
    ttl_seconds: {
      type: "integer",
      minimum: 300,
      maximum: 86400,
      default: 900,
      description: "Lifetime of the exchanged token"
    }
  },
  required: ["audience", "scopes"],
  additionalProperties: false
} as const;

3. Full FastMCP server

The handlers below derive identity from the per-request Authorization header — that is the stateless model in action:

import { FastMCP } from "@modelcontextprotocol/sdk/server/mcp.js";
import { validateAccessToken, exchangeTokenFor, revokeAtIdp } from "./oidc.js";

const exchangeTokenSchema = {
  type: "object",
  properties: {
    audience: { type: "string", description: "The MCP resource URI the token must be bound to" },
    scopes: { type: "array", items: { type: "string" }, minItems: 1, description: "Down-scoped scopes" },
    ttl_seconds: { type: "integer", minimum: 300, maximum: 86400, default: 900, description: "Token lifetime" }
  },
  required: ["audience", "scopes"],
  additionalProperties: false
} as const;

const server = new FastMCP("enterprise-tools", {
  version: "1.0.0",
  capabilities: { tools: {}, auth: { enterpriseManaged: true } }
});

async function principal(extra: any) {
  const token = extra?.headers?.authorization?.replace("Bearer ", "");
  if (!token) throw new Error("missing bearer token");
  return validateAccessToken(token); // JWKS + issuer + audience + scope checks
}

server.tool("exchange_token",
  {
    title: "Exchange a host token for a scoped MCP token",
    description: "Performs an RFC 8693 token exchange against the enterprise IdP.",
    inputSchema: exchangeTokenSchema
  },
  async ({ audience, scopes, ttl_seconds }, extra) => {
    const p = await principal(extra);
    const exchanged = await exchangeTokenFor(p.sub, audience, scopes, ttl_seconds);
    return { content: [{ type: "text", text: JSON.stringify(exchanged, null, 2) }] };
  }
);

server.tool("list_entitlements",
  {
    title: "List the agent's authorized scopes",
    description: "Returns the scopes a validated token may exercise against this server.",
    inputSchema: { type: "object", properties: {}, additionalProperties: false }
  },
  async (_args, extra) => {
    const p = await principal(extra);
    return { content: [{ type: "text", text: JSON.stringify({ subject: p.sub, scope: p.scope }, null, 2) }] };
  }
);

server.tool("revoke_token",
  {
    title: "Revoke an exchanged token",
    description: "Immediately revokes a previously exchanged access token at the IdP.",
    inputSchema: {
      type: "object",
      properties: { token: { type: "string", description: "The token value to revoke" } },
      required: ["token"]
    }
  },
  async ({ token }) => {
    await revokeAtIdp(token);
    return { content: [{ type: "text", text: "revoked" }] };
  }
);

await server.start({ transportType: "http", hostname: "0.0.0.0", port: 8787 });

The principal() helper reads the incoming Authorization header on every call, so any replica can process any request. Helpers exchangeTokenFor and revokeAtIdp wrap your IdP's RFC 8693 and RFC 7009 endpoints.

4. Token validation against your IdP

The validator is the heart of the security model. Prefer local JWKS verification for latency, and fall back to RFC 7662 introspection for opaque or just-issued tokens:

import { createRemoteJWKSet, jwtVerify } from "jose";

const IDP_ISSUER = process.env.IDP_ISSUER!;
const RESOURCE_AUDIENCE = process.env.MCP_AUDIENCE!;
const jwks = createRemoteJWKSet(new URL(`${IDP_ISSUER}/oauth2/jwks`));

export async function validateAccessToken(raw: string) {
  const { payload } = await jwtVerify(raw, jwks, {
    issuer: IDP_ISSUER,
    audience: RESOURCE_AUDIENCE,
    algorithms: ["RS256"]
  });
  if (!payload.scope || payload.token_use === "id") {
    throw new Error("invalid token claims");
  }
  return payload;
}

For tokens you cannot verify locally, call the introspection endpoint with the server's own credentials:

const resp = await fetch(`${IDP_ISSUER}/oauth2/introspect`, {
  method: "POST",
  headers: { "content-type": "application/x-www-form-urlencoded" },
  body: new URLSearchParams({
    token: raw,
    client_id: CLIENT_ID,
    client_secret: CLIENT_SECRET,
    token_type_hint: "access_token"
  })
});

Set IDP_ISSUER, MCP_AUDIENCE, CLIENT_ID, and CLIENT_SECRET as environment variables — never in the repository.

Claude Desktop mcpServers Configuration

Claude Desktop, and every MCP 2026-compliant client, wires to the server with an mcpServers block. Dynamic client registration means the client can register itself against your IdP without portal choreography:

{
  "mcpServers": {
    "enterprise-tools": {
      "type": "http",
      "url": "https://mcp.corp.example.com/enterprise-tools",
      "authorization": {
        "type": "oauth2",
        "issuer": "https://idp.corp.example.com/oauth2",
        "authorization_endpoint": "https://idp.corp.example.com/oauth2/v1/authorize",
        "token_endpoint": "https://idp.corp.example.com/oauth2/v1/token",
        "client_registration": {
          "type": "dynamic",
          "endpoint": "https://idp.corp.example.com/oauth2/v1/clients"
        },
        "scopes": ["mcp.invoke", "iam.entitlements:read"]
      }
    }
  }
}

Because the flow is enterprise-managed, Claude Desktop requests the token silently through your IdP. A browser opens only for device verification if your org requires MFA — never for a per-user tool consent screen.

Which IdP Flow Should You Use?

Flow Who authorizes User interaction PKCE Best for
Zero-touch enterprise IdP policy (groups + posture) None Required Large orgs, regulated data, audit-heavy workloads
Authorization Code + PKCE End user in browser One consent prompt Required Public SaaS, indie devs, single-tenant apps
Client Credentials Service principal + secret None n/a CI/CD, server-to-server pipelines
Device Authorization End user on a device Enter a code Required CLIs, headless machines, kiosks

For a corporate MCP deployment you will almost always want the first row. Everything below it is what you already had before Enterprise-Managed Authorization went stable.

OAuth 2.0 Security Guide for MCP

  • Short-lived tokens. Configure the IdP to issue access tokens in the 5–15 minute range. If a token leaks from a log line or a debugger, the blast radius is minutes, not hours.
  • Audience binding. Every MCP server must have a unique resource URI in its aud claim. Reject any token whose audience does not match. This stops a leaked token from one server being replayed against another.
  • Scope minimization. Grant the agent the narrowest scope per tool. Model the MCP tool set as scopes (orders.read vs orders.write), not one big mcp.* wildcard.
  • Validate more than the signature. Check iss, aud, exp, nbf, and token_use explicitly. Signature validity alone is not authorization.
  • Never log tokens. Redact the Authorization header in access logs; log the token's jti instead, so you can correlate revocations without storing secrets.
  • Support revocation. Wire RFC 7009 revocation to the IdP and expose it as a tool so incident response can kill a credential mid-conversation.
  • Encrypt in transit and at rest. TLS 1.3 on the MCP transport, and never persist exchanged tokens — the stateless model has no session store; keep it that way.
  • Rotate the client secret. Dynamic registration gives you a rotation endpoint; automate monthly rotation and alert on any client using a credential older than 60 days.

Wrapping Up

Enterprise-Managed Authorization turns MCP from a developer convenience into a governed corporate capability. With the stateless 2026-07-28 spec and stable zero-touch OAuth, you get dynamic client registration, IdP-issued tokens, and per-tool scope enforcement with no browser prompts and no server-side session state. The TypeScript server above is a working skeleton — add your organization's tools, wire the validator to your IdP, and your agents are enterprise-ready.

Keep the surrounding pieces sharp: audit your tool inventory in the MCP Directory, and study how other teams assemble these patterns on the Workflows desk. The MCP ecosystem moves weekly — the latest AI news desk will keep you ahead of the next spec change.

Executive Briefing

Enjoyed this breakdown? Get our morning dispatch in your inbox.

Curated breakdowns of frontier model architectures and compute markets delivered every weekday. Zero fluff.

Frequently Asked Questions
The enterprise IdP authorizes agents on behalf of users based on group and device policy, so no per-user browser consent screen appears during agent tool calls.
It removed server-side session state so any replica can serve any request, enabling horizontal scaling, deterministic audit logs, and per-request token validation.
Yes. OAuth 2.1 makes PKCE mandatory even for confidential clients, and it protects the authorization code flow against interception and replay.
Yes. With Enterprise-Managed Authorization the host exchanges tokens silently with your IdP; a browser only appears if your org requires device or MFA verification.
Disable the user or group in the IdP, call the RFC 7009 revocation endpoint, or invoke the revoke_token tool. Every request is validated per call, so revocation is immediate.
Deepak Bagada
Author Profile

Deepak Bagada

CEO, SaaSNext

Deepak Bagada is the CEO of SaaSNext and founder of Daily AI World. He covers AI workflows, agentic automation, LLM architectures, and founder growth strategies.

Related Intelligence Analysis

Briefing AI Tools

Vercel AI SDK Tool Calling React: 5 Steps (2026)

Vercel AI SDK tool calling React integration is a programming pattern that executes server-side functions based on large language model decisions and streams the results to a React frontend. By combining streamText with...

Deepak Bagada Deepak Bagada
12m read
Breaking AI Tools

Fact-Density vs. Word Count: The New SEO for 2026

Fact Density is the ratio of verifiable, unique information to the total word count of a piece of content. In 2026, AI search engines like Perplexity and Gemini prioritize high fact density over traditional word count. A...

Deepak Bagada Deepak Bagada
4m read
Audio Briefing
Accessibility Preferences
High Contrast Mode
Accessible Reading Font

Keyboard Shortcuts

Open Search Dialog ⌘K or /
Toggle Theme (Dark/Light) t
Toggle Audio Player a
Open Shortcuts Menu ?
Close Active Dialog Esc