/** * Install-level client config loading and InspectorClient auth option wiring * for Node runners (TUI, CLI). */ import type { SecretStore } from "../auth/node/secret-store.js"; import { defaultSecretStore } from "../auth/node/secret-store-selection.js"; import type { InspectorClientOptions, InspectorServerSettings, } from "../mcp/types.js"; import { oauthAuthorizationParamsFromSettings, oauthEndpointOverridesFromSettings, } from "../mcp/serverList.js"; import { loadClientConfig } from "./config.js"; import type { ClientConfig } from "./types.js"; import { getActiveCimdClientMetadataUrl, getActiveEnterpriseManagedAuthIdp, } from "./types.js"; export interface LoadRunnerClientConfigOptions { /** Explicit path from `--client-config` (or MCP_CLIENT_CONFIG_PATH when unset). */ clientConfigPath?: string; /** Secret store for the IdP clientSecret; defaults to the store selected for * this host (OS keychain, or its file/memory fallback). Tests inject an * in-memory store for determinism. */ secretStore?: SecretStore; } /** Load install-level client.json with keychain-backed IdP secrets. */ export async function loadRunnerClientConfig( options?: LoadRunnerClientConfigOptions, ): Promise { const customPath = options?.clientConfigPath?.trim() || process.env.MCP_CLIENT_CONFIG_PATH?.trim() || undefined; const secretStore = options?.secretStore ?? defaultSecretStore(); return loadClientConfig({ filePath: customPath, secretStore }); } export interface RunnerClientConfigOverrides { clientId?: string; clientSecret?: string; clientMetadataUrl?: string; } /** HTTP transports (SSE, streamable-http) can use OAuth. */ export function isOAuthCapableServerConfig( config: { type?: string } | null | undefined, ): boolean { if (!config) return false; return config.type === "sse" || config.type === "streamable-http"; } /** * Derive OAuth / EMA / CIMD InspectorClient options from install client.json, * per-server settings, and CLI flag overrides (flags win over client.json). */ export function buildRunnerClientAuthOptions( clientConfig: ClientConfig, savedSettings?: InspectorServerSettings, cliOverrides?: RunnerClientConfigOverrides, ): Pick< InspectorClientOptions, | "oauth" | "enterpriseManagedAuth" | "installEnterpriseManagedAuth" | "directAuthRecovery" > { const activeIdp = getActiveEnterpriseManagedAuthIdp(clientConfig); const activeCimdUrl = getActiveCimdClientMetadataUrl(clientConfig); const serverAuthorizationParams = savedSettings ? oauthAuthorizationParamsFromSettings(savedSettings) : undefined; const serverEndpointOverrides = savedSettings ? oauthEndpointOverridesFromSettings(savedSettings) : undefined; const oauthFromServer = savedSettings && (savedSettings.oauthClientId || savedSettings.oauthClientSecret || savedSettings.oauthScopes || serverAuthorizationParams || serverEndpointOverrides || savedSettings.enterpriseManaged || savedSettings.oauthRequestRefreshToken === false) ? { ...(savedSettings.oauthClientId && { clientId: savedSettings.oauthClientId, }), ...(savedSettings.oauthClientSecret && { clientSecret: savedSettings.oauthClientSecret, }), ...(savedSettings.oauthScopes && { scope: savedSettings.oauthScopes, }), ...(serverAuthorizationParams && { authorizationParams: serverAuthorizationParams, }), ...serverEndpointOverrides, ...(savedSettings.enterpriseManaged && { enterpriseManaged: true, }), // #2068: only the explicit opt-out is forwarded; omitting the key // leaves the provider's default (declare `refresh_token`) in place. ...(savedSettings.oauthRequestRefreshToken === false && { requestRefreshToken: false, }), } : undefined; const clientMetadataUrl = cliOverrides?.clientMetadataUrl?.trim() || activeCimdUrl; const oauthFromCli = cliOverrides?.clientId || cliOverrides?.clientSecret || clientMetadataUrl ? { ...(cliOverrides?.clientId && { clientId: cliOverrides.clientId }), ...(cliOverrides?.clientSecret && { clientSecret: cliOverrides.clientSecret, }), ...(clientMetadataUrl && { clientMetadataUrl }), } : undefined; const oauth = oauthFromServer || oauthFromCli ? { ...(oauthFromServer ?? {}), ...(oauthFromCli ?? {}), } : undefined; return { ...(oauth && { oauth }), ...(activeIdp && { enterpriseManagedAuth: { idp: activeIdp }, }), ...(clientConfig.enterpriseManagedAuth && { installEnterpriseManagedAuth: clientConfig.enterpriseManagedAuth, }), ...(oauth && { directAuthRecovery: true }), }; }