How Continue's Authentication and OAuth Work: MCP Integration Explained

Continue.dev implements Model-Context-Protocol (MCP) OAuth to authenticate against remote MCP servers, wrapping the standard OAuth 2.0 authorization-code grant with PKCE-style state handling in the MCPConnectionOauthProvider class to support VS Code, JetBrains, and other IDE environments.

Continue.dev enables secure connections to remote Model-Context-Protocol (MCP) servers through a comprehensive OAuth 2.0 implementation. The authentication system, centered in core/context/mcp/MCPOauth.ts, coordinates redirect URL handling, state management, and token persistence across different IDE platforms. This guide examines the exact mechanisms powering Continue's authentication and OAuth flow, from initial redirect to token storage.

Redirect URL Handling

The MCPConnectionOauthProvider class dynamically determines the appropriate callback URL based on IDE capabilities. This ensures compatibility with both local development environments and cloud-based IDEs like VS Code for Web.

// core/context/mcp/MCPOauth.ts#L86-L99
private async _initializeRedirectUrl(): Promise<void> {
  if (this.ide.getExternalUri) {
    const localUri = `http://localhost:${PORT}`;
    const externalUri = await this.ide.getExternalUri(localUri);
    this._redirectUrl = externalUri;          // IDE‑provided public URL
  }
  // otherwise keep default localhost URL
}

The constructor initializes a default http://localhost:3000 endpoint before asynchronously calling _initializeRedirectUrl(). When the IDE exposes getExternalUri, the system replaces the localhost address with a publicly reachable tunnel URL required for web-based authentication flows. The redirectUrl getter guarantees synchronous access to a valid URL even before async initialization completes.

Building OAuth Client Metadata

The provider constructs dynamic client metadata that incorporates PKCE-style state parameters to prevent CSRF attacks and support multiple simultaneous authentication flows.

// core/context/mcp/MCPOauth.ts#L19-L34
get clientMetadata() {
  const state = authenticatingContexts.get(this.oauthServerUrl)?.state;
  const redirectUri = state
    ? this.getRedirectUrlWithState(state)
    : this.redirectUrl;

  return {
    redirect_uris: [redirectUri],
    token_endpoint_auth_method: "none",
    grant_types: ["authorization_code", "refresh_token"],
    response_types: ["code"],
    client_name: "Continue Dev, Inc",
    client_uri: "https://continue.dev",
  };
}

When a state value exists in the authenticatingContexts map, the system appends it to the redirect URI as a query parameter. This metadata object is passed to the MCP SDK's auth helper, which registers or retrieves a temporary OAuth client on the target MCP server.

Initiating the Authorization Flow

The performAuth function orchestrates the complete login sequence, generating unique flow identifiers and mapping them to server URLs for later callback validation.

// core/context/mcp/MCPOauth.ts#L43-L56
export async function performAuth(serverId: string, url: string, ide: IDE) {
  const authProvider = new MCPConnectionOauthProvider(url, ide);
  await authProvider.ensureRedirectUrl();

  const state = uuidv4();                     // unique flow identifier
  authenticatingContexts.set(url, { serverId, ide, state });
  stateToServerUrl.set(state, url);           // map state → server

  return await auth(authProvider, { serverUrl: url });
}

This function performs four critical operations:

  • Instantiates MCPConnectionOauthProvider for the target server
  • Guarantees redirect URL readiness via ensureRedirectUrl()
  • Generates a UUID state and stores it in two maps: authenticatingContexts (holding IDE, server ID, and state) and stateToServerUrl (enabling state-to-URL resolution)
  • Invokes the SDK's auth function to open the provider's authorization endpoint in the user's browser

Local Callback Server and Token Exchange

When using localhost redirects, the system spawns a temporary HTTP server to capture the OAuth callback and exchange the authorization code for tokens.

// core/context/mcp/MCPOauth.ts#L33-L65
const createServerForOAuth = () =>
  http.createServer((req, res) => {
    const parsedUrl = url.parse(req.url!, true);
    const code = parsedUrl.query["code"] as string;
    const state = parsedUrl.query["state"] as string | undefined;
    void handleMCPOauthCode(code, state);
    // respond with a tiny “Authentication Complete” page
  });

The server listens on port 3000, extracts the code and optional state from query parameters, and delegates processing to handleMCPOauthCode. This handler validates the state against the stateToServerUrl map, falls back to the sole active context if state is missing (legacy browser support), and completes the token exchange:

// core/context/mcp/MCPOauth.ts#L20-L27 (inside handleMCPOauthCode)
const authProvider = new MCPConnectionOauthProvider(serverUrl, ide);
await authProvider.ensureRedirectUrl();
const authStatus = await auth(authProvider, {
  serverUrl,
  authorizationCode,
});
if (authStatus === "AUTHORIZED") {
  const { MCPManagerSingleton } = await import("./MCPManagerSingleton");
  await MCPManagerSingleton.getInstance().refreshConnection(serverId);
}

The auth helper from @modelcontextprotocol/sdk/client/auth performs the actual code-to-token exchange. Upon successful authorization, the system immediately refreshes the MCP connection through MCPManagerSingleton to activate the new credentials.

Token Storage and Retrieval

Continue persists OAuth credentials using the GlobalContext key-value store, enabling token reuse across IDE sessions without repeated logins.

// core/context/mcp/MCPOauth.ts#L36‑L55 (private helpers)
private _getOauthStorage<K extends MCPOauthStorageKey>(key: K) { … }
private _updateOauthStorage<K extends MCPOauthStorageKey>(key: K, value: MCPOauthStorage[K]) { … }
private _clearOauthStorage() { … }

The provider implements three storage operations:

  • Saving: saveClientInformation, saveTokens, and saveCodeVerifier persist data via _updateOauthStorage
  • Retrieval: clientInformation() and tokens() fetch and validate stored values using Zod schemas from @modelcontextprotocol/sdk/shared/auth
  • Convenience access: The getOauthToken function extracts the access token for immediate API usage
// core/context/mcp/MCPOauth.ts#L33-L37
export async function getOauthToken(mcpServerUrl: string, ide: IDE) {
  const authProvider = new MCPConnectionOauthProvider(mcpServerUrl, ide);
  const tokens = await authProvider.tokens();
  return tokens?.access_token;
}

Revoking Authentication

To force re-authentication or remove credentials, the system provides removeMCPAuth, which clears all persisted OAuth data for a specific server.

// core/context/mcp/MCPOauth.ts#L46-L49
export function removeMCPAuth(url: string, ide: IDE) {
  const authProvider = new MCPConnectionOauthProvider(url, ide);
  authProvider.clear();   // clears the persisted storage for that server
}

This operation wipes client information, access tokens, refresh tokens, and the code verifier from GlobalContext, ensuring the next request triggers a fresh OAuth flow.

Summary

  • Continue's authentication and OAuth implementation leverages the Model-Context-Protocol SDK to provide standardized OAuth 2.0 flows for MCP servers.
  • The MCPConnectionOauthProvider class in core/context/mcp/MCPOauth.ts handles dynamic redirect URL detection, supporting both localhost and IDE-provided tunnel URLs.
  • PKCE-style state management using authenticatingContexts and stateToServerUrl maps enables secure, concurrent authentication flows to multiple servers.
  • A temporary HTTP server on port 3000 captures authorization codes, exchanges them for tokens via the SDK's auth helper, and immediately refreshes the MCP connection.
  • All credentials persist in GlobalContext with Zod schema validation, exposing getOauthToken for retrieval and removeMCPAuth for credential revocation.

Frequently Asked Questions

How does Continue handle OAuth redirects in cloud-based IDEs like VS Code for Web?

Continue detects IDE capabilities through the getExternalUri method. When available (as in VS Code for Web), the system replaces the default localhost:3000 redirect with a publicly reachable tunnel URL provided by the IDE. This ensures OAuth callbacks can reach the authentication handler even when the IDE runs in a browser environment.

What happens if the OAuth state parameter is missing during the callback?

The handleMCPOauthCode function includes fallback logic for legacy browsers or providers that omit the state parameter. If state is undefined, the system checks authenticatingContexts for a single active flow. When exactly one authentication context exists, it proceeds with that context; otherwise, it logs an error and aborts the exchange.

Where are OAuth tokens stored in Continue, and are they secure?

Tokens persist in the GlobalContext key-value store, which uses the IDE's native storage mechanisms (encrypted where supported by the host application). The implementation uses Zod schemas from @modelcontextprotocol/sdk/shared/auth to validate token structure on retrieval, ensuring data integrity across sessions.

Can users revoke MCP server authentication without restarting Continue?

Yes. Calling removeMCPAuth(url, ide) immediately clears all cached OAuth data—including client information, access tokens, and refresh tokens—for the specified server URL. This forces a fresh authentication flow on the next request without requiring an IDE restart.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →