Auto-approve Codex title MCP tool

This commit is contained in:
weishu
2026-06-08 13:44:26 +08:00
parent fa363c2f6c
commit deb05bb783
7 changed files with 84 additions and 5 deletions
+7 -1
View File
@@ -80,7 +80,12 @@ describe('codexLocal', () => {
mcpServers: {
hapi: {
command: hapiCommandPath,
args: ['mcp', '--url', 'http://127.0.0.1:63995/']
args: ['mcp', '--url', 'http://127.0.0.1:63995/'],
tools: {
change_title: {
approval_mode: 'approve'
}
}
}
},
sessionHook: {
@@ -108,6 +113,7 @@ describe('codexLocal', () => {
expect(hookArg).toBeDefined();
expect(hookArg).toContain('{ hooks = [{ type = "command", command = "');
expect(args).toContain("mcp_servers.hapi.args=['mcp','--url','http://127.0.0.1:63995/']");
expect(args).toContain('mcp_servers.hapi.tools.change_title.approval_mode="approve"');
});
it('passes reasoning effort through Codex config instead of an unsupported CLI flag', async () => {
+2 -1
View File
@@ -9,6 +9,7 @@ import {
import { codexSystemPrompt } from './utils/systemPrompt';
import type { ReasoningEffort } from './appServerTypes';
import { resolveCodexCommand } from './utils/codexExecutable';
import type { McpServersConfig } from './utils/buildHapiMcpBridge';
/**
* Filter out 'resume' subcommand which is managed internally by hapi.
@@ -38,7 +39,7 @@ export async function codexLocal(opts: {
sandbox?: 'read-only' | 'workspace-write' | 'danger-full-access';
onSessionFound: (id: string) => void;
codexArgs?: string[];
mcpServers?: Record<string, { command: string; args: string[] }>;
mcpServers?: McpServersConfig;
sessionHook?: {
port: number;
token: string;
@@ -47,6 +47,37 @@ describe('appServerConfig', () => {
expect(params.approvalPolicy).toBe('on-request');
});
it('passes MCP per-tool approval config through thread config', () => {
const params = buildThreadStartParams({
cwd: '/workspace/project',
mode: { permissionMode: 'default', collaborationMode: 'default' },
mcpServers: {
hapi: {
command: 'node',
args: ['mcp'],
tools: {
change_title: {
approval_mode: 'approve'
}
}
}
}
});
expect(params.config).toEqual({
'mcp_servers.hapi': {
command: 'node',
args: ['mcp'],
tools: {
change_title: {
approval_mode: 'approve'
}
}
},
developer_instructions: codexSystemPrompt
});
});
it('ignores CLI overrides when permission mode is not default', () => {
const params = buildThreadStartParams({
cwd: '/workspace/project',
+2 -1
View File
@@ -61,7 +61,8 @@ function buildMcpServerConfig(mcpServers: McpServersConfig): Record<string, unkn
for (const [name, server] of Object.entries(mcpServers)) {
config[`mcp_servers.${name}`] = {
command: server.command,
args: server.args
args: server.args,
...(server.tools ? { tools: server.tools } : {})
};
}
+13 -1
View File
@@ -12,9 +12,16 @@ import type { ApiSessionClient } from '@/api/apiSession';
/**
* MCP server entry configuration.
*/
export type McpToolApprovalMode = 'auto' | 'prompt' | 'approve';
export interface McpServerToolConfig {
approval_mode?: McpToolApprovalMode;
}
export interface McpServerEntry {
command: string;
args: string[];
tools?: Record<string, McpServerToolConfig>;
}
/**
@@ -63,7 +70,12 @@ export async function buildHapiMcpBridge(
mcpServers: {
hapi: {
command: bridgeCommand.command,
args: bridgeCommand.args
args: bridgeCommand.args,
tools: {
change_title: {
approval_mode: 'approve'
}
}
}
}
};
@@ -23,6 +23,24 @@ describe('codexMcpConfig', () => {
]);
});
it('builds per-tool approval mode config', () => {
const mcpServers = {
hapi: {
command: 'hapi',
args: ['mcp'],
tools: {
change_title: {
approval_mode: 'approve' as const
}
}
}
};
const args = buildMcpServerConfigArgs(mcpServers);
expect(args).toContain('mcp_servers.hapi.tools.change_title.approval_mode="approve"');
});
it('builds config args for multiple MCP servers', () => {
const mcpServers = {
hapi: { command: 'hapi', args: ['mcp'] },
+11 -1
View File
@@ -9,6 +9,7 @@
import { createHash } from 'node:crypto';
import { getHappyCliCommand } from '@/utils/spawnHappyCLI';
import type { McpServersConfig } from './buildHapiMcpBridge';
/**
* Escape a string value for use in a TOML string literal.
@@ -121,7 +122,7 @@ export function buildSessionStartHookConfigArgs(port: number, token: string): st
* @returns Array of CLI arguments to pass to codex
*/
export function buildMcpServerConfigArgs(
mcpServers: Record<string, { command: string; args: string[] }>
mcpServers: McpServersConfig
): string[] {
const configArgs: string[] = [];
@@ -133,6 +134,15 @@ export function buildMcpServerConfigArgs(
// Use TOML literal strings to avoid shell-quote mangling on Windows.
const argsToml = buildTomlLiteralArray(server.args);
configArgs.push('-c', `mcp_servers.${name}.args=${argsToml}`);
for (const [toolName, tool] of Object.entries(server.tools ?? {})) {
if (tool.approval_mode) {
configArgs.push(
'-c',
`mcp_servers.${name}.tools.${toolName}.approval_mode="${escapeTomlString(tool.approval_mode)}"`
);
}
}
}
return configArgs;