用 Cursor 做 Code Review

149 阅读7分钟

Code Review 是开发流程中绑定的环节,但人工审查效率有限,容易遗漏问题。这篇文章介绍我写的一个工具,让 Cursor 能够直接读取 PR/MR、分析代码变更、生成审查意见并提交到对应平台。

项目地址:github.com/OldJii/code…

思路

Cursor 本身不具备访问 GitHub/GitLab API 的能力,需要通过 MCP(Model Context Protocol)来扩展。MCP 是 Anthropic 推出的协议,允许 AI 模型与外部数据源和工具交互。

整体思路是:

  1. 写一个 MCP Server,封装 GitHub/GitLab 的 API
  2. 在 Cursor 中配置这个 MCP Server
  3. 用 mdc 规则文件定义审查规范,约束 AI 的审查行为
  4. 在 Cursor 中对话,让 AI 读取 PR、分析代码、提交评论

MCP Server

MCP Server 用 Python 实现,通过 stdio 与 Cursor 通信。核心是抽象出一个 CodeReviewProvider 基类,GitHub 和 GitLab 各自实现具体逻辑。

class CodeReviewProvider(ABC):
    @abstractmethod
    def get_pr_info(self, repo: str, pr_id: int) -> Dict:
        pass
    
    @abstractmethod
    def get_pr_changes(self, repo: str, pr_id: int) -> Dict:
        pass
    
    @abstractmethod
    def add_inline_comment(self, repo: str, pr_id: int, file_path: str,
                          line: int, line_type: str, comment: str) -> Dict:
        pass
    
    @abstractmethod
    def add_pr_comment(self, repo: str, pr_id: int, comment: str) -> Dict:
        pass

GitHub 和 GitLab 的 API 差异主要在两个地方:

  1. 认证方式:GitHub 用 Bearer Token,GitLab 用 PRIVATE-TOKEN Header
  2. 行内评论:GitLab 需要计算 line_code,GitHub 直接传行号

Token 获取

为了简化配置,没有让用户手动填写 Token,而是复用 ghglab 命令行工具的认证信息。

GitHub 直接调用 gh auth token 获取:

def _get_token(self) -> str:
    try:
        result = subprocess.run(
            ["gh", "auth", "token"],
            capture_output=True, text=True
        )
        if result.returncode == 0:
            return result.stdout.strip()
    except FileNotFoundError:
        pass
    return ""

GitLab 需要从 glab 的配置文件中解析:

def _get_token(self) -> str:
    config_paths = [
        Path.home() / ".config" / "glab-cli" / "config.yml",
        Path.home() / "Library" / "Application Support" / "glab-cli" / "config.yml",
    ]
    
    for config_path in config_paths:
        if config_path.exists():
            with open(config_path, 'r') as f:
                content = f.read()
            pattern = rf'{re.escape(self.host)}:.*?token:\s*([^\s\n]+)'
            match = re.search(pattern, content, re.DOTALL)
            if match:
                return match.group(1).strip()
    return ""

这样用户只需要执行 gh auth loginglab auth login,MCP Server 就能自动获取 Token。

GitLab 行内评论

GitLab 的行内评论 API 比较麻烦,需要提供 line_code 参数。这个参数的格式是 {head_sha}_{old_line}_{new_line},需要从 diff 中解析出来。

def _find_line_code(self, diff: str, target_line: int, line_type: str, head_sha: str) -> str:
    lines = diff.split('\n')
    old_line = 0
    new_line = 0
    
    for line in lines:
        if line.startswith('@@'):
            match = re.match(r'@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@', line)
            if match:
                old_line = int(match.group(1)) - 1
                new_line = int(match.group(2)) - 1
        elif line.startswith('-'):
            old_line += 1
            if line_type == "old" and old_line == target_line:
                return f"{head_sha}_{old_line}_"
        elif line.startswith('+'):
            new_line += 1
            if line_type == "new" and new_line == target_line:
                return f"{head_sha}_{old_line}_{new_line}"
        else:
            old_line += 1
            new_line += 1
    
    return ""

核心逻辑是遍历 diff 内容,根据 @@ 行头解析起始行号,然后跟踪 old_line 和 new_line 的变化,找到目标行对应的 line_code。

MCP 工具

最终暴露给 Cursor 的工具有 6 个:

工具说明
get_pr_info获取 PR/MR 的标题、描述、分支等信息
get_pr_changes获取代码变更,支持按文件类型过滤
extract_related_prs从描述中提取关联的 PR 链接
add_inline_comment添加行内评论
add_pr_comment添加整体评论
batch_add_comments批量添加评论

batch_add_comments 是为了减少用户确认次数,一次性提交所有评论。

审查规范

MCP 只解决了"能做什么"的问题,"怎么做"需要通过 mdc 规则文件来约束。

mdc 是 Cursor 的规则文件格式,可以在项目的 .cursor/rules/ 目录下定义。AI 在对话时会参考这些规则。

核心原则

审查规范的核心是"理解优先":

  • 先理解 PR 整体目的,再看具体 diff
  • 深度分析(架构/逻辑/性能)优先于风格检查
  • 只提"确认的问题",不提"可能的问题"
  • 结合上下文分析,不孤立判断

最后一条很重要。AI 容易犯的错误是看到一个变量可能为空就报 NPE 风险,但实际上调用方可能已经做过校验。规则里明确要求 AI 追溯上下文,避免误报。

审查流程

规则里定义了完整的审查流程:

  1. 获取变更:调用 get_pr_changes 获取所有文件的完整 diff
  2. 理解上下文:从标题、描述、变更列表理解 PR 目的
  3. 深度分析:逐文件分析架构、逻辑、性能、安全问题
  4. 去重:同一文件相同问题合并,避免重复评论
  5. 预览确认:展示所有评论让用户确认
  6. 批量提交:调用 batch_add_comments 一次性提交

第 5 步是关键。AI 生成的评论需要人工把关,避免提交低质量或错误的评论。

优先级

问题按严重程度分为三级:

  • P0:崩溃、内存泄漏、严重性能问题、安全漏洞
  • P1:架构问题、逻辑缺陷、代码质量
  • P2:代码复用、命名优化

P0 问题不限数量,P1 和 P2 会根据 P0 的数量动态调整。如果 P0 超过 10 个,说明代码质量较差,此时减少 P1/P2 的数量,集中精力在严重问题上。

避免误报

规则里列举了常见的误报场景:

类型误报条件处理
NPE调用方已校验不提
越界上文已检查长度不提
性能数据量 <= 5不提
线程耗时 < 1ms不提

这些规则是从实际 Review 经验中总结出来的。AI 的判断倾向于保守,容易对一些不是问题的代码报警。通过这些规则可以让 AI 更准确地识别真正的问题。

使用

配置

MCP 配置是全局的,编辑 ~/.cursor/mcp.json

{
  "mcpServers": {
    "code-review": {
      "command": "python3",
      "args": ["/path/to/cursor-ai-code-review/code_review_mcp.py"]
    }
  }
}

审查规范是项目级别的,需要复制到每个项目的 .cursor/rules/ 目录。这个设计是因为不同项目可能有不同的审查标准。

审查

配置完成后,在 Cursor 中直接输入:

Review https://github.com/owner/repo/pull/123

AI 会自动调用 MCP 工具获取 PR 信息和代码变更,然后按照规则进行审查,审查完成后会展示所有评论供确认:

我删除了某 Flutter 项目中 一个Dart 文件的一行 import 代码,用于演示工具的使用流程。

确认无误后,AI 会调用 batch_add_comments 批量提交评论到 GitHub/GitLab。

私有 GitLab

如果使用私有化部署的 GitLab,在认证时指定地址:

glab auth login --hostname gitlab.yourcompany.com

MCP Server 会自动从 glab 配置中读取对应的 Token。

局限

这个工具目前还有一些局限:

  1. 上下文有限:AI 只能看到 diff,无法读取完整的项目代码。对于涉及多文件交互的问题,分析能力有限。
  2. 规则依赖:审查质量很大程度取决于 mdc 规则的完善程度。规则写得不好,AI 可能遗漏问题或产生误报。
  3. 需要人工确认:每次提交评论前需要人工确认,无法完全自动化。这是有意为之,避免 AI 提交低质量评论。

后续可以考虑接入更多上下文,比如让 AI 能够读取相关文件、查看历史提交等。规则也可以根据实际使用情况持续优化。

总结

这个工具的核心价值是把 Code Review 的机械性工作交给 AI,人只需要确认最终结果。实际使用下来,对于常规的代码问题(空指针、资源泄漏、性能隐患等),AI 的识别率还是可以的。

但 AI 不能完全替代人工 Review。架构设计、业务逻辑这些需要深度理解上下文的问题,还是得靠人来判断。把 AI 当作辅助工具,提高效率的同时保持人的把关,是目前比较务实的用法。