An open-source Model Context Protocol (MCP) server that reviews — read-only — the code produced by other AI coding assistants (Cursor, OpenCode, Cline, etc.).
Critic-MCP is a "second pair of eyes": it never fixes your code, it only critiques it without mercy. It exposes a single tool (review_code) and has absolutely no file-write capability.
The review_code tool compares the code you send against the original requirement (intent) and, through an LLM, produces a review report with the following sections:
- Verdict:
APPROVED|MODIFICATION_REQUIRED|REJECTED - Missing Requirements — the gap between intent and code
- Security Findings — SQL injection, XSS, privilege escalation, hardcoded secrets
- Edge-Case Findings — null/empty inputs, boundary values, off-by-one, race conditions
- Performance Findings — N+1 queries, memory leaks, redundant computation
- Other Findings + Must-Fix Items (in priority order)
Requirement: Node.js >= 20
Run the interactive setup, which works just like aws configure or gh auth login:
npx -y critic-mcp authIt asks which provider you use (gemini / openai / deepseek), prompts for your API key, and saves both to ~/.critic-mcp.json in your home directory (0600 permissions on Unix).
Add only this to your IDE's MCP settings:
{ "command": "npx", "args": ["-y", "critic-mcp"] }See the AI Assistant Integration section for client-specific details. That's it — your keys now live in one place, outside every IDE configuration.
Keys are never written into IDE configs. When the server starts it looks at
process.envfirst, then at~/.critic-mcp.json; if a key is found in neither, it directs you tonpx critic-mcp auth.
git clone https://github.com/layermedya/Critic-MCP.git
cd Critic-MCP
npm ci
npm run build
node dist/index.js auth # authenticate against your own buildnpm run build # TypeScript compilation
npm run typecheck # Type checking
npm test # Vitest unit tests
npm run test:watch # Tests in watch mode
npm start # Start the server on stdio
npm run inspect # Manual testing in the browser via MCP InspectorAll of these are optional; the normal path for API keys is npx critic-mcp auth. Environment variables always take precedence over the config file (for CI/server setups).
| Variable | Description |
|---|---|
CRITIC_PROVIDER |
gemini, openai or deepseek (falls back to the choice in ~/.critic-mcp.json, then to gemini) |
GEMINI_API_KEY |
Gemini key (overrides the file when set) |
OPENAI_API_KEY |
OpenAI/DeepSeek key (overrides the file when set) |
GEMINI_MODEL |
Gemini model name (default: gemini-3.6-flash) |
OPENAI_MODEL |
Model name (default: gpt-4o-mini, deepseek-chat for deepseek) |
OPENAI_BASE_URL |
Base URL for DeepSeek etc. (deepseek defaults to https://api.deepseek.com) |
CRITIC_TIMEOUT_MS |
LLM request timeout (default: 120000) |
CHUNK_SIZE |
Chunking limit (default: 30000 characters) |
CRITIC_CONCURRENCY |
Parallel requests during chunked review (default: 3) |
CRITIC_CONFIG_PATH |
Overrides the config file location (default: ~/.critic-mcp.json) |
None of the configurations below carry any keys; you authenticate once via the auth command (Step 1 above). npx requires the package to be published on npm; for a local clone you can instead use "command": "node", "args": ["ABSOLUTE_PATH/dist/index.js"].
In the project-level .cursor/mcp.json (or the global ~/.cursor/mcp.json):
{
"mcpServers": {
"critic": {
"command": "npx",
"args": ["-y", "critic-mcp"]
}
}
}Alternatively: Settings → MCP → Add new MCP server, then paste the JSON.
In the project-level .opencode/opencode.json or the global ~/.config/opencode/opencode.json:
{
"mcp": {
"critic": {
"type": "local",
"command": ["npx", "-y", "critic-mcp"],
"enabled": true
}
}
}OpenCode uses the
mcpkey (notmcpServers) and theenvironmentfield (notenv);commandmust be an array. You no longer need to write keys into anenvironmentblock.
Open the Cline panel → MCP Servers tab → Edit Global MCP or Edit Project MCP, then edit the JSON:
{
"mcpServers": {
"critic": {
"command": "npx",
"args": ["-y", "critic-mcp"],
"disabled": false,
"autoApprove": ["review_code"]
}
}
}
autoApprovelets Cline runreview_codewithout confirmation; it is safe because the tool never writes files.
Add the MCP server to ~/.continue/config.json (stdio transport is supported regardless of your Continue version):
{
"experimental": {
"modelContextProtocolServers": [
{
"transport": {
"type": "stdio",
"command": "npx",
"args": ["-y", "critic-mcp"]
}
}
]
}
}examples/bad_code.js is an Express example that deliberately contains SQL injection, XSS, and N+1 queries; examples/intent.txt holds the original requirement. Invoke it from any client as follows:
"Review the code in examples/bad_code.js with the review_code tool. Requirement: examples/intent.txt"
Expect the critic to catch at least the following:
- CRITICAL:
db.query("SELECT * FROM users WHERE email = '" ...)— SQL injection - CRITICAL:
res.send(comment.body)— stored XSS - HIGH: A separate query per user — N+1 problem
src/index.ts -> MCP server, zod validation, error handling + `auth` argv routing
src/cli.ts -> Interactive authentication flow (`critic-mcp auth`)
src/config.ts -> Global config (~/.critic-mcp.json) + credential resolution (env → file)
src/prompt.ts -> Ruthless Critic system prompt + chunked-review prompts
src/llm.ts -> Provider layer + timeout protection + map-reduce orchestration
src/chunker.ts -> Line-ending based chunking (for code above the limit)
When code_snippet exceeds CHUNK_SIZE (default 30,000 characters), the system automatically switches to a map-reduce flow:
- Map: The code is split at line boundaries; every chunk is sent to the LLM concurrently (default 3 parallel requests, configurable via
CRITIC_CONCURRENCY). A single chunk failure never halts the whole review. - Reduce: All returned partial analyses are merged by the "Synthesizer" prompt — which never weakens findings and never returns APPROVED when a single part reports CRITICAL — into one final report.
The server only returns a string report; it carries no file-write capability and never exposes a network client outward.
MIT