servers takes its own authProvider and headers, and MCPAdapter hands the provider object to the MCP SDK transport as-is and sends the headers with each request. Stdio servers take neither. Use a header or token provider for credentials your app manages, or an OAuth client provider for OAuth 2.1. When each run should reach the server as its caller, use per-user authentication.
This page requires
@langchain/mcp-adapters 2.0 or later. If you are upgrading from 1.x, see the migration guide.Bearer token
For a fixed API key, send a staticAuthorization header. For a token your app issues or rotates, pass an AuthProvider ({ token, onUnauthorized? }) as authProvider.
The example uses an application-provided tokenStore. Implement current() to read the token and refresh() to save a newer token and report whether it succeeded.
token(): Runs before every request and returns the current token.onUnauthorized(): Optional. Runs on a 401, before the request is retried once. Over SSE, the SDK calls it again every time a reconnect is rejected, with no cap. ThrowUnauthorizedErrorwhen you have no newer token.
onUnauthorized, a 401 fails the connection with the SDK’s UnauthorizedError. See Handle authentication errors.
Each named server opens its own connection, so servers in one adapter authenticate independently, as the two servers in this example do.
The adapter re-exports the AuthProvider and OAuthClientProvider types, so you can type a provider without importing the SDK.
OAuth authentication
OAuth lets users authorize your application to access an MCP server. Pass anOAuthClientProvider as authProvider. The SDK handles discovery, client registration, the code exchange, and token refresh. Your application supplies the provider’s storage, browser redirect, and callback handler.
Add @modelcontextprotocol/client 2.2.0 or later as a direct dependency. The adapter depends on the same range, so your app and the adapter share one SDK copy. The callback transport, SdkHttpError, and SseError come from it. Implement your provider using the SDK’s OAuth client guide. Configure its redirectUrl for your callback route, and store client information, tokens, the PKCE verifier, and discovery state. Include invalidateCredentials() so rejected refresh credentials can trigger a new login.
Keep each provider and its storage separate for each user and MCP server. The example below also binds each pending login to an authenticated application session. Read the user and session IDs from your application’s session middleware, never from callback query parameters.
The createMcpSession() helper below connects these steps:
- Call
listTools()to discover tools. If login is required, redirect the user’s browser to the returnedauthorizationUrl. - In your callback route, call
completeLogin()on the same session. It checks the caller and consumes the pendingstatebefore exchanging the code. - Use the returned tools. After the exchange, the helper retries discovery with the saved tokens.
Session helper
Session helper
This example keeps pending login state in one Node.js process. For multiple processes or restarts, use shared storage with an expiry. Check the state, user, session, and server, and consume the record in one atomic operation. The SDK does not validate
state.crm available to that application’s session until the callback completes. Do not share its provider with another session.
Complete the redirect
After the user signs in, the authorization server redirects to your provider’sredirectUrl. In that route, retrieve the same crm instance and the caller’s verified user and session IDs, then complete login:
finishAuth() so the SDK can validate the authorization server’s iss parameter. It rejects missing, mismatched, or already consumed state before the exchange. Keep the adapter open while using its tools, then call await crm.close() when the application session ends.
For a server configured with transport: "sse", use SSEClientTransport in the callback helper. For machine-to-machine access without a user, use the SDK’s ClientCredentialsProvider instead of a browser login.
Handle authentication errors
A connection rejected for credentials throws anMCPClientError. The SDK error sits one level down its cause chain, or two when an HTTP connection falls back to SSE. This example walks a few levels and handles HTTP authentication failures with either of these causes:
UnauthorizedError: The SDK cannot recover on its own. An OAuth login is needed, or a token provider has noonUnauthorized.- An HTTP 401 (an
SdkHttpErrorwithstatus: 401, or anSseErrorwithcode: 401over SSE): The server rejects the credentials, or none were provided.
UnauthorizedError. Import SdkHttpError and SseError from @modelcontextprotocol/client. An MCPClientError message includes the SDK’s error text, so log it rather than show it to users.
When a server rejects the credentials during a tool call, the tool fails with a ToolException instead. Under createAgent, the model sees it as an error message and the run continues.
Discovery retries failures caused by UnauthorizedError or HTTP 401 on the next call, even when onConnectionError skips failed servers. A later login can recover without recreating the adapter. Other errors from token() or onUnauthorized(), such as a failed token-store request, do not count as authentication failures. When onConnectionError skips a connection after one of these errors, that connection stays blocked until close() clears it.
Header precedence
Once a provider has a token, it replaces a configuredAuthorization header. Until then, the header is sent, so a static API key can fall back to OAuth.
The SDK also forwards a static Authorization header to the authorization server’s discovery, registration, and token endpoints, not only to the MCP server. Do not pair a secret API key with an OAuth provider whose authorization server lives on another origin.
Per-user authentication
In a deployment, each run should reach the MCP server as the user who started it, not with one shared credential. PassauthProvider or headers in the options of listTools(), listToolsets(), getClient(), or the resource methods (listResources(), listResourceTemplates(), and readResource()):
tokenFor(userId) to retrieve a token for the authenticated user.
The returned tools call the server through that user’s connection. These rules apply to method-level authentication:
- Adapter-wide override: An
authProviderorheadersin a method’s options applies to every HTTP and SSE server the adapter holds, not only the one you name. Use a single-server adapter when different users need different credentials. - Header precedence: Server-configured headers take precedence over same-named method-level headers, regardless of capitalization. When passing a per-user
Authorizationheader, omit that header from the server configuration. A provider token still takes precedence over either header. - One connection per provider object: Each distinct provider object gets its own connection, kept until
close(). Reuse one provider object per user rather than creating one per call. - Isolated catalogs: Catalogs are keyed by provider object and headers, so one user never receives another user’s cached tool list. Recreate the adapter when the account behind the same provider object changes.
See also
- Migrate to
@langchain/mcp-adapters2.0 - MCP authorization specification
- MCP TypeScript SDK OAuth client guide
- Custom authentication for a LangGraph server
Connect these docs to your agent of choice via MCP for real-time answers.

