Skip to content
This repository was archived by the owner on Feb 14, 2026. It is now read-only.
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 65 additions & 20 deletions src/codex/codexMcpClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'
import { logger } from '@/ui/logger';
import type { CodexSessionConfig, CodexToolResponse } from './types';
import { z } from 'zod';
import { ElicitRequestSchema } from '@modelcontextprotocol/sdk/types.js';
import { CodexPermissionHandler } from './utils/permissionHandler';
import { execSync } from 'child_process';
import { randomUUID } from 'node:crypto';

const DEFAULT_TIMEOUT = 14 * 24 * 60 * 60 * 1000; // 14 days, which is the half of the maximum possible timeout (~28 days for int32 value in NodeJS)

Expand Down Expand Up @@ -107,50 +107,95 @@ export class CodexMcpClient {
}

private registerPermissionHandlers(): void {
// Codex uses MCP `elicitation/create` for approvals, but augments params with `codex_*` fields.
// The upstream SDK schema for ElicitRequest is strict and drops unknown fields, which breaks
// our ability to read `codex_call_id` / `codex_command` / `codex_changes`.
const CodexElicitationCreateRequestSchema = z.object({
method: z.literal('elicitation/create'),
params: z.object({
message: z.string(),
requestedSchema: z.any().optional(),

codex_elicitation: z.string().optional(),
codex_mcp_tool_call_id: z.string().optional(),
codex_event_id: z.string().optional(),
codex_call_id: z.string().optional(),
codex_command: z.array(z.string()).optional(),
codex_cwd: z.string().optional(),
codex_parsed_cmd: z.any().optional(),
codex_changes: z.record(z.string(), z.any()).optional(),
}).passthrough(),
}).passthrough();

// Register handler for exec command approval requests
this.client.setRequestHandler(
ElicitRequestSchema,
CodexElicitationCreateRequestSchema,
async (request) => {
console.log('[CodexMCP] Received elicitation request:', request.params);

// Load params
const params = request.params as unknown as {
message: string,
codex_elicitation: string,
codex_mcp_tool_call_id: string,
codex_event_id: string,
codex_call_id: string,
codex_command: string[],
codex_cwd: string
}
const toolName = 'CodexBash';
const params = request.params;
logger.debug('[CodexMCP] Received elicitation request:', params);

const toolCallId = params.codex_call_id || params.codex_event_id || params.codex_mcp_tool_call_id || randomUUID();
const codexElicitationType = params.codex_elicitation || 'unknown';

const toolName = (() => {
switch (codexElicitationType) {
case 'exec-approval':
return 'CodexBash';
case 'patch-approval':
return 'CodexPatch';
default:
return 'CodexElicitation';
}
})();

const toolInput = (() => {
switch (codexElicitationType) {
case 'exec-approval':
return {
command: params.codex_command,
cwd: params.codex_cwd
};
case 'patch-approval':
return {
changes: params.codex_changes
};
default:
return params;
}
})();

// If no permission handler set, deny by default
if (!this.permissionHandler) {
logger.debug('[CodexMCP] No permission handler set, denying by default');
return {
action: 'decline' as const,
decision: 'denied' as const,
};
}

try {
// Request permission through the handler
const result = await this.permissionHandler.handleToolCall(
params.codex_call_id,
toolCallId,
toolName,
{
command: params.codex_command,
cwd: params.codex_cwd
}
toolInput
);

logger.debug('[CodexMCP] Permission result:', result);

const action = result.decision === 'approved' || result.decision === 'approved_for_session'
? 'accept' as const
: result.decision === 'denied'
? 'decline' as const
: 'cancel' as const;
return {
action,
decision: result.decision
}
} catch (error) {
logger.debug('[CodexMCP] Error handling permission request:', error);
return {
action: 'decline' as const,
decision: 'denied' as const,
reason: error instanceof Error ? error.message : 'Permission request failed'
};
Expand Down