openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
82 lines (81 loc) • 3.84 kB
JavaScript
import { t as formatErrorMessage } from "./errors-Db3Ymjlb.js";
import { r as normalizeConfiguredMcpServers } from "./mcp-config-normalize-CWrDNtye.js";
import { i as loadEnabledBundleMcpConfig } from "./bundle-mcp-zo_t2klC.js";
import fs from "node:fs";
//#region src/agents/bundle-mcp-config.ts
/**
* Merges bundled plugin MCP servers with user-configured MCP servers for agent
* runtimes.
*/
const OPENCLAW_TRANSPORT_TO_CLI_BUNDLE_TYPE = {
"streamable-http": "http",
http: "http",
sse: "sse",
stdio: "stdio"
};
function prepareOwnedBundleMcpDataDirs(params) {
const mcpServers = { ...params.config.mcpServers };
const prepareDataDirsByServer = {};
const diagnostics = [];
for (const [serverName, ownership] of Object.entries(params.prepareDataDirsByServer)) {
if (!Object.hasOwn(mcpServers, serverName)) continue;
try {
fs.mkdirSync(ownership.dataDir, { recursive: true });
prepareDataDirsByServer[serverName] = ownership;
} catch (error) {
delete mcpServers[serverName];
diagnostics.push({
pluginId: ownership.pluginId,
message: `unable to prepare PLUGIN_DATA directory "${ownership.dataDir}" for MCP server "${serverName}": ${formatErrorMessage(error)}`
});
}
}
return {
config: { mcpServers },
diagnostics,
prepareDataDirsByServer
};
}
/**
* User config stores OpenClaw MCP transport names, while CLI backends such as
* Claude Code and Gemini expect a downstream `type` field. Keep this adapter
* out of the generic merge path because embedded OpenClaw still consumes the raw
* OpenClaw `transport` shape directly.
*/
function toCliBundleMcpServerConfig(server) {
const next = { ...server };
const rawTransport = next.transport;
delete next.transport;
if (typeof next.type === "string") return next;
if (typeof rawTransport === "string") {
const mapped = OPENCLAW_TRANSPORT_TO_CLI_BUNDLE_TYPE[rawTransport];
if (mapped) next.type = mapped;
}
return next;
}
/** Loads enabled bundled MCP servers and overlays user config by server name. */
function loadMergedBundleMcpConfig(params) {
const bundleMcp = loadEnabledBundleMcpConfig({
workspaceDir: params.workspaceDir,
cfg: params.cfg,
manifestRegistry: params.manifestRegistry
});
const configuredMcp = normalizeConfiguredMcpServers(params.cfg?.mcp?.servers);
const serverOverrides = params.toolOverrides?.mcpServers;
const readServerOverride = (name) => serverOverrides && Object.hasOwn(serverOverrides, name) ? serverOverrides[name] : void 0;
const disabledConfiguredNames = new Set(Object.entries(configuredMcp).filter(([name, server]) => readServerOverride(name) !== true && server.enabled === false).map(([name]) => name));
const enabledConfiguredMcp = Object.fromEntries(Object.entries(configuredMcp).filter(([name, server]) => readServerOverride(name) !== false && (readServerOverride(name) === true || server.enabled !== false)));
const enabledBundleMcp = Object.fromEntries(Object.entries(bundleMcp.config.mcpServers).filter(([name]) => readServerOverride(name) !== false && !disabledConfiguredNames.has(name)));
const mapConfiguredServer = params.mapConfiguredServer ?? ((server) => server);
const prepareDataDirsByServer = Object.fromEntries(Object.entries(bundleMcp.prepareDataDirsByServer ?? {}).filter(([name]) => Object.hasOwn(enabledBundleMcp, name) && !Object.hasOwn(enabledConfiguredMcp, name)));
return {
config: { mcpServers: {
...Object.fromEntries(Object.entries(enabledBundleMcp).map(([name, server]) => [name, mapConfiguredServer(server, name)])),
...Object.fromEntries(Object.entries(enabledConfiguredMcp).map(([name, server]) => [name, mapConfiguredServer(server, name)]))
} },
diagnostics: bundleMcp.diagnostics,
prepareDataDirsByServer
};
}
//#endregion
export { prepareOwnedBundleMcpDataDirs as n, toCliBundleMcpServerConfig as r, loadMergedBundleMcpConfig as t };