Configure MCP Auth in MCP server
With the latest MCP specification, your MCP server acts as a Resource Server that validates access tokens issued by external authorization servers.
The MCP SDK asks you to bring two things: a token verifier and your auth metadata. Both come from one MCPAuth instance. This page covers its configuration and the metadata half; Configure Bearer auth covers the verifier half.
Each MCPAuth instance represents one protected resource trusting one authorization server. Its configuration is the Protected Resource Metadata declaration of your MCP server (RFC 9728): everything you declare is published through the metadata endpoints, and the token verifier enforces what is declared.
Configuring MCP Auth takes three steps:
- Provide the authorization server config: tell MCP Auth which authorization server to trust and how to obtain its metadata
- Declare the protected resource metadata: define your MCP server's resource identifier and supported scopes
- Serve the OAuth discovery documents: feed the metadata to the MCP SDK's helpers so clients can discover it
Step 1: Provide the authorization server config
On-demand discovery
The simplest way is to provide just the issuer and type. The metadata is fetched from the server's well-known endpoint when first needed and cached afterwards:
const authServerConfig = { issuer: 'https://auth.logto.io/oidc', type: 'oidc' }; // or 'oauth'
This performs no I/O at startup, which makes it required for edge runtimes like Cloudflare Workers where network calls are not allowed during module initialization, and a fast default everywhere else.
The well-known URL is derived from the issuer according to the server type:
- OpenID Connect Discovery (
type: 'oidc'): the path is appended to the issuer. For example, issuerhttps://my-project.logto.app/oidc→https://my-project.logto.app/oidc/.well-known/openid-configuration. - OAuth 2.0 Authorization Server Metadata (
type: 'oauth', RFC 8414): the path is inserted between the origin and the issuer path. For example, issuerhttps://my-project.logto.app/oauth→https://my-project.logto.app/.well-known/oauth-authorization-server/oauth.
Pre-fetch the metadata at startup
If your provider conforms to one of the following standards:
You can use fetchServerConfig to retrieve and validate the metadata before initializing MCPAuth, so misconfigurations fail fast at startup. It also validates that the issuer field in the fetched metadata matches the issuer you provided:
import { fetchServerConfig } from 'mcp-auth';
const authServerConfig = await fetchServerConfig('https://auth.logto.io/oidc', { type: 'oidc' }); // or 'oauth'
Other ways to configure authorization server metadata
Fetch metadata from a specific URL
If your provider serves its metadata from a non-standard URL, you can fetch it directly (no issuer match validation is performed in this case):
import { fetchServerConfigByWellKnownUrl } from 'mcp-auth';
const authServerConfig = await fetchServerConfigByWellKnownUrl('<metadata-url>', { type: 'oidc' }); // or 'oauth'
Custom data transpilation
In some cases, the metadata returned by the provider may not conform to the expected format. If you are confident that the provider is compliant, you can use the transpileData option to modify the metadata before it is used:
import { fetchServerConfig } from 'mcp-auth';
const authServerConfig = await fetchServerConfig('<auth-server-issuer>', {
type: 'oidc',
transpileData: (data) => ({ ...data, response_types_supported: ['code'] }),
});
The transpileData option is available for both fetchServerConfig and fetchServerConfigByWellKnownUrl.
Manually provide metadata
If your provider does not support metadata fetching, you can manually provide the metadata object:
const authServerConfig = {
metadata: {
issuer: '<issuer-url>',
// Metadata fields use the wire format (snake_case), as defined by RFC 8414
authorization_endpoint: '<authorization-endpoint-url>',
token_endpoint: '<token-endpoint-url>',
jwks_uri: '<jwks-uri>',
response_types_supported: ['code'],
// ... other metadata fields
},
type: 'oidc', // or 'oauth'
};
The metadata stays in wire format (snake_case) end-to-end: it is validated as-is and passed verbatim to the MCP SDK's metadata helpers. The jwks_uri field is required by MCP Auth for JWT access token verification.
Step 2: Declare the protected resource metadata
Initialize MCPAuth with your resource identifier and the authorization server config from Step 1:
import { MCPAuth } from 'mcp-auth';
const mcpAuth = new MCPAuth({
protectedResourceMetadata: {
// The resource identifier of this MCP server (RFC 8707)
resource: 'https://api.example.com/notes',
// The authorization server config from Step 1
authorizationServer: authServerConfig,
// The scopes this MCP server understands, advertised as `scopes_supported`
scopesSupported: ['read:notes', 'write:notes'],
// Optional: a human-readable name, advertised as `resource_name`
resourceName: 'Notes API',
// Optional: a documentation URL, advertised as `resource_documentation`
serviceDocumentationUrl: 'https://docs.example.com',
},
});
A few things to know:
- The
resourceidentifier must be an HTTP(S) URL without a fragment component. It is published as theresourcevalue of the Protected Resource Metadata document and used as the expectedaud(audience) claim of access tokens. The MCP specification requires access tokens to be bound to the resource they are issued for (RFC 8707), so audience validation is always on and cannot be disabled. - The configuration is validated in the constructor so misconfigurations fail fast. For a resolved authorization server config, the metadata is validated immediately; for a discovery config, it is validated when first fetched.
- If your deployment serves multiple resources, create one
MCPAuthinstance per resource.
For more advanced metadata parameters, see RFC 9728.
Step 3: Serve the OAuth discovery documents
mcpAuth.getAuthMetadataOptions() returns the MCP SDK's AuthMetadataOptions, ready to feed to the SDK's metadata helpers. They serve both discovery documents:
- Protected Resource Metadata (RFC 9728), at the path derived from your resource identifier:
- No path:
https://api.example.com→/.well-known/oauth-protected-resource - With path:
https://api.example.com/notes→/.well-known/oauth-protected-resource/notes
- No path:
- Authorization Server Metadata (RFC 8414), mirrored at
/.well-known/oauth-authorization-server(and the OpenID Connect variant) for MCP clients that look it up on your MCP server.
Serving them is one call into the SDK helper for your runtime:
// Fetch-native (Cloudflare Workers, Deno, Bun, Node.js): inside your fetch handler
const metadata = oauthMetadataResponse(request, await mcpAuth.getAuthMetadataOptions());
// Express: mount the router once
app.use(mcpAuthMetadataRouter(await mcpAuth.getAuthMetadataOptions()));
See Get started for the complete wiring. With the metadata endpoints in place, the next step is to protect your MCP endpoint with Bearer auth: check Configure Bearer auth.