在 Cursor 里搭一套可复用的 AI 代码评审 Skill:用自定义 MCP Server 接管 Git diff、测试与静态检查
在 Cursor 里搭一套可复用的 AI 代码评审 Skill
很多团队已经在用 Cursor 辅助写代码,但代码评审这件事,常常还停留在“把 diff 丢给 AI 看一眼”。这种做法有三个明显问题:
- 上下文不完整:AI 只看得到局部 diff,看不到测试结果、lint 报错、变更文件类型和团队规范。
- 结果不可控:同一段代码,今天说有问题,明天说没问题;缺少固定输入和固定流程。
- 无法纳入团队流程:没有权限边界、没有日志、没有失败兜底,个人玩具很难进入 PR 流程。
这篇教程的目标不是做一个“能跑”的 demo,而是设计一套可以从个人本地提效,逐步扩展到团队协作的方案:
- 用 自定义 MCP Server 暴露代码评审所需能力
- 在 Cursor 中定义一个可复用 Skill
- 让 AI 自动执行:PR 初审、风险标注、变更摘要、修复建议
- 同时保证:权限收敛、可审计、可回退、可接入 CI
如果你之前没搭过 MCP 服务,可以先参考站内的 my-mcp-server 理解 TypeScript MCP 服务的基本骨架;如果后面你想把评审结果同步到其他协作系统,也可以看看 cursorbridgemcp 和 cursor-mcp 这类跨系统协作方案。
一、先定目标:AI 代码评审到底该做什么
先别急着写工具。真正能落地的评审 Skill,职责应该非常清晰。
我建议把 AI 初审限定在这四类输出:
-
变更摘要
- 这次 PR 改了什么
- 涉及哪些模块
- 是否有数据库、配置、接口等敏感变化
-
风险标注
- 高风险:权限、事务、并发、数据删除、鉴权、密钥、外部调用
- 中风险:兼容性、异常处理、边界条件、测试缺失
- 低风险:命名、重复逻辑、注释、轻微可读性问题
-
修复建议
- 给出具体文件、具体代码段、具体修改方向
- 最好附带 patch 或伪代码
-
是否建议人工重点复核
- 比如:schema 变更、跨服务调用、缓存一致性、重试逻辑
注意,这套 Skill 不应该直接合并代码,也不应该默认自动修复。它的角色是“初审助手”,不是“最终审批人”。
二、整体架构:把模型能力和工程事实分开
推荐的架构如下:
-
Cursor Skill 层
- 负责编排步骤
- 控制提示词模板
- 决定何时调用哪些 MCP 工具
-
自定义 MCP Server
- 只暴露受控能力
- 提供 Git diff、测试结果、静态检查、文件读取、规范读取等工具
- 记录日志,限制命令范围
-
本地仓库 / CI 环境
- 真正执行 git、test、lint、typecheck
- 生成原始工程数据
-
团队规则源
- 例如
docs/review-rules.md - 或
.cursor/review-policy.yaml - 存放团队编码规范、风险检查清单、输出格式要求
- 例如
可以把它理解成一句话:
模型负责判断,MCP 负责取证,仓库负责提供事实。
这样做的好处是:
- 模型不会凭空脑补项目状态
- 每次评审输入都有来源
- 后面可以把同一套工具搬到 CI、Bot、审批流里复用
三、MCP Server 该暴露哪些工具
不要一上来开放 shell。评审场景其实只需要一小组高度收敛的工具。
建议最小工具集如下:
1)get_diff
获取当前分支相对目标分支的 diff。
输入建议:
baseRef: 例如origin/mainheadRef: 可选,默认当前 HEADpaths: 可选,限制目录contextLines: diff 上下文行数
输出建议:
- 文件列表
- unified diff
- 新增/删除行数统计
- 文件类型分类
2)get_changed_files
只列出变更文件,适合给模型先做路由判断。
输出建议:
- 文件路径
- 状态:A/M/D/R
- 语言类型
- 是否测试文件
- 是否配置文件
- 是否 migration
3)run_checks
统一执行检查任务,而不是开放任意命令。
输入建议:
tasks:test,lint,typecheck,build,security-scanscope:changed,package,repo
输出建议:
- 每项任务状态
- 退出码
- 截断后的 stdout/stderr
- 总耗时
4)read_file
读取完整文件,供模型理解上下文。
输入建议:
pathstartLineendLine
5)get_review_policy
读取团队规则,不要把规范硬编码进 prompt。
输出建议:
- 风险规则
- 必查项
- 输出格式
- 禁止建议项
6)write_review_log
把本次评审结论写入本地文件、数据库或审计系统。
输入建议:
reviewIdsummaryriskLevelfindingstoolCallsstatus
7)create_patch_preview(可选)
根据建议生成 patch 预览,但不自动落盘。
这一步很关键:不要让模型直接拥有写文件权限。先生成 patch 预览,再由人确认。
四、MCP Server 的一个可运行骨架
下面用 Node.js + TypeScript 给一个简化版结构。这里的代码重点是架构,不是完整生产实现。
目录结构
text review-mcp/ src/ index.ts tools/ getDiff.ts getChangedFiles.ts runChecks.ts readFile.ts getReviewPolicy.ts writeReviewLog.ts utils/ exec.ts audit.ts guard.ts package.json tsconfig.json
package.json
{ "name": "review-mcp", "version": "1.0.0", "type": "module", "scripts": { "build": "tsc -p tsconfig.json", "dev": "tsx src/index.ts" }, "dependencies": { "zod": "^3.23.8" }, "devDependencies": { "tsx": "^4.19.2", "typescript": "^5.6.2", "@types/node": "^22.7.4" } }
命令执行封装:src/utils/exec.ts
ts import { execFile } from "node:child_process"; import { promisify } from "node:util";
const execFileAsync = promisify(execFile);
export async function safeExec( command: string, args: string[], cwd: string, timeout = 60_000 ) { const { stdout, stderr } = await execFileAsync(command, args, { cwd, timeout, maxBuffer: 1024 * 1024 * 8, env: { ...process.env, CI: "1" } });
return { stdout: stdout?.toString() ?? "", stderr: stderr?.toString() ?? "" }; }
审计日志:src/utils/audit.ts
ts import fs from "node:fs/promises"; import path from "node:path";
const LOG_DIR = path.resolve(process.cwd(), ".review-logs");
export async function appendAuditLog(event: Record<string, unknown>) {
await fs.mkdir(LOG_DIR, { recursive: true });
const file = path.join(LOG_DIR, ${new Date().toISOString().slice(0, 10)}.jsonl);
await fs.appendFile(file, JSON.stringify({ ts: new Date().toISOString(), ...event }) + "\n");
}
权限守卫:src/utils/guard.ts
ts const ALLOWED_TASKS = new Set(["test", "lint", "typecheck", "build"]);
export function assertAllowedTask(task: string) {
if (!ALLOWED_TASKS.has(task)) {
throw new Error(Task not allowed: ${task});
}
}
export function assertSafePath(inputPath: string) { if (inputPath.includes("..")) { throw new Error("Path traversal is not allowed"); } }
getDiff 工具:src/tools/getDiff.ts
ts import { z } from "zod"; import { safeExec } from "../utils/exec.js"; import { appendAuditLog } from "../utils/audit.js";
const InputSchema = z.object({ repoPath: z.string(), baseRef: z.string().default("origin/main"), headRef: z.string().default("HEAD"), contextLines: z.number().int().min(0).max(20).default(3) });
export async function getDiff(rawInput: unknown) {
const input = InputSchema.parse(rawInput);
const args = [
"diff",
--unified=${input.contextLines},
${input.baseRef}...${input.headRef}
];
const result = await safeExec("git", args, input.repoPath);
await appendAuditLog({ tool: "get_diff", repoPath: input.repoPath, baseRef: input.baseRef, headRef: input.headRef });
return { diff: result.stdout, stderr: result.stderr }; }
runChecks 工具:src/tools/runChecks.ts
ts import { z } from "zod"; import { safeExec } from "../utils/exec.js"; import { assertAllowedTask } from "../utils/guard.js"; import { appendAuditLog } from "../utils/audit.js";
const InputSchema = z.object({ repoPath: z.string(), tasks: z.array(z.string()).min(1) });
const TASK_COMMANDS: Record<string, { command: string; args: string[] }> = { lint: { command: "pnpm", args: ["lint"] }, test: { command: "pnpm", args: ["test", "--runInBand"] }, typecheck: { command: "pnpm", args: ["typecheck"] }, build: { command: "pnpm", args: ["build"] } };
export async function runChecks(rawInput: unknown) { const input = InputSchema.parse(rawInput); const results = [] as Array<Record<string, unknown>>;
for (const task of input.tasks) { assertAllowedTask(task); const spec = TASK_COMMANDS[task]; const startedAt = Date.now();
try {
const output = await safeExec(spec.command, spec.args, input.repoPath, 120_000);
results.push({
task,
ok: true,
durationMs: Date.now() - startedAt,
stdout: output.stdout.slice(-8000),
stderr: output.stderr.slice(-4000)
});
} catch (error) {
const e = error as Error;
results.push({
task,
ok: false,
durationMs: Date.now() - startedAt,
error: e.message
});
}
}
await appendAuditLog({ tool: "run_checks", repoPath: input.repoPath, tasks: input.tasks, resultCount: results.length });
return { results }; }
readFile 工具:src/tools/readFile.ts
ts import fs from "node:fs/promises"; import path from "node:path"; import { z } from "zod"; import { assertSafePath } from "../utils/guard.js";
const InputSchema = z.object({ repoPath: z.string(), filePath: z.string(), startLine: z.number().int().positive().optional(), endLine: z.number().int().positive().optional() });
export async function readFileTool(rawInput: unknown) { const input = InputSchema.parse(rawInput); assertSafePath(input.filePath); const fullPath = path.join(input.repoPath, input.filePath); const content = await fs.readFile(fullPath, "utf8");
const lines = content.split("\n"); const start = input.startLine ? input.startLine - 1 : 0; const end = input.endLine ? input.endLine : lines.length;
return { filePath: input.filePath, content: lines.slice(start, end).join("\n") }; }
MCP 入口:src/index.ts
下面这段是伪代码,表示你需要把这些工具注册到自己的 MCP server 实现里。不同 SDK 写法会略有差异。
ts import { getDiff } from "./tools/getDiff.js"; import { runChecks } from "./tools/runChecks.js"; import { readFileTool } from "./tools/readFile.js";
async function main() { const server = createMcpServer({ name: "review-mcp", version: "1.0.0" });
server.registerTool("get_diff", getDiff); server.registerTool("run_checks", runChecks); server.registerTool("read_file", readFileTool);
await server.startStdio(); }
main().catch((err) => { console.error(err); process.exit(1); });
如果你不想从零搭 SDK,直接借鉴 my-mcp-server 的 TypeScript 可扩展模板会更快。
五、协议交互怎么设计:别让模型自己乱探索
真正好用的关键,不在于工具多,而在于调用顺序固定。
推荐把一次评审拆成下面 6 步:
第 1 步:列出变更范围
先调用 get_changed_files。
目的:
- 判断变更规模
- 判断是否包含敏感文件
- 决定后续要不要跑完整测试、要不要读取额外上下文
第 2 步:获取 diff
调用 get_diff,拿到统一格式的 diff。
目的:
- 让模型先形成初步判断
- 标出值得深挖的文件和片段
第 3 步:拉取规范
调用 get_review_policy。
目的:
- 把团队规则外置
- 支持不同仓库或不同服务有不同审查标准
第 4 步:按需读取文件上下文
只对有风险的文件调用 read_file。
目的:
- 控制 token 成本
- 避免把整个仓库塞给模型
第 5 步:运行检查
调用 run_checks。
建议顺序:
linttypechecktest- 必要时
build
第 6 步:输出评审结论并写审计日志
模型输出标准化结果,再通过 write_review_log 落盘。
这个流程的核心思想是:
先缩范围,再补上下文,最后做结论。
这样比“把整个 PR 和仓库都丢给模型”稳定得多。
六、Cursor Skill 的提示词别写成一坨
代码评审最怕一个大 prompt 包打天下。建议拆成三层:
- 系统职责提示词:约束角色和输出边界
- 流程提示词:规定调用顺序
- 评审规则提示词:从工具读取,不写死在 prompt 里
下面给一个可直接改造的 Skill 草案。
七、一个可复用的 Cursor Review Skill 模板
你可以在 Cursor 中配置一个类似这样的 Skill。
Skill 名称
PR Reviewer
Skill 目标
对当前分支相对目标分支的变更进行 AI 初审,输出:
- 变更摘要
- 风险等级
- 具体问题清单
- 修复建议
- 是否建议人工重点复核
系统提示词
text 你是资深代码评审助手,只负责初审,不负责最终审批。
你的原则:
- 只基于工具返回的事实做判断,不脑补仓库状态。
- 没有证据就明确写“未确认”。
- 优先指出高风险问题:安全、数据一致性、鉴权、并发、事务、兼容性。
- 建议必须落到具体文件、具体原因、具体修复方向。
- 如果测试、lint、typecheck 未执行成功,必须把这一点写入结论。
- 不要直接修改文件,除非用户明确要求生成 patch 预览。
流程提示词
text 按以下步骤执行,不要跳步:
- 调用 get_changed_files 识别变更范围。
- 调用 get_diff 获取完整 diff。
- 调用 get_review_policy 读取团队规范。
- 根据 diff 判断哪些文件需要 read_file 补充上下文。
- 调用 run_checks 获取 lint、typecheck、test 结果。
- 输出标准化评审结果。
- 调用 write_review_log 记录本次评审摘要。
输出模板
text
Review Summary
- PR 概要:
- 涉及模块:
- 整体风险:高/中/低
Key Findings
对于每个问题,使用以下格式:
- [风险级别] 文件路径
- 问题:
- 原因:
- 证据:
- 建议:
Check Results
- lint:
- typecheck:
- test:
- build:
Human Attention Needed
- 是否建议人工重点复核:是/否
- 建议重点查看:
Patch Preview
- 如果未生成 patch,明确写“未生成”
这里最重要的一点是:输出格式固定。一旦固定,后面你就能把结果自动发到 PR 评论、飞书、Slack,甚至做统计分析。
八、团队规范怎么外置
强烈建议在仓库里放一份评审规则文件,比如:.cursor/review-policy.yaml
yaml version: 1 reviewFocus:
- security
- transaction
- error-handling
- backward-compatibility
- test-coverage
highRiskPatterns:
- name: auth-bypass description: 涉及鉴权中间件、权限判断、角色校验的修改
- name: schema-change description: 涉及 migration、DDL、字段删除、索引调整
- name: external-side-effect description: 涉及支付、消息投递、邮件、三方 API 调用
mustCheck:
- 新增 API 是否有参数校验
- 异常分支是否记录日志
- 重试逻辑是否幂等
- 删除操作是否有保护条件
- 配置变更是否影响线上兼容性
outputRules:
- 结论必须区分高/中/低风险
- 没有证据禁止下结论
- 建议必须指向文件路径
forbidden:
- 不允许直接执行 git push
- 不允许修改生产配置
- 不允许自动提交修复
这样做有几个好处:
- 团队规则版本化
- 不同仓库有不同审查重点
- 规则变更不需要改 Skill 本身
九、一个完整示例:AI 如何审一个有风险的 PR
假设当前 PR 改了这些文件:
text src/controllers/order.ts src/services/payment.ts src/db/migrations/20250110_add_refund_status.sql src/config/default.ts
第一步:get_changed_files
模型先看到:
- 有 controller
- 有 payment service
- 有 migration
- 有 config
这时它就应该自动把风险预估抬高,因为已经触发了多个高风险类别。
第二步:get_diff
假设 diff 中包含:
- 把支付回调里的签名校验挪了位置
- migration 给
refund_status加了默认值 - 配置里新增第三方重试次数
第三步:read_file
模型只读取:
src/services/payment.tssrc/controllers/order.ts- migration 文件
而不是把整个仓库都读一遍。
第四步:run_checks
结果如下:
lint: 通过typecheck: 通过test: 失败,2 个支付回调用例失败
第五步:输出评审
一个合格的评审结论应该类似:
text
Review Summary
- PR 概要:本次变更涉及订单退款状态字段扩展、支付回调逻辑调整和重试配置新增。
- 涉及模块:订单控制层、支付服务、数据库 migration、应用配置。
- 整体风险:高
Key Findings
-
[高] src/services/payment.ts
- 问题:签名校验位置后移,存在在校验前处理业务字段的风险。
- 原因:若未先验证回调真实性,可能导致伪造请求进入业务分支。
- 证据:diff 显示 parse payload 与状态更新逻辑位于 verifySignature 之前。
- 建议:将签名验证前置到任何业务处理之前,并补充伪造回调测试用例。
-
[高] src/db/migrations/20250110_add_refund_status.sql
- 问题:新增字段默认值可能改变旧数据读取语义。
- 原因:历史记录在未回填的情况下会被统一视为某个默认状态,影响下游统计或补偿逻辑。
- 证据:migration 中直接设置 DEFAULT 'pending',未见 backfill 说明。
- 建议:补充数据迁移策略,明确旧数据回填逻辑,并检查查询侧兼容性。
-
[中] src/config/default.ts
- 问题:新增重试次数配置,但未见幂等保护说明。
- 原因:支付、退款类调用引入重试时容易造成重复副作用。
- 证据:配置新增 paymentRetryTimes=3,但 diff 未显示 request id / 幂等键处理。
- 建议:确认重试链路具备幂等约束,并在评审说明中注明。
Check Results
- lint: 通过
- typecheck: 通过
- test: 失败,2 个支付回调用例失败
- build: 未执行
Human Attention Needed
- 是否建议人工重点复核:是
- 建议重点查看:支付回调鉴权顺序、退款状态字段兼容性、重试链路幂等性
Patch Preview
- 未生成
这类结果才有工程价值,因为它不是空泛地说“建议补测试”,而是把风险、证据和建议打通了。
十、权限边界怎么收:这是能不能进团队的关键
很多本地 AI 工具的问题不在智能,而在权限太大。
评审场景建议遵守这几个原则:
1)默认只读
MCP Server 只提供:
- diff 读取
- 文件读取
- 检查执行
- 日志写入
不要直接开放:
- 任意 shell
- 任意文件写入
- git commit / push
- 部署命令
2)任务白名单
run_checks 只能跑预设命令,例如:
pnpm lintpnpm testpnpm typecheckpnpm build
不要允许模型传入自定义命令字符串。
3)路径约束
read_file 只能读取仓库根目录下的相对路径。
4)敏感信息脱敏
如果日志、配置或环境变量里可能有 token、秘钥、连接串,必须在 MCP 层做脱敏。
例如:
ts export function redactSecrets(text: string) { return text .replace(/sk-[a-zA-Z0-9_-]+/g, "sk-") .replace(/AKIA[0-9A-Z]{16}/g, "AKIA") .replace(/(?<=password=)[^\s;]+/gi, "***"); }
5)结果不可直接落代码
就算后面要做自动修复,也建议采用两阶段:
- 第一阶段:生成 patch preview
- 第二阶段:人工确认后再应用
这一点和直接放开 write_file 是两个风险等级。
十一、可审计日志怎么做
如果没有审计,团队很难信任 AI 评审结果。
至少要记这几类日志:
-
谁触发的
- 用户名
- 机器
- 仓库
- 分支
-
调用了哪些工具
- 工具名
- 输入参数摘要
- 执行时长
- 是否成功
-
模型最终输出了什么
- 结论摘要
- 风险等级
- 问题数量
-
失败信息
- 哪一步失败
- 是工具失败还是模型失败
- 是否有 fallback
日志不一定一开始就接 ELK 或 Datadog,本地 JSONL 也可以,关键是结构统一。
推荐日志结构:
{ "reviewId": "pr-2025-01-10-001", "repo": "order-service", "branch": "feature/refund-status", "actor": "alice", "toolCalls": [ {"tool": "get_changed_files", "ok": true, "durationMs": 80}, {"tool": "get_diff", "ok": true, "durationMs": 120}, {"tool": "run_checks", "ok": true, "durationMs": 18230} ], "result": { "riskLevel": "high", "findingCount": 3, "humanReviewRequired": true }, "status": "completed" }
后面你就能做几件事:
- 统计 AI 发现问题的命中率
- 统计哪些仓库最容易出现高风险 PR
- 统计哪些规则最常触发
- 反向优化提示词和 review policy
十二、失败兜底怎么设计
真实环境里,工具失败比模型瞎说更常见。
所以必须定义 fallback,不要只追求“理想路径”。
场景 1:git diff 失败
可能原因:
- baseRef 不存在
- 仓库未拉取远端
- 当前目录不是 git repo
兜底策略:
- 尝试改用
HEAD~1..HEAD - 明确在输出中标注“基线不完整,评审可信度下降”
- 不允许模型假装拿到了完整 diff
场景 2:测试执行超时
可能原因:
- 测试本身慢
- 环境依赖未就绪
- 本地资源不足
兜底策略:
- 降级成只做静态评审
- 在结论中明确写:测试结果缺失,不可据此判断功能正确性
场景 3:某文件太大,超出上下文
兜底策略:
- 只读取变更片段上下游 100~200 行
- 对超大文件先做人类摘要再继续
场景 4:模型输出格式跑偏
兜底策略:
- 用固定 JSON schema 或固定 section 标题
- 不合规就要求重新生成
场景 5:工具返回敏感信息
兜底策略:
- MCP 层先脱敏再返回
- 日志再次脱敏
一句话总结:
失败不可怕,可怕的是失败后还给出一个看起来很自信的错误结论。
十三、从个人本地提效,怎么扩展到团队协作
这套方案最好的地方,是可以分阶段落地。
阶段 1:个人本地使用
目标:先让自己在开 PR 前多一个稳定的初审助手。
做法:
- 本地启动
review-mcp - Cursor Skill 手动触发
- 输出结果保存在
.review-logs
这个阶段最重要的是打磨两件事:
- 工具集是否足够小而稳
- 提示词是否能持续产出你认可的格式
阶段 2:小团队共享规范
目标:同一个团队都用同一套 review policy。
做法:
- 把
.cursor/review-policy.yaml放进仓库 - 统一 Skill 模板
- 审计日志上传到共享位置
这个阶段开始,你会发现 AI 评审不再是“个人习惯”,而是“团队规则自动执行器”。
阶段 3:接到 PR 流程
目标:PR 创建或更新时自动跑一轮初审。
做法:
- 在 CI 中运行同一套 MCP 后端逻辑,或复用其工具实现
- 将结果同步到 GitHub/GitLab 评论
- 对高风险 PR 自动打标签
这里可以结合 cursor-mcp-server 这类代理执行方案,把代码库检索和任务委托串起来;如果你还想把结果同步到其他协作文档或审批说明,也可以参考 cursorbridgemcp。
阶段 4:多角色协作
目标:不只是 reviewer,一个 PR 可以并发跑多个专长代理。
比如:
- 安全 reviewer:只盯鉴权、注入、密钥、外部访问
- 后端 reviewer:只盯事务、并发、性能、异常分支
- 测试 reviewer:只盯测试覆盖率、断言质量、回归风险
如果你想进一步做消息协作、预算控制或多窗口稳定交互,可以继续看 cursor-mcp 和 MCP Feedback Enhanced (Hunter Fork)。
十四、落地时最容易踩的坑
坑 1:一开始就让 AI 读完整仓库
结果通常是:
- 成本高
- 响应慢
- 重点不清楚
- 模型容易被无关代码带偏
正确做法是:先看 changed files,再读 diff,再按需读文件。
坑 2:把规则全写死在 prompt 里
问题是团队规则一变,Skill 全得改。
正确做法:规则放仓库文件里,用 get_review_policy 读取。
坑 3:开放任意 shell
这会直接把评审工具变成通用代理,权限边界完全失控。
正确做法:只开放白名单任务。
坑 4:输出格式不固定
结果是每次都像一篇散文,后面无法自动化接入 PR 评论或统计系统。
正确做法:固定 section 或 JSON schema。
坑 5:没有日志
一旦出现误判,你根本不知道:
- 当时读取了哪些文件
- 测试是否跑过
- 模型依据是什么
正确做法:工具调用和结果都记录。
十五、你可以直接照着做的最小落地清单
如果你准备本周就试,我建议按这个最小清单推进:
第一天
- 建一个
review-mcp项目 - 实现
get_changed_files、get_diff、read_file - 在 Cursor 里连上 MCP Server
第二天
- 加
run_checks - 加
.cursor/review-policy.yaml - 定一个固定输出模板
第三天
- 增加
write_review_log - 跑 3~5 个真实 PR 做对比
- 调整高风险规则和建议格式
第四天以后
- 加 patch preview
- 把结果自动贴到 PR 评论
- 统计命中率和误报率
这样推进的节奏最现实:先稳定输入,再稳定输出,最后再考虑自动化闭环。
小结
这套方案的核心不是“让 AI 看代码”,而是把代码评审拆成一个可控的工程系统:
- MCP Server 提供事实输入:diff、文件、测试、静态检查、规范
- Cursor Skill 负责稳定编排:固定步骤、固定提示词、固定输出
- 权限边界保证安全:默认只读、任务白名单、路径限制、脱敏
- 审计日志保证可追踪:知道 AI 看了什么、做了什么、怎么得出结论
- 失败兜底保证可信:拿不到证据就降级,而不是乱判
- 架构可扩展到团队流程:从个人本地,到共享规范,到 PR 自动初审
如果你只记住一句话,那就是:
不要把 AI 代码评审做成一个会说话的 diff 查看器,而要把它做成一个有输入约束、有规则来源、有日志闭环的评审流水线。
做到这一步,AI 才真正有机会进入团队工程流程,而不是停留在“偶尔问一下”的玩具阶段。
// 本文推荐
// 相关阅读
给 Cursor/Claude Code 接上项目大脑:用 MCP 把代码索引、文档和变更记录变成可检索上下文
只靠 IDE 当前打开的文件,AI 很难在中大型仓库里稳定协作。本文从零搭建一个最小可用 MCP Server,把代码、文档和 Git 变更暴露给 Cursor 或 Claude Code,让建议更可追溯、更可信。
从零搭建基于 MCP 的开发助手工作流:让 Cursor / Claude Code 先读文档、再改代码、最后产出可审查 PR
这篇教程面向有项目经验的开发者,讲清楚如何把 MCP 接进 Cursor 或 Claude Code,搭建一条“读仓库文档与 issue → 定位问题 → 生成修复方案 → 修改代码 → 跑测试 → 输出 PR 说明”的可复用工作流。
用 MCP 给 Cursor 接入团队知识库:从文档检索到可审计的问答 Skill 实战
本地代码补全只能看见当前仓库,解决不了团队规范、接口约定和历史决策的检索问题。本文带你从 0 到 1 用 MCP 为 Cursor 接入内部文档、代码索引和 API 说明,并封装成可复用、可控、可审计的团队知识增强编程流。