Skip to main content
Glama

Cursor Reviewer MCP

by kodaimaehata
server.ts4.06 kB
import { existsSync } from 'node:fs'; import { readFileSync } from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { runCursorReview } from './cursor.js'; import { runCodexReview } from './codex.js'; import { normalizeReviewInput, type ReviewInput } from './review.js'; // MCP SDK import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { ListToolsRequestSchema, ListToolsResultSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js'; // Resolve schema JSON relative to this file, allowing env override const schemaDirOverride = process.env.REVIEWER_MCP_SCHEMA_DIR; const cursorSchemaPathOverride = process.env.REVIEWER_MCP_CURSOR_SCHEMA_PATH || process.env.REVIEWER_MCP_SCHEMA_PATH; const codexSchemaPathOverride = process.env.REVIEWER_MCP_CODEX_SCHEMA_PATH; const cursorSchemaPath = cursorSchemaPathOverride ? cursorSchemaPathOverride : schemaDirOverride ? path.join(schemaDirOverride, 'cursor.review.input.schema.json') : fileURLToPath(new URL('./schemas/cursor.review.input.schema.json', import.meta.url)); const codexSchemaPath = codexSchemaPathOverride ? codexSchemaPathOverride : schemaDirOverride ? path.join(schemaDirOverride, 'codex.review.input.schema.json') : fileURLToPath(new URL('./schemas/codex.review.input.schema.json', import.meta.url)); const cursorInputSchema = JSON.parse(readFileSync(cursorSchemaPath, 'utf8')); const codexInputSchema = JSON.parse(readFileSync(codexSchemaPath, 'utf8')); type Targets = { file: string; path: string }[]; type Refs = { file: string; path: string }[]; function validateTargets(targets: Targets) { for (const t of targets) { if (!existsSync(t.path)) { throw new Error(`Target path not found: ${t.path}`); } } } function validateRefs(name: string, refs: Refs | undefined) { if (!refs) return; for (const r of refs) { if (!existsSync(r.path)) { throw new Error(`${name} path not found: ${r.path}`); } } } async function main() { const server = new Server( { name: 'cursor-reviewer', version: '0.1.0' }, { capabilities: { tools: {} } } ); // SDK v1.17.x: register tools via setRequestHandler for tools/list and tools/call server.setRequestHandler(ListToolsRequestSchema, async (_req: any) => { return ListToolsResultSchema.parse({ tools: [ { name: 'cursor.review', title: 'Run review via Cursor (GPT‑5)', description: 'Review deliverables via Cursor CLI (GPT‑5) and return review JSON only.', inputSchema: cursorInputSchema }, { name: 'codex.review', title: 'Run review via Codex CLI', description: 'Review deliverables via Codex CLI and return review JSON only.', inputSchema: codexInputSchema } ] }); }); server.setRequestHandler(CallToolRequestSchema, async (request: any) => { const { name, arguments: args } = request.params ?? {}; if (name !== 'cursor.review' && name !== 'codex.review') { throw new Error(`Unknown tool: ${String(name)}`); } const input = args as ReviewInput; if (!input || !Array.isArray(input.targets) || typeof input.review_request !== 'string') { throw new Error('Invalid input: missing required fields'); } normalizeReviewInput(input); validateTargets(input.targets); validateRefs('reference', input.reference); validateRefs('previous_reviews', input.previous_reviews); const review = name === 'cursor.review' ? await runCursorReview(input) : await runCodexReview(input); const text = typeof review === 'string' ? review : JSON.stringify(review); return { content: [ { type: 'text', text } ] }; }); const transport = new StdioServerTransport(); await server.connect(transport); } // eslint-disable-next-line @typescript-eslint/no-floating-promises main();

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/kodaimaehata/reviewer-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server