UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

128 lines (127 loc) 5.22 kB
import { c as isRecord, s as filterStringRecord } from "./record-coerce-DItp3I4t.js"; import { t as createLazyRuntimeMethod } from "./lazy-runtime-CgCh8H_K.js"; import { p as operatorMcpOAuthIdentity } from "./mcp-oauth-store-rMpCI-eU.js"; import { d as buildMcpHttpFetch, f as withSameOriginMcpHttpHeaders, l as resolveMcpOAuthAccessToken, p as withoutMcpAuthorizationHeader } from "./mcp-oauth-bxRFfdN1.js"; import { n as resolveMcpTransportConfig } from "./mcp-transport-config-mph-T0Ys.js"; import crypto from "node:crypto"; //#region src/agents/mcp-auth-profile.ts /** * Auth-profile backed bearer injection for remote MCP servers. */ /** Returns the refresh-capable auth profile selected for one MCP server. */ function resolveMcpAuthProfileId(rawServer) { if (!isRecord(rawServer) || rawServer.auth !== "oauth" || !isRecord(rawServer.oauth)) return; const authProfileId = rawServer.oauth.authProfileId; return typeof authProfileId === "string" && authProfileId.trim().length > 0 ? authProfileId.trim() : void 0; } /** Returns whether a server needs an OpenClaw-managed bearer projected externally. */ function requiresMcpBearerProjection(rawServer) { if (!isRecord(rawServer) || rawServer.auth !== "oauth") return false; return Boolean(resolveMcpAuthProfileId(rawServer) || typeof rawServer.url === "string"); } const resolveMcpAuthProfileBearerToken = createLazyRuntimeMethod(() => import("./agents/mcp-auth-profile.runtime.js"), (runtime) => runtime.resolveMcpAuthProfileBearerToken); async function resolveMcpBearerToken(params) { const authProfileId = resolveMcpAuthProfileId(params.server); if (authProfileId) return await resolveMcpAuthProfileBearerToken({ serverName: params.serverName, profileId: authProfileId, cfg: params.cfg, agentDir: params.agentDir }); if (params.server.auth !== "oauth") return; const resolved = resolveMcpTransportConfig(params.serverName, params.server); if (!resolved || resolved.kind !== "http") return; const fetchFn = withSameOriginMcpHttpHeaders({ fetchFn: buildMcpHttpFetch({ sslVerify: resolved.sslVerify, clientCert: resolved.clientCert, clientKey: resolved.clientKey, resourceUrl: resolved.url, timeoutMs: resolved.requestTimeoutMs }), headers: withoutMcpAuthorizationHeader(resolved.headers), resourceUrl: resolved.url }); return await resolveMcpOAuthAccessToken({ identity: operatorMcpOAuthIdentity(params.serverName, resolved.url), config: resolved.oauth, fetchFn }); } /** Wraps HTTP MCP fetch with same-origin, refreshed bearer injection. */ function withMcpAuthProfileBearer(params) { const resourceOrigin = new URL(params.resourceUrl).origin; const configuredHeaders = withoutMcpAuthorizationHeader(params.headers); return async (url, init) => { if (new URL(url).origin !== resourceOrigin) return params.fetchFn(url, init); const headers = new Headers(configuredHeaders); for (const [key, value] of new Headers(init?.headers)) if (key.toLowerCase() !== "authorization") headers.set(key, value); const token = await resolveMcpAuthProfileBearerToken({ serverName: params.serverName, profileId: params.authProfileId, cfg: params.cfg, agentDir: params.agentDir }); headers.set("authorization", `Bearer ${token}`); return params.fetchFn(url, { ...init, headers }); }; } function buildTokenEnvVarName(serverName) { return `OPENCLAW_MCP_AUTH_${crypto.createHash("sha256").update(serverName).digest("hex").slice(0, 12).toUpperCase()}_TOKEN`; } function stripOpenClawOnlyOAuthConfig(server) { const next = { ...server }; delete next.auth; delete next.oauth; return next; } /** Resolves OAuth-backed MCP servers into bearer headers for external runtimes. */ async function resolveMcpBearerBundleConfig(params) { let nextServers; let nextEnv = params.env; const tokenProjection = params.tokenProjection ?? "env"; for (const [serverName, server] of Object.entries(params.config.mcpServers)) { let token; try { token = await resolveMcpBearerToken({ serverName, server, cfg: params.cfg, agentDir: params.agentDir }); } catch (error) { if (!params.omitUnavailableOAuthServers || !requiresMcpBearerProjection(server)) throw error; nextServers ??= { ...params.config.mcpServers }; delete nextServers[serverName]; params.onServerUnavailable?.(serverName, error); continue; } if (!token) continue; let authorization; if (tokenProjection === "literal") authorization = `Bearer ${token}`; else { const envVar = buildTokenEnvVarName(serverName); if (!nextEnv || nextEnv === params.env) nextEnv = { ...params.env }; nextEnv[envVar] = token; authorization = `Bearer \${${envVar}}`; } const headers = withoutMcpAuthorizationHeader(filterStringRecord(server.headers)); nextServers ??= { ...params.config.mcpServers }; nextServers[serverName] = stripOpenClawOnlyOAuthConfig({ ...server, headers: { ...headers, Authorization: authorization } }); } return { config: nextServers ? { mcpServers: nextServers } : params.config, env: nextEnv }; } //#endregion export { withMcpAuthProfileBearer as i, resolveMcpAuthProfileId as n, resolveMcpBearerBundleConfig as r, requiresMcpBearerProjection as t };