AccessToken, and the right way to do that depends entirely on what kind of token your identity provider issues. FastMCP ships three verifiers covering the cases you actually meet in practice, and each is a TokenVerifier you pass to the server’s auth option. They differ in where the trust comes from: a JWT verifier trusts a cryptographic signature it can check locally, an introspection verifier trusts the issuing server’s live answer, and a static verifier trusts a map you wrote by hand. Picking the right one is mostly a question of which of those you have.
jwtVerifier
Reach forjwtVerifier first. It is the production default because JSON Web Tokens carry their own proof: the token is signed by your identity provider, the provider publishes its public keys at a JWKS endpoint, and FastMCP validates the signature locally on every request. There is no network call on the hot path — the keys are fetched once and cached — so verification stays fast even under load, and the server keeps working through brief provider outages.
A signature only proves the token is authentic, not that it was meant for you. That is why you give the verifier an issuer and an audience: the issuer check rejects tokens minted by anyone other than your provider, and the audience check rejects tokens issued for some other service that happen to share the same signing keys. Set both in production. Once a token passes, its sub claim becomes the AccessToken.clientId, its scope claim (whether a space-delimited string or an array) becomes scopes, its exp becomes expiresAt, and the entire payload is preserved under claims for any custom fields you rely on.
server.ts
leeway, in seconds, tolerates that skew; it defaults to 0, so set a few seconds if you see spurious expiry rejections.
introspectionVerifier
Some providers issue opaque tokens — random strings that carry no payload and no signature you can check. There is nothing to validate locally, so you ask the provider whether the token is still good, using the introspection endpoint defined by RFC 7662. FastMCPPOSTs the token to that endpoint with your client credentials, and trusts the active: true response.
The reason you would choose this over JWT verification is the same reason it costs more: introspection asks the authorization server in real time, so a token revoked seconds ago stops working immediately. JWTs, being self-contained, remain valid until they expire even if revoked at the source. If immediate revocation matters to your threat model, introspection buys it. The price is a network call per request, which is why the verifier accepts a cacheTtl (in seconds) to cache the introspection result for a short window — a deliberate trade of some revocation latency back for throughput.
server.ts
client_id to clientId, the space-delimited scope to scopes, exp to expiresAt, and the whole response body to claims. An inactive token, or any non-200 from the endpoint, is rejected.
Static tokens
When there is no identity provider in the loop — a local development server, a script, an internal tool behind its own network boundary — a full verifier is more machinery than the job needs.staticTokenVerifier takes a plain map from token strings to the identity each one represents, so you can hand a teammate a known token and have it resolve to a known clientId and scope set with no infrastructure at all.
server.ts
debugTokenVerifier accepts any non-empty bearer token and resolves it to an empty-scoped identity. It logs a warning on construction precisely because it grants access to anyone, so confine it to development. Both static verifiers honor an optional expiresAt on an entry and reject tokens that have passed it.
These verifiers earn their keep when the alternative is disabling auth entirely during development. Keeping a real verifier in place — even a trivial one — means your handlers see a populated ctx.auth and your authorization checks run, so the code path you test locally matches the one you ship.
Combining verifiers
A server in transition often needs to accept more than one kind of token at once — long-lived static tokens for internal callers alongside JWTs from your identity provider, or two providers during a migration.multiAuth composes several verifiers into one. It tries each in turn and returns the first AccessToken any of them produces; only if every verifier rejects the token does the request fail.
server.ts
AccessToken shape, the rest of your server — context, scope checks, per-component rules — never needs to know which one matched.
Authorization server discovery
A verifier checks tokens, but a new MCP client has no token. It must first find which authorization server to use. RFC 9728 defines the answer: the server publishes protected resource metadata, a small JSON document that names the authorization servers it trusts. When you verify tokens from an external authorization server such as Auth0, Keycloak, or Okta, wrap your verifier withremoteAuth to publish this document.
remoteAuth takes the verifier, the issuer URLs of your authorization servers, and the baseUrl of your server. It returns a verifier that behaves like the one you passed in, so you give it to the auth option as before. Over HTTP, the server then serves the metadata document at /.well-known/oauth-protected-resource followed by the MCP path. For the default /mcp path, that is /.well-known/oauth-protected-resource/mcp. The document needs no token. Every 401 response also carries a WWW-Authenticate: Bearer challenge. The challenge names the document in its resource_metadata parameter. If you set scopesSupported, the challenge also lists them in its scope parameter.
server.ts
baseUrl to the public URL that clients use, without the MCP path. FastMCP joins baseUrl and the MCP path to make the resource value in the document. If your server runs behind a reverse proxy, use the address of the proxy, not the address that the server binds to. The proxy must also forward the well-known path to the server unchanged. If baseUrl has a path, the well-known path includes it and starts at the root of the origin. For example, with baseUrl: 'https://example.com/api', the document is at https://example.com/.well-known/oauth-protected-resource/api/mcp. A proxy that forwards only /api/ does not forward this path. A client that cannot reach the document cannot find the authorization server.
remoteAuth also accepts a RequestVerifier. The optional resourceName and resourceDocumentation fields add a display name and a documentation link to the document. remoteAuth throws at startup if baseUrl or an authorization server is not a valid http or https URL. It also throws if authorizationServers is empty.
remoteAuth is for servers that verify tokens from another service. If you also set the oauth option, oauth takes precedence and remoteAuth publishes nothing.
Header-established identity
TokenVerifier covers callers that present a bearer token. When a trusted reverse proxy authenticates the caller and forwards the identity as request headers instead, use a RequestVerifier.