> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mcp-use.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Custom Provider

> Integrate a DCR-capable OAuth authorization server with an MCP server.

Use `oauthCustomProvider` when your authorization server supports Dynamic Client Registration (DCR) but mcp-use does not provide a built-in adapter. The MCP client registers upstream; your server publishes metadata, verifies access tokens, and maps verified claims into application identity.

## Required contract

Import `MCPServer` from `mcp-use` and the provider contract from `mcp-use/oauth`.

```typescript theme={null}
import { MCPServer } from "mcp-use";
import {
  oauthCustomProvider,
  type OAuthAuthInfo,
  type OAuthMetadata,
} from "mcp-use/oauth";
import { createRemoteJWKSet, jwtVerify } from "jose";

const issuer = "https://auth.example.com";
const jwks = createRemoteJWKSet(new URL(`${issuer}/.well-known/jwks.json`));

const oauthMetadata = {
  issuer,
  authorization_endpoint: `${issuer}/oauth/authorize`,
  token_endpoint: `${issuer}/oauth/token`,
  registration_endpoint: `${issuer}/oauth/register`,
  response_types_supported: ["code"],
  grant_types_supported: ["authorization_code", "refresh_token"],
  code_challenge_methods_supported: ["S256"],
} satisfies OAuthMetadata;

type AppUser = {
  id: string;
  email?: string;
  organizationId?: string;
};

const server = new MCPServer({
  name: "custom-auth-server",
  version: "1.0.0",
  oauth: oauthCustomProvider<AppUser>({
    oauthMetadata,

    createTokenVerifier(resource) {
      return {
        async verifyAccessToken(token) {
          const { payload } = await jwtVerify(token, jwks, {
            issuer,
            audience: resource.href,
          });

          if (!payload.sub || !payload.exp) {
            throw new Error("Token is missing sub or exp");
          }

          const scopes =
            typeof payload.scope === "string"
              ? payload.scope.split(/\s+/).filter(Boolean)
              : [];

          return {
            token,
            clientId:
              typeof payload.client_id === "string"
                ? payload.client_id
                : typeof payload.azp === "string"
                  ? payload.azp
                  : "",
            scopes,
            expiresAt: payload.exp,
            resource,
            extra: { payload: payload as Record<string, unknown> },
          };
        },
      };
    },

    mapAuthInfo(authInfo: OAuthAuthInfo) {
      const payload = authInfo.extra?.payload as Record<string, unknown>;
      if (typeof payload.sub !== "string") {
        throw new Error("Verified token is missing sub");
      }

      return {
        user: {
          id: payload.sub,
          email: typeof payload.email === "string" ? payload.email : undefined,
          organizationId:
            typeof payload.organization_id === "string"
              ? payload.organization_id
              : undefined,
        },
        payload,
        permissions: Array.isArray(payload.permissions)
          ? payload.permissions.filter(
              (value): value is string => typeof value === "string",
            )
          : [],
      };
    },
  }),
});

export default server;
```

`createTokenVerifier(resource)` receives the resolved canonical MCP resource. The verifier must validate token signature or introspection, issuer, expiry, and that resource, then return SDK auth information including `scopes`, `expiresAt`, and the validated `resource`.

`oauthMetadata` is the authorization server's RFC 8414 metadata. It must describe the actual upstream endpoints and include the `registration_endpoint` used by MCP clients.

`mapAuthInfo(authInfo)` receives only verified SDK auth information. It must return:

* `user`: your provider-specific normalized user, with `id` as the stable subject;
* `payload`: the verified claims or introspection response;
* `permissions`: application permissions derived from verified data.

The server exposes those values as `ctx.auth.user`, `ctx.auth.payload`, and `ctx.auth.permissions`. `ctx.auth.scopes` remains the verifier's verified `authInfo.scopes`.

## Verify the integration

Test that:

* the client discovers metadata containing the correct registration endpoint;
* client registration, PKCE authorization, and token exchange happen upstream;
* tokens with the wrong issuer, expiry, signature, or MCP resource are rejected;
* mapped identity and permissions contain only verified data.

## Next steps

<CardGroup cols={2}>
  <Card title="User Context" icon="user" href="/v2/typescript/server/authentication/user-context">
    Use the mapped user, scopes, and permissions in callbacks.
  </Card>

  <Card title="OAuth overview" icon="shield-check" href="/v2/typescript/server/authentication/index">
    Compare built-in providers.
  </Card>
</CardGroup>
