> ## 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 {
  createJwtVerifier,
  oauthCustomProvider,
  type OAuthAuthInfo,
  type OAuthMetadata,
} from "mcp-use/oauth";

const issuer = "https://auth.example.com";

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 createJwtVerifier({
        issuer,
        jwksUrl: new URL(`${issuer}/.well-known/jwks.json`),
        resource,
      });
    },

    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`.

If your authorization server issues standard JWTs, `createJwtVerifier` does all of that for you and is the same helper the built-in providers use. Given a `jwksUrl`, it checks the signature, issuer, required `exp` and `sub` claims, and resource binding. It then returns the auth information in the shape the SDK expects.

For standards-conformant JWT access tokens, the verifier checks the [`aud` claim](https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.3) for the MCP resource URL. The `aud` claim can be a string or an array containing the resource URL. [RFC 8707](https://www.rfc-editor.org/rfc/rfc8707.html#section-2) defines `resource` as an authorization and token request parameter, not as a JWT access-token claim. For compatibility with providers that include a non-standard `resource` claim, the verifier accepts a string-valued `resource` claim equal to the MCP resource URL and uses it instead of `aud`. Pass `audience` when the provider's JWT audience differs from the MCP resource.

Write the verifier by hand when the provider does not issue JWTs, for example when you have to call a token introspection endpoint.

`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>
