/** * OAuth 2.1 client provider for `@revealui/mcp`. * * Phase: Stage 2 PR-2.1 of the MCP v1 plan (see * `.jv/docs/mcp-productionization-scope.md`). * * Implements the SDK's `OAuthClientProvider` interface, backed by a pluggable * `Vault` for durable credential storage. The default implementation shells * out to the `revvault` CLI; a memory-backed implementation is exported for * tests and for environments without revvault on the host. * * Storage layout per `(tenant, server)` pair, rooted at `mcp///`: * tokens — OAuthTokens JSON (access + refresh + expiry + token_type + scope) * client — DCR client info JSON (client_id + optional client_secret) * verifier — PKCE code verifier, opaque string, single-use during redirect * discovery — cached authorization-server metadata (optional) * * Flow (first-time authorization): * 1. Caller constructs a provider with `{ tenant, server, vault, redirectUrl, * clientMetadata, onRedirect }` and attaches it to a Streamable HTTP * transport via `authProvider` on `StreamableHttpTransportOptions`. * 2. On `connect()`, the SDK transport detects missing tokens, runs OAuth * discovery, performs Dynamic Client Registration (RFC 7591) if * `saveClientInformation` is implemented, generates a PKCE verifier, * persists it, and calls `redirectToAuthorization(url)`. * 3. This provider's `redirectToAuthorization` records the URL on * `lastAuthorizationUrl` and invokes the caller's `onRedirect` hook. The * caller navigates the user agent (e.g. a Next.js route handler issuing * an HTTP 302). * 4. After the user authorizes and returns to the callback URL, the caller * extracts the `code` query parameter and invokes `mcpClient.finishAuth(code)`, * which delegates to the transport's `finishAuth(code)` — this exchanges * the code via PKCE and persists tokens via `saveTokens`. * 5. Subsequent `connect()` calls use the stored tokens. Token refresh is * automatic: the SDK detects expiry, calls the refresh endpoint, and * calls `saveTokens` with the new token set. OAuth 2.1 §4.12 refresh-token * rotation is transparent — the rotated refresh_token flows through * `saveTokens` verbatim. */ import type { OAuthClientProvider } from '@modelcontextprotocol/sdk/client/auth.js'; import type { OAuthClientInformation, OAuthClientInformationFull, OAuthClientInformationMixed, OAuthClientMetadata, OAuthTokens } from '@modelcontextprotocol/sdk/shared/auth.js'; /** * Minimal key/value interface the provider needs from its backing store. * * Keys are revvault-style slash-delimited paths (e.g. `mcp/acme/linear/tokens`). * Values are opaque strings; the provider JSON-encodes structured values * before calling `set`. */ export interface Vault { /** Returns the value at `path`, or `undefined` if not present. */ get(path: string): Promise; /** Stores `value` at `path`, overwriting any prior value. */ set(path: string, value: string): Promise; /** Deletes the value at `path`. No-op if not present. */ delete(path: string): Promise; /** * Lists every path that starts with `prefix`. Returns an empty array when * no matches exist. The return order is implementation-defined. * * Used by admin catalog tooling to enumerate configured servers without * requiring an out-of-band registry. */ list(prefix: string): Promise; } export interface RevvaultVaultOptions { /** Path to the `revvault` binary. Defaults to `~/.local/bin/revvault`. */ binPath?: string; /** * Age identity path. Passed via `REVVAULT_IDENTITY` env var. Defaults to * `~/.config/age/keys.txt`, matching revvault's own default. */ identityPath?: string; /** Spawn timeout in ms. Defaults to 10_000. */ timeoutMs?: number; } /** * Creates a {@link Vault} backed by the `revvault` CLI. Requires `revvault` to * be installed on the host and the age identity to be readable. * * Production default for RevealUI deployments. In CI or test environments * without revvault, prefer {@link createMemoryVault}. */ export declare function createRevvaultVault(options?: RevvaultVaultOptions): Vault; export declare class RevvaultError extends Error { constructor(message: string); } /** * Creates an in-memory {@link Vault} backed by a `Map`. Intended for tests and * for short-lived processes where persistence is not required. * * An optional `seed` object prepopulates the map. */ export declare function createMemoryVault(seed?: Record): Vault; /** * Canonical revvault path layout for MCP OAuth credentials. Exported so admin * tooling and audits can introspect the layout without reimplementing it. */ export interface McpOAuthPaths { tokens: string; client: string; verifier: string; discovery: string; } /** Build the {@link McpOAuthPaths} for a given `(tenant, server)` pair. */ export declare function mcpOAuthPaths(tenant: string, server: string): McpOAuthPaths; export interface McpOAuthProviderOptions { /** Tenant identifier. Used as the first path segment in revvault. */ tenant: string; /** Server identifier (e.g. `linear`, `notion`). Second path segment. */ server: string; /** Backing vault. Use {@link createRevvaultVault} in production. */ vault: Vault; /** * URL the authorization server should redirect to after user consent. * This is the public URL of the caller's callback handler. */ redirectUrl: string | URL; /** * OAuth client metadata used for Dynamic Client Registration and advertised * to the authorization server. Must include `redirect_uris` covering * `redirectUrl`. */ clientMetadata: OAuthClientMetadata; /** * Called when the SDK's `auth()` needs to send the user to the authorization * URL. The caller is responsible for navigating the user agent (e.g. by * issuing an HTTP 302 from a Next.js route handler). The provider also * records the URL on {@link RevvaultOAuthProvider.lastAuthorizationUrl} for * callers that prefer synchronous inspection over a callback. */ onRedirect?(authorizationUrl: URL): void | Promise; /** * Generates an opaque OAuth state parameter. Defaults to a `crypto.randomUUID()`. * Callers that want to bind state to session identifiers can override. */ state?(): string | Promise; } /** * SDK-compatible OAuth client provider backed by a {@link Vault}. Instances are * scoped to a single `(tenant, server)` pair — construct one per MCP server * the caller intends to authorize against. */ export declare class McpOAuthProvider implements OAuthClientProvider { private readonly vault; private readonly paths; private readonly _redirectUrl; private readonly _clientMetadata; private readonly onRedirect?; private readonly _state?; /** * The most recent authorization URL the SDK asked us to send the user to. * Populated by {@link redirectToAuthorization}. Callers may read this * synchronously after an `auth()` or `connect()` call that returns * `'REDIRECT'`. */ lastAuthorizationUrl?: URL; constructor(options: McpOAuthProviderOptions); get redirectUrl(): string | URL; get clientMetadata(): OAuthClientMetadata; state(): string | Promise; clientInformation(): Promise; saveClientInformation(info: OAuthClientInformationMixed): Promise; tokens(): Promise; saveTokens(tokens: OAuthTokens): Promise; saveCodeVerifier(codeVerifier: string): Promise; codeVerifier(): Promise; redirectToAuthorization(authorizationUrl: URL): Promise; invalidateCredentials(scope: 'all' | 'client' | 'tokens' | 'verifier' | 'discovery'): Promise; } export type { OAuthClientInformation, OAuthClientInformationFull, OAuthClientMetadata, OAuthTokens, }; //# sourceMappingURL=oauth.d.ts.map