openclaw / src /agents /cli-runner /bundle-mcp.codex.test.ts
SaylorTwift's picture
SaylorTwift HF Staff
Add files using upload-large-folder tool
e249c6d verified
Raw History Blame Contribute Delete
8.64 kB
/** Tests Codex CLI bundle-MCP config override generation. */
import { describe, expect, it } from "vitest";
import type { OpenClawConfig } from "../../config/types.openclaw.js";
import { withEnvAsync } from "../../test-utils/env.js";
import { retireSessionMcpRuntime } from "../agent-bundle-mcp-manager-api.js";
import { AuthStorage } from "../sessions/auth-storage.js";
import { ModelRegistry } from "../sessions/model-registry.js";
import { buildCodexUserMcpServersThreadConfigPatchForRun } from "./bundle-mcp-codex.js";
import { prepareCliBundleMcpConfig } from "./bundle-mcp.js";
import {
cliBundleMcpHarness,
cliNativeMcpPolicyContext,
setupCliBundleMcpTestHarness,
writeCliMcpPolicyProbeServer,
} from "./bundle-mcp.test-support.js";
setupCliBundleMcpTestHarness();
describe("prepareCliBundleMcpConfig codex", () => {
it.each([
["agent:worker:policy", undefined],
["global", "worker"],
] as const)(
"keeps Codex server ownership separate from the runtime policy owner (%s)",
async (sandboxSessionKey, sandboxAgentId) => {
const serverPath = await writeCliMcpPolicyProbeServer();
const workspaceDir = cliBundleMcpHarness.bundleProbeWorkspaceDir;
const sessionId = "codex-independent-policy-owner";
const config: OpenClawConfig = {
plugins: { enabled: false },
agents: {
entries: { main: {}, worker: { tools: { deny: ["docs__delete_docs"] } } },
},
mcp: {
servers: {
docs: { command: process.execPath, args: [serverPath], codex: { agents: ["main"] } },
},
},
};
const authStorage = AuthStorage.inMemory();
await withEnvAsync(
{ OPENCLAW_STATE_DIR: cliBundleMcpHarness.bundleProbeHomeDir },
async () => {
try {
const patch = await buildCodexUserMcpServersThreadConfigPatchForRun({
cwd: workspaceDir,
run: {
agentId: "main",
sessionId,
sessionKey: "agent:main:conversation",
sandboxSessionKey,
sandboxAgentId,
sessionFile: "agent:main:conversation",
workspaceDir,
config,
prompt: "hello",
timeoutMs: 1_000,
runId: sessionId,
provider: "openai",
modelId: "gpt-5.6-luna",
model: {
id: "gpt-5.6-luna",
name: "Test model",
api: "openai-responses",
provider: "openai",
baseUrl: "https://api.openai.com/v1",
reasoning: false,
input: ["text"],
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: 100_000,
maxTokens: 1_000,
},
authStorage,
authProfileStore: { version: 1, profiles: {} },
modelRegistry: ModelRegistry.inMemory(authStorage),
thinkLevel: "off",
},
});
expect(patch?.mcp_servers.docs).toMatchObject({
enabled_tools: ["read_docs"],
disabled_tools: ["app_docs", "delete_docs", "task_docs"],
});
} finally {
await retireSessionMcpRuntime({ sessionId, reason: "test-complete" });
}
},
);
},
);
it("disables Codex native web search without bundle MCP", async () => {
const prepared = await prepareCliBundleMcpConfig({
enabled: false,
mode: "codex-config-overrides",
backend: { command: "codex", args: ["exec"] },
workspaceDir: "/tmp/openclaw-cli-codex-web-search-disabled",
toolOverrides: { webSearch: false },
});
expect(prepared.backend.args).toEqual(["exec", "-c", 'web_search="disabled"']);
expect(prepared.mcpConfigHash).toMatch(/^[0-9a-f]{64}$/);
});
it("projects session MCP tool denials into Codex disabled_tools", async () => {
const prepared = await prepareCliBundleMcpConfig({
enabled: true,
mode: "codex-config-overrides",
backend: { command: "codex", args: ["exec"] },
workspaceDir: "/tmp/openclaw-bundle-mcp-codex-deny",
config: {
plugins: { enabled: false },
mcp: {
servers: {
docs: { transport: "streamable-http", url: "https://docs.example.com/mcp" },
},
},
},
toolOverrides: { mcpToolsDeny: { docs: ["delete_docs"] }, webSearch: false },
});
expect(prepared.backend.args?.find((arg) => arg.startsWith("mcp_servers="))).toContain(
'disabled_tools = ["delete_docs"]',
);
expect(prepared.backend.args).toContain('web_search="disabled"');
});
it("projects configured wildcard filters as exact Codex CLI overrides", async () => {
const serverPath = await writeCliMcpPolicyProbeServer();
const config: OpenClawConfig = {
plugins: { enabled: false },
tools: { allow: ["docs__*"] },
mcp: {
servers: {
docs: {
command: process.execPath,
args: [serverPath],
toolFilter: { exclude: ["delete_*"] },
},
},
},
};
const prepared = await prepareCliBundleMcpConfig({
enabled: true,
mode: "codex-config-overrides",
backend: { command: "codex", args: ["exec"] },
workspaceDir: cliBundleMcpHarness.bundleProbeWorkspaceDir,
config,
nativeMcpPolicy: cliNativeMcpPolicyContext(config, "codex-cli-policy"),
});
const override = prepared.backend.args?.find((arg) => arg.startsWith("mcp_servers="));
expect(override).toContain('enabled_tools = ["read_docs"]');
expect(override).toContain('disabled_tools = ["app_docs", "delete_docs", "task_docs"]');
expect(override).not.toContain("delete_*");
});
it("hides non-model MCP tools from Codex without an explicit policy", async () => {
const serverPath = await writeCliMcpPolicyProbeServer();
const config: OpenClawConfig = {
plugins: { enabled: false },
mcp: { servers: { docs: { command: process.execPath, args: [serverPath] } } },
};
const prepared = await prepareCliBundleMcpConfig({
enabled: true,
mode: "codex-config-overrides",
backend: { command: "codex", args: ["exec"] },
workspaceDir: cliBundleMcpHarness.bundleProbeWorkspaceDir,
config,
nativeMcpPolicy: cliNativeMcpPolicyContext(config, "codex-default-hidden"),
});
const override = prepared.backend.args?.find((arg) => arg.startsWith("mcp_servers="));
expect(override).toContain('enabled_tools = ["delete_docs", "read_docs"]');
expect(override).toContain('disabled_tools = ["app_docs", "task_docs"]');
});
it("injects codex MCP config overrides with env-backed loopback headers", async () => {
const prepared = await prepareCliBundleMcpConfig({
enabled: true,
mode: "codex-config-overrides",
backend: {
command: "codex",
args: ["exec", "--json"],
resumeArgs: ["exec", "resume", "{sessionId}"],
},
workspaceDir: "/tmp/openclaw-bundle-mcp-codex",
config: { plugins: { enabled: false } },
additionalConfig: {
mcpServers: {
openclaw: {
type: "http",
url: "http://127.0.0.1:23119/mcp",
headers: {
Authorization: "Bearer ${OPENCLAW_MCP_TOKEN}",
"x-session-key": "${OPENCLAW_MCP_SESSION_KEY}",
"x-openclaw-cli-capture-key": "${OPENCLAW_MCP_CLI_CAPTURE_KEY}",
},
},
},
},
});
// Codex consumes MCP config through TOML-like -c overrides instead of a
// generated config file.
expect(prepared.backend.args).toEqual([
"exec",
"--json",
"-c",
'mcp_servers={ openclaw = { url = "http://127.0.0.1:23119/mcp", default_tools_approval_mode = "approve", bearer_token_env_var = "OPENCLAW_MCP_TOKEN", env_http_headers = { x-session-key = "OPENCLAW_MCP_SESSION_KEY", x-openclaw-cli-capture-key = "OPENCLAW_MCP_CLI_CAPTURE_KEY" } } }',
]);
expect(prepared.backend.resumeArgs).toEqual([
"exec",
"resume",
"{sessionId}",
"-c",
'mcp_servers={ openclaw = { url = "http://127.0.0.1:23119/mcp", default_tools_approval_mode = "approve", bearer_token_env_var = "OPENCLAW_MCP_TOKEN", env_http_headers = { x-session-key = "OPENCLAW_MCP_SESSION_KEY", x-openclaw-cli-capture-key = "OPENCLAW_MCP_CLI_CAPTURE_KEY" } } }',
]);
expect(prepared.cleanup).toBeUndefined();
});
});