openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
202 lines (201 loc) • 12.1 kB
JavaScript
import { u as asPositiveFiniteNumber } from "./number-coercion-CLj0HTDM.js";
import { c as isRecord } from "./record-coerce-DItp3I4t.js";
import { l as normalizeOptionalString } from "./string-coerce-CIXf7egm.js";
import { n as parseBooleanValue, t as asBoolean } from "./boolean-DmBL0YJK.js";
import "./string-coerce-runtime-GQa0ehRA.js";
import { s as createMigrationManualItem } from "./migration-q6QzXKht.js";
import { u as sanitizeName } from "./helpers-DWNr52wp.js";
import { i as resolveMcpEnvReferences, n as mcpValueHasEnvReferences } from "./config-env-D-ZcknMk.js";
import "./config-provider-contract-DFe5VpP9.js";
//#region extensions/migrate-hermes/config-mcp.ts
const MCP_RESOURCE_UTILITY_TOOLS = ["resources_list", "resources_read"];
const MCP_PROMPT_UTILITY_TOOLS = ["prompts_list", "prompts_get"];
function readPositiveNumeric(value) {
if (typeof value === "number") return asPositiveFiniteNumber(value);
if (typeof value !== "string" || !value.trim()) return;
return asPositiveFiniteNumber(Number(value));
}
function readToolFilterList(value) {
if (typeof value === "string") return value.trim() ? [value.trim()] : [];
if (!Array.isArray(value) || !value.every((entry) => typeof entry === "string")) return;
return [...new Set(value.map((entry) => entry.trim()).filter(Boolean))];
}
function hasUnsupportedToolPattern(pattern) {
return pattern.includes("?") || pattern.includes("[");
}
function mapHermesToolFilter(value) {
const direct = isRecord(value.toolFilter) ? value.toolFilter : isRecord(value.tool_filter) ? value.tool_filter : void 0;
if (direct) {
const include = readToolFilterList(direct.include);
const exclude = readToolFilterList(direct.exclude);
if (include && include.length > 0) return { include };
return exclude !== void 0 && exclude.length > 0 ? { exclude } : void 0;
}
const tools = isRecord(value.tools) ? value.tools : void 0;
if (!tools) return;
const include = readToolFilterList(tools.include);
const exclude = readToolFilterList(tools.exclude);
const resourcesEnabled = parseBooleanValue(tools.resources) !== false;
const promptsEnabled = parseBooleanValue(tools.prompts) !== false;
if (include !== void 0) {
const allowed = [
...include.filter((pattern) => !hasUnsupportedToolPattern(pattern)),
...resourcesEnabled ? MCP_RESOURCE_UTILITY_TOOLS : [],
...promptsEnabled ? MCP_PROMPT_UTILITY_TOOLS : []
];
return allowed.length > 0 ? { include: allowed } : { exclude: ["*"] };
}
const translatedExclude = [
...exclude ?? [],
...!resourcesEnabled ? MCP_RESOURCE_UTILITY_TOOLS : [],
...!promptsEnabled ? MCP_PROMPT_UTILITY_TOOLS : []
];
return translatedExclude.length > 0 ? { exclude: translatedExclude } : void 0;
}
function mapHermesClientCertificate(value) {
const cert = value.clientCert ?? value.client_cert;
const key = normalizeOptionalString(value.clientKey) ?? normalizeOptionalString(value.client_key);
if (Array.isArray(cert) && cert.length === 2) {
const certPath = normalizeOptionalString(cert[0]);
const keyPath = normalizeOptionalString(cert[1]);
return certPath && keyPath ? {
clientCert: certPath,
clientKey: keyPath
} : {};
}
const certPath = normalizeOptionalString(cert);
return certPath && key ? {
clientCert: certPath,
clientKey: key
} : {};
}
const MCP_CONNECTION_FIELDS = [
"enabled",
"command",
"args",
"cwd",
"workingDirectory",
"url",
"connectionTimeoutMs",
"requestTimeoutMs"
];
function importsMcpSensitiveValues(value, includeSecrets) {
return includeSecrets && (value.env !== void 0 || value.headers !== void 0 || MCP_CONNECTION_FIELDS.some((key) => mcpValueHasEnvReferences(value[key])));
}
function mapHermesMcpOauth(value) {
const oauth = isRecord(value.oauth) ? value.oauth : void 0;
if (!oauth) return;
const mapped = {};
for (const key of [
"authProfileId",
"scope",
"redirectUrl",
"clientMetadataUrl"
]) {
const fieldValue = normalizeOptionalString(oauth[key]);
if (fieldValue) mapped[key] = fieldValue;
}
return Object.keys(mapped).length > 0 ? mapped : void 0;
}
function mapMcpServer(value, includeSecrets, env) {
const next = {};
for (const key of MCP_CONNECTION_FIELDS) {
const sourceValue = value[key];
if (sourceValue === void 0) continue;
if (!mcpValueHasEnvReferences(sourceValue)) {
next[key] = sourceValue;
continue;
}
if (includeSecrets) {
const resolved = resolveMcpEnvReferences(sourceValue, env);
if (!resolved.unresolved) next[key] = resolved.value;
}
}
const transport = normalizeOptionalString(value.transport) ?? normalizeOptionalString(value.type);
if (transport === "http" || transport === "streamable-http") next.transport = "streamable-http";
else if (transport === "sse" || transport === "stdio") next.transport = transport;
else if (!transport && normalizeOptionalString(next.url)) next.transport = "streamable-http";
const connectionTimeoutSeconds = value.connectTimeout ?? value.connect_timeout;
if (next.connectionTimeoutMs === void 0 && typeof connectionTimeoutSeconds === "number" && connectionTimeoutSeconds > 0 && Number.isFinite(connectionTimeoutSeconds * 1e3)) next.connectionTimeoutMs = connectionTimeoutSeconds * 1e3;
const requestTimeoutSeconds = value.timeout;
if (next.requestTimeoutMs === void 0 && typeof requestTimeoutSeconds === "number" && requestTimeoutSeconds > 0 && Number.isFinite(requestTimeoutSeconds * 1e3)) next.requestTimeoutMs = requestTimeoutSeconds * 1e3;
next.supportsParallelToolCalls = asBoolean(value.supportsParallelToolCalls ?? value.supports_parallel_tool_calls);
next.sslVerify = asBoolean(value.sslVerify ?? value.ssl_verify);
next.auth = normalizeOptionalString(value.auth) === "oauth" ? "oauth" : void 0;
next.oauth = mapHermesMcpOauth(value);
Object.assign(next, mapHermesClientCertificate(value));
next.toolFilter = mapHermesToolFilter(value);
const tools = isRecord(value.tools) ? value.tools : void 0;
if (tools && readToolFilterList(tools.include) === void 0 && readToolFilterList(tools.exclude)?.some(hasUnsupportedToolPattern)) next.enabled = false;
if (includeSecrets) {
for (const key of ["env", "headers"]) if (value[key] !== void 0) {
const resolved = resolveMcpEnvReferences(value[key], env);
if (!resolved.unresolved) next[key] = resolved.value;
}
}
const mapped = Object.fromEntries(Object.entries(next).filter(([, entry]) => entry !== void 0));
return normalizeOptionalString(mapped.command) || normalizeOptionalString(mapped.url) ? mapped : {};
}
function mcpManualItems(params) {
const { name, raw } = params;
const safeName = sanitizeName(name);
const items = [];
const add = (suffix, message, recommendation) => {
items.push(createMigrationManualItem({
id: `manual:mcp-server-${suffix}:${safeName}`,
source: params.source,
message,
recommendation
}));
};
const interpolatedValues = [
...MCP_CONNECTION_FIELDS.map((key) => raw[key]),
raw.env,
raw.headers
];
if (!params.includeSecrets && (raw.env !== void 0 || raw.headers !== void 0 || interpolatedValues.some(mcpValueHasEnvReferences))) add("secrets", `Hermes MCP server "${name}" has environment-backed values that were not imported without secret consent.`, "Re-run with --include-secrets or configure these values manually.");
if (params.includeSecrets && interpolatedValues.some((value) => value !== void 0 && resolveMcpEnvReferences(value, params.env).unresolved)) add("unresolved-secrets", `Hermes MCP server "${name}" references environment values that were not found in its .env file.`, "Define the missing values in OpenClaw's MCP server environment or headers manually.");
const cert = raw.clientCert ?? raw.client_cert;
const key = normalizeOptionalString(raw.clientKey) ?? normalizeOptionalString(raw.client_key);
if (Array.isArray(cert) && cert.length === 3) add("client-cert-password", `Hermes MCP server "${name}" uses a password-protected client key, which OpenClaw cannot represent in MCP config.`, "Configure an unencrypted protected key path or an equivalent TLS proxy manually.");
else if ((cert !== void 0 || key !== void 0) && !(Array.isArray(cert) && cert.length === 2 && normalizeOptionalString(cert[0]) && normalizeOptionalString(cert[1]) || normalizeOptionalString(cert) && key)) add("client-cert", `Hermes MCP server "${name}" uses a combined or invalid client-certificate shape that was not imported.`, "Configure separate OpenClaw clientCert and clientKey file paths manually.");
if (typeof (raw.sslVerify ?? raw.ssl_verify) === "string") add("tls-ca", `Hermes MCP server "${name}" uses a CA bundle path for TLS verification, which OpenClaw MCP config cannot represent.`, "Install the CA in the host trust store or configure an equivalent TLS proxy manually.");
const transport = normalizeOptionalString(raw.transport) ?? normalizeOptionalString(raw.type);
if (transport && ![
"http",
"streamable-http",
"sse",
"stdio"
].includes(transport)) add("transport", `Hermes MCP server "${name}" uses unsupported transport "${transport}".`, "Configure an equivalent OpenClaw MCP transport manually.");
const auth = normalizeOptionalString(raw.auth);
if (auth && auth !== "oauth") add("auth", `Hermes MCP server "${name}" uses unsupported authentication mode "${auth}".`, "Configure an equivalent OpenClaw MCP authentication mode manually.");
const oauth = isRecord(raw.oauth) ? raw.oauth : void 0;
if (auth === "oauth" || oauth) add("oauth-login", `Hermes MCP server "${name}" requires OAuth login in OpenClaw.`, `Run "openclaw mcp login ${name}" after migration.`);
if (oauth && Object.keys(oauth).some((keyName) => ![
"authProfileId",
"scope",
"redirectUrl",
"clientMetadataUrl"
].includes(keyName))) add("oauth-client", `Hermes MCP server "${name}" uses pre-registered OAuth client settings that were not copied into OpenClaw config.`, `Run "openclaw mcp login ${name}" and configure supported OAuth metadata manually.`);
const tools = isRecord(raw.tools) ? raw.tools : void 0;
const include = tools ? readToolFilterList(tools.include) : void 0;
if ((include ?? (tools ? readToolFilterList(tools.exclude) : void 0))?.some(hasUnsupportedToolPattern)) add("tool-patterns", include === void 0 ? `Hermes MCP server "${name}" was imported disabled because its tool exclusions use unsupported fnmatch patterns.` : `Hermes MCP server "${name}" has tool include patterns that were omitted because OpenClaw supports only exact names and "*".`, "Replace ? and bracket patterns with exact tool names or equivalent * patterns in mcp.servers toolFilter, then enable the server if disabled.");
if (tools && (Object.keys(tools).some((keyName) => ![
"include",
"exclude",
"resources",
"prompts"
].includes(keyName)) || tools.include !== void 0 && !readToolFilterList(tools.include) || tools.exclude !== void 0 && !readToolFilterList(tools.exclude) || tools.resources !== void 0 && parseBooleanValue(tools.resources) === void 0 || tools.prompts !== void 0 && parseBooleanValue(tools.prompts) === void 0)) add("tool-policy", `Hermes MCP server "${name}" has a tool policy that cannot be translated exactly.`, "Review and configure mcp.servers toolFilter manually.");
const lifecycle = isRecord(raw.lifecycle) ? raw.lifecycle : {};
const unsupported = [
["preflight", raw.skip_preflight === true],
["sampling", isRecord(raw.sampling) && raw.sampling.enabled !== false],
["elicitation", isRecord(raw.elicitation) && raw.elicitation.enabled !== false],
["lifecycle", readPositiveNumeric(raw.idle_timeout_seconds ?? lifecycle.idle_timeout_seconds) !== void 0 || readPositiveNumeric(raw.max_lifetime_seconds ?? lifecycle.max_lifetime_seconds) !== void 0],
["keepalive", readPositiveNumeric(raw.keepalive_interval) !== void 0]
];
for (const [feature, configured] of unsupported) if (configured) add(feature, `Hermes MCP server "${name}" uses ${feature} behavior that OpenClaw MCP config does not expose.`, "Review the server requirement and configure an equivalent deployment or runtime policy manually.");
return [...new Map(items.map((item) => [item.id, item])).values()];
}
//#endregion
export { mapMcpServer as n, mcpManualItems as r, importsMcpSensitiveValues as t };