openai_review
Review code changes using OpenAI Codex to get a non-interactive analysis of uncommitted modifications or changes against a base branch.
Instructions
Code review via Codex review (non-interactive). Reviews uncommitted changes or changes against a base branch.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| instructions | No | Custom review instructions (e.g., 'Focus on error handling and race conditions') | |
| uncommitted | No | Review uncommitted changes (default true) | |
| base | No | Review against this base branch | |
| commit | No | Review a specific commit SHA | |
| timeout | No | Timeout in seconds (default 120) | |
| cwd | No | Working directory (git repo root) |
Implementation Reference
- servers/mcp-openai/server.js:290-386 (registration)Registration of the 'openai_review' tool with the MCP server, including its input schema (instructions, uncommitted, base, commit, timeout, cwd).
mcpServer.registerTool( "openai_review", { description: "Code review via Codex review (non-interactive). Reviews uncommitted changes or changes against a base branch.", inputSchema: { instructions: z .string() .optional() .describe("Custom review instructions (e.g., 'Focus on error handling and race conditions')"), uncommitted: z .boolean() .default(true) .describe("Review uncommitted changes (default true)"), base: z .string() .optional() .describe("Review against this base branch"), commit: z .string() .optional() .describe("Review a specific commit SHA"), timeout: z .number() .default(120) .describe("Timeout in seconds (default 120)"), cwd: z .string() .optional() .describe("Working directory (git repo root)"), }, }, async ({ instructions, uncommitted = true, base, commit, timeout = 120, cwd }) => { const timeoutMs = timeout * 1000; try { log(`Review: uncommitted=${uncommitted}, base=${base || "none"}, timeout=${timeout}s`); const startTime = Date.now(); const args = ["review", "--ephemeral"]; if (uncommitted) { args.push("--uncommitted"); } if (base) { args.push("--base", base); } if (commit) { args.push("--commit", commit); } if (instructions) { args.push("-"); } const { stdout, stderr, exitCode } = await runCodex(args, { timeoutMs, stdin: instructions || undefined, cwd, }); const elapsed = ((Date.now() - startTime) / 1000).toFixed(1); const combined = stdout + stderr; const error = detectError(combined); if (error) { log(`${error.errorType}: ${error.message}`); return { content: [{ type: "text", text: error.message }], isError: true, }; } const response = combined.trim() || "No review output"; log(`Review OK in ${elapsed}s (${response.length} chars)`); return { content: [{ type: "text", text: response }], }; } catch (error) { const knownError = detectError(error.message); if (knownError) { log(`${knownError.errorType}: ${knownError.message}`); return { content: [{ type: "text", text: knownError.message }], isError: true, }; } log(`Error: ${error.message}`); return { content: [{ type: "text", text: `Codex review error: ${error.message}` }], isError: true, }; } } ); - servers/mcp-openai/server.js:295-320 (schema)Input schema/validation for openai_review, defining parameters: instructions (optional string), uncommitted (boolean, default true), base (optional string), commit (optional string), timeout (number, default 120), cwd (optional string).
inputSchema: { instructions: z .string() .optional() .describe("Custom review instructions (e.g., 'Focus on error handling and race conditions')"), uncommitted: z .boolean() .default(true) .describe("Review uncommitted changes (default true)"), base: z .string() .optional() .describe("Review against this base branch"), commit: z .string() .optional() .describe("Review a specific commit SHA"), timeout: z .number() .default(120) .describe("Timeout in seconds (default 120)"), cwd: z .string() .optional() .describe("Working directory (git repo root)"), }, - servers/mcp-openai/server.js:322-385 (handler)Handler function for openai_review. Calls 'codex review --ephemeral' with optional flags (--uncommitted, --base, --commit). Passes custom instructions as stdin. Detects known errors (quota, auth, model) via detectError(). Returns review output or error message.
async ({ instructions, uncommitted = true, base, commit, timeout = 120, cwd }) => { const timeoutMs = timeout * 1000; try { log(`Review: uncommitted=${uncommitted}, base=${base || "none"}, timeout=${timeout}s`); const startTime = Date.now(); const args = ["review", "--ephemeral"]; if (uncommitted) { args.push("--uncommitted"); } if (base) { args.push("--base", base); } if (commit) { args.push("--commit", commit); } if (instructions) { args.push("-"); } const { stdout, stderr, exitCode } = await runCodex(args, { timeoutMs, stdin: instructions || undefined, cwd, }); const elapsed = ((Date.now() - startTime) / 1000).toFixed(1); const combined = stdout + stderr; const error = detectError(combined); if (error) { log(`${error.errorType}: ${error.message}`); return { content: [{ type: "text", text: error.message }], isError: true, }; } const response = combined.trim() || "No review output"; log(`Review OK in ${elapsed}s (${response.length} chars)`); return { content: [{ type: "text", text: response }], }; } catch (error) { const knownError = detectError(error.message); if (knownError) { log(`${knownError.errorType}: ${knownError.message}`); return { content: [{ type: "text", text: knownError.message }], isError: true, }; } log(`Error: ${error.message}`); return { content: [{ type: "text", text: `Codex review error: ${error.message}` }], isError: true, }; } } - servers/mcp-openai/server.js:41-71 (helper)Helper function detectError() used by the openai_review handler to detect known error patterns (quota exceeded, model not supported, auth expired) in the output of the codex CLI.
function detectError(output) { const combined = output.toLowerCase(); if (combined.includes("usage limit") || combined.includes("hit your usage limit")) { const match = output.match(/try again at (.+?)[\.\n]/); const resetDate = match ? match[1] : "unknown"; return { isError: true, errorType: "QUOTA_EXCEEDED", message: `Codex usage limit reached. Credits reset at: ${resetDate}. Use a fallback provider.`, }; } if (combined.includes("not supported when using codex with a chatgpt account")) { return { isError: true, errorType: "MODEL_NOT_SUPPORTED", message: "This model is not available with ChatGPT Plus. Use the default model.", }; } if (combined.includes("auth") && (combined.includes("expired") || combined.includes("login"))) { return { isError: true, errorType: "AUTH_EXPIRED", message: "Codex auth token expired. Run 'codex login' to re-authenticate.", }; } return null; } - servers/mcp-openai/server.js:112-177 (helper)Helper function runCodex() that spawns the 'codex' CLI process with timeout and buffer limits, used by the openai_review handler to execute 'codex review' commands.
function runCodex(args, options = {}) { const { timeoutMs = 90000, stdin: stdinData, cwd } = options; return new Promise((resolve, reject) => { const proc = spawn("codex", args, { cwd: cwd || process.cwd(), stdio: ["pipe", "pipe", "pipe"], env: { ...process.env, CODEX_HOME, }, }); let stdout = ""; let stderr = ""; let killed = false; let killTimer; const timer = setTimeout(() => { killed = true; proc.kill("SIGTERM"); killTimer = setTimeout(() => { try { if (!proc.killed) proc.kill("SIGKILL"); } catch {} }, 5000); }, timeoutMs); proc.stdout.on("data", (data) => { stdout += data.toString(); if (stdout.length > MAX_BUFFER) { killed = true; proc.kill("SIGTERM"); } }); proc.stderr.on("data", (data) => { stderr += data.toString(); if (stderr.length > MAX_BUFFER) { killed = true; proc.kill("SIGTERM"); } }); if (stdinData) { proc.stdin.write(stdinData); proc.stdin.end(); } else { proc.stdin.end(); } proc.on("close", (exitCode) => { clearTimeout(timer); clearTimeout(killTimer); if (killed) { reject(new Error(`Process killed after ${timeoutMs / 1000}s timeout. Partial output: ${(stdout + stderr).slice(-200)}`)); } else { resolve({ stdout, stderr, exitCode }); } }); proc.on("error", (err) => { clearTimeout(timer); clearTimeout(killTimer); reject(err); }); }); }