vercel/ai · error · MCPClientOAuthError
Stored OAuth authorization server metadata is required when
Error message
Stored OAuth authorization server metadata is required when exchanging an authorization code
What it means
During the authorization-code callback, the client must pin the authorization server it originally talked to. It reads the stored authorization server metadata (via getStoredAuthorizationServerInformation, typically attached to the saved client information). If none is stored, the issuer of the callback cannot be verified and the code exchange is refused with an MCPClientOAuthError.
Source
Thrown at packages/mcp/src/tool/oauth.ts:1362
/** On callback, validate state and AS pin before code exchange */
if (authorizationCode !== undefined) {
if (provider.storedState) {
const expectedState = await provider.storedState();
if (expectedState !== undefined && expectedState !== callbackState) {
throw new Error(
'OAuth state parameter mismatch - possible CSRF attack',
);
}
}
const storedAuthorizationServerInformation =
await getStoredAuthorizationServerInformation({
provider,
clientInformation,
});
if (!storedAuthorizationServerInformation) {
throw new MCPClientOAuthError({
message:
'Stored OAuth authorization server metadata is required when exchanging an authorization code',
});
}
validateAuthorizationResponseIssuer({
callbackIssuer,
expectedIssuer:
storedAuthorizationServerInformation.issuer ??
metadata?.issuer ??
String(authorizationServerUrl),
});
assertAuthorizationServerInformationMatches({
storedAuthorizationServerInformation,
currentAuthorizationServerInformation,
});
const codeVerifier = await provider.codeVerifier();
const tokens = await exchangeAuthorization(authorizationServerUrl, {View on GitHub (pinned to 69428b1f8b)
Solutions
- Clear the stored client information and restart the OAuth flow so the library re-registers the client and saves metadata-pinned client information.
- If you implement saveClientInformation yourself, persist exactly the object given (including any authorizationServerInformation fields), not a subset.
- Upgrade @ai-sdk/mcp and any custom provider code together so the same client-information shape is written and read.
Example fix
// before
saveClientInformation: async (info) => {
await store.set('client', { client_id: info.client_id });
}
// after
saveClientInformation: async (info) => {
await store.set('client', info); // keep full object incl. pinned AS metadata
} Defensive patterns
Strategy: validation
Validate before calling
const clientInfo = await provider.clientInformation?.();
if (clientCodeExchangeRequested && clientInfo && !('authorizationServerInformation' in (clientInfo as object))) {
// stored client predates AS pinning: clear it and restart the flow
await provider.clearClientInformation?.();
} Try / catch
try {
await auth(serverUrl, { ...provider, authorizationCode });
} catch (e) {
if (e instanceof MCPClientOAuthError && e.message.includes('Stored OAuth authorization server metadata is required')) {
// wipe stale client info and restart authorization from scratch
await provider.clearClientInformation?.();
}
throw e;
} Prevention
- Persist client information objects verbatim (never strip fields)
- Keep @ai-sdk/mcp versions consistent between the flow start and callback
- Clear old-format credentials when upgrading the SDK
- Test the full redirect round-trip against durable storage
When it happens
Trigger: auth() is called with an authorizationCode while getStoredAuthorizationServerInformation({provider, clientInformation}) returns undefined — e.g. client information was saved by an older SDK version or a custom saveClientInformation that strips the pinned AS metadata, or registration happened outside this library.
Common situations: Upgrading from a version of @ai-sdk/mcp that did not pin authorization-server metadata onto client information; hand-rolled client registration storing raw RFC 7591 responses; wiping stored credentials between the redirect and the callback.
Related errors
- OAuth protected resource metadata URL ${resourceMetadataUrl.
- Incompatible OIDC provider at ${endpointUrl}: does not suppo
- Incompatible auth server: does not support response type ${r
- Incompatible auth server: does not support code challenge me
- Unsupported client authentication method: ${method}
AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30).
Data as JSON: /api/errors/41d80016e0f3cfb9.
Report an issue: GitHub.