




























@@ -263,6 +263,7 @@ describe("prepareCliBundleMcpConfig", () => {
263263});
264264265265const configFlagIndex = prepared.backend.args?.indexOf("--mcp-config") ?? -1;
266+expect(configFlagIndex).toBeGreaterThanOrEqual(0);
266267const generatedConfigPath = prepared.backend.args?.[configFlagIndex + 1];
267268const raw = JSON.parse(await fs.readFile(generatedConfigPath as string, "utf-8")) as {
268269mcpServers?: Record<string, { url?: string }>;
@@ -272,6 +273,90 @@ describe("prepareCliBundleMcpConfig", () => {
272273await prepared.cleanup?.();
273274});
274275276+it("replaces overlapping bundle server entries with user-configured mcp.servers", async () => {
277+const workspaceDir = await tempHarness.createTempDir(
278+"openclaw-cli-bundle-mcp-user-servers-replace-",
279+);
280+await writeClaudeBundleManifest({
281+homeDir: bundleProbeHomeDir,
282+pluginId: "omi",
283+manifest: { name: "omi" },
284+});
285+const pluginDir = path.join(bundleProbeHomeDir, ".openclaw", "extensions", "omi");
286+await fs.writeFile(
287+path.join(pluginDir, ".mcp.json"),
288+`${JSON.stringify(
289+ {
290+ mcpServers: {
291+ omi: {
292+ command: process.execPath,
293+ args: [bundleProbeServerPath],
294+ env: { BUNDLE_ONLY: "true" },
295+ },
296+ },
297+ },
298+ null,
299+ 2,
300+ )}\n`,
301+"utf-8",
302+);
303+304+const env = captureEnv(["HOME"]);
305+try {
306+process.env.HOME = bundleProbeHomeDir;
307+const prepared = await prepareCliBundleMcpConfig({
308+enabled: true,
309+mode: "claude-config-file",
310+backend: {
311+command: "node",
312+args: ["./fake-claude.mjs"],
313+},
314+ workspaceDir,
315+config: {
316+plugins: {
317+entries: {
318+omi: { enabled: true, path: pluginDir },
319+},
320+},
321+mcp: {
322+servers: {
323+omi: {
324+type: "sse",
325+url: "https://api.omi.me/v1/mcp/sse",
326+headers: { Authorization: "Bearer test-token" },
327+},
328+},
329+},
330+},
331+});
332+333+const configFlagIndex = prepared.backend.args?.indexOf("--mcp-config") ?? -1;
334+expect(configFlagIndex).toBeGreaterThanOrEqual(0);
335+const generatedConfigPath = prepared.backend.args?.[configFlagIndex + 1];
336+const raw = JSON.parse(await fs.readFile(generatedConfigPath as string, "utf-8")) as {
337+mcpServers?: Record<
338+string,
339+{
340+type?: string;
341+url?: string;
342+command?: string;
343+args?: string[];
344+env?: Record<string, string>;
345+}
346+>;
347+};
348+expect(raw.mcpServers?.omi?.type).toBe("sse");
349+expect(raw.mcpServers?.omi?.url).toBe("https://api.omi.me/v1/mcp/sse");
350+expect(raw.mcpServers?.omi?.command).toBeUndefined();
351+expect(raw.mcpServers?.omi?.args).toBeUndefined();
352+expect(raw.mcpServers?.omi?.env).toBeUndefined();
353+354+await prepared.cleanup?.();
355+} finally {
356+env.restore();
357+}
358+});
359+275360it("stabilizes the resume hash when only the OpenClaw loopback port changes", async () => {
276361const first = await prepareBundleProbeCliConfig({
277362additionalConfig: {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。