codex.review
Review code files using GPT-5 via Cursor CLI to generate structured JSON feedback with audit logging.
Instructions
Review deliverables via Codex CLI and return review JSON only.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| targets | Yes | レビュー対象ファイル一覧(表示名とパス) | |
| previous_reviews | No | 前回レビュー結果JSON(must_fixes/チェックリスト等)への参照一覧 | |
| reference | Yes | レビューで参照すべきドキュメント一覧(表示名とパス) | |
| review_request | Yes | レビューワへの依頼文(自然文) | |
| timeout_ms | No | 外部レビュー実行のタイムアウト(ミリ秒) | |
| policy | No | 将来予約(LGTM基準等)。初期バージョンは null 固定・不使用 |
Implementation Reference
- src/codex.ts:10-20 (handler)Main execution handler for the 'codex.review' tool. Builds a prompt from input, calls the Codex CLI (with retry on failure), and returns the parsed review JSON.export async function runCodexReview(input: ReviewInput): Promise<any> { const prompt = buildPrompt(input); const timeout = input.timeout_ms ?? 1_800_000; // 30m default try { return await callCodexAndParse(prompt, timeout); } catch (_) { const retryPrompt = `${prompt}\n\n重要: 必ずJSONのみを返し、説明文やコードフェンスは含めないこと。`; return await callCodexAndParse(retryPrompt, timeout); } }
- src/codex.ts:22-56 (helper)Helper function that invokes the 'codex' CLI binary with the prompt, handles execution timeout, extracts JSON from output, parses it, and persists the review.async function callCodexAndParse(prompt: string, timeoutMs: number): Promise<any> { const allowPlainFallback = process.env.REVIEWER_MCP_ALLOW_PLAINTEXT_FALLBACK === '1' || process.env.REVIEWER_MCP_ALLOW_PLAINTEXT_FALLBACK === 'true'; const bin = process.env.REVIEWER_MCP_CODEX_BIN || 'codex'; const flags = parseFlags(process.env.REVIEWER_MCP_CODEX_FLAGS || ''); const args = ['exec', ...flags, prompt]; let stdout: string; try { ({ stdout } = await execWithTimeout(bin, args, timeoutMs)); } catch (e: any) { if (e && (e.code === 'ENOENT' || /ENOENT/.test(e.message))) { throw new Error(`Codex CLI not found: set REVIEWER_MCP_CODEX_BIN or install 'codex'.`); } throw e; } try { const extracted = extractJsonString(stdout); if (!extracted) { if (allowPlainFallback) { return stdout.trim(); } throw new Error('Could not locate JSON in Codex output'); } const reviewObj = JSON.parse(extracted); persistReview(reviewObj); return reviewObj; } catch (e) { if (allowPlainFallback) { return stdout.trim(); } throw new Error(`Review JSON parse failed: ${(e as Error).message}`); } }
- src/server.ts:72-77 (registration)Registration of the 'codex.review' tool in the MCP server's listTools response, including name, title, description, and input schema reference.{ name: 'codex.review', title: 'Run review via Codex CLI', description: 'Review deliverables via Codex CLI and return review JSON only.', inputSchema: codexInputSchema }
- src/review.ts:6-16 (schema)TypeScript type definitions for ReviewInput, Target, and Reference, which define the structure for the tool's input schema.export type Target = { file: string; path: string }; export type Reference = { file: string; path: string }; export type ReviewInput = { targets: Target[]; reference: Reference[]; previous_reviews?: Reference[]; review_request: string; timeout_ms?: number; policy?: string | null; };
- src/review.ts:24-51 (helper)Helper function to build the review prompt by loading a template and interpolating targets, references, previous reviews, and instructions.export function buildPrompt(input: ReviewInput): string { const tmplPathOverride = process.env.REVIEWER_MCP_TEMPLATE_PATH; const tmplPath = tmplPathOverride ?? fileURLToPath(new URL('./prompt/template.txt', import.meta.url)); const tmpl = readFileSync(tmplPath, 'utf8'); const targets_json = JSON.stringify(input.targets, null, 2); const reference_json = JSON.stringify(input.reference, null, 2); const prevList = input.previous_reviews ?? []; const previous_reviews_objects: any[] = []; for (const p of prevList) { try { const raw = readFileSync(p.path, 'utf8'); const obj = JSON.parse(raw); previous_reviews_objects.push({ file: p.file, path: p.path, review: obj }); } catch { previous_reviews_objects.push({ file: p.file, path: p.path, review: null, error: 'unreadable_or_invalid_json' }); } } const previous_reviews_json = JSON.stringify(previous_reviews_objects, null, 2); const follow_up_instructions = prevList.length > 0 ? '前回レビュー(must_fixes と acceptance_checklist)に基づき、各指摘が解消済みかを厳密に確認し、未解消の場合は理由と改善指示を明確に示してください。新規の懸念点があれば suggestions に含めてください。出力はJSONのみです。' : ''; return tmpl .replace('{{review_request}}', input.review_request) .replace('{{targets_json}}', targets_json) .replace('{{reference_json}}', reference_json) .replace('{{previous_reviews_json}}', previous_reviews_json) .replace('{{follow_up_instructions}}', follow_up_instructions); }