1. 为什么我要自己造一个自动提交 PR 的工具
先说结论:我日常的主力代码托管平台是 Azure DevOps,但团队里有一半人习惯用命令行 Git,另一半人喜欢在网页上点来点去。每次迭代结束,光是“拉分支、改配置、提 PR、填描述、指派 Reviewer”这一套流程,就要消耗掉我大概二十分钟的机械操作时间。更烦的是,有些重复性的小改动——比如批量更新流水线里的环境变量、统一修改某个微服务的配置文件——完全没必要人工走一遍完整 PR 流程,但直接推到主分支又不符合规范。
于是我就想:能不能做一个工具,我只需要告诉它“把哪些文件改了什么”,它就能自动完成从建分支、提交、推送,到在 Azure DevOps 上创建 Pull Request 并指派审核人的全过程?正好最近 MCP(Model Context Protocol)这个概念在工具链圈子里讨论得很多,它本质上是一套让不同工具之间互相“对话”的协议标准。而 Skill 则是我自己定义的一套轻量级任务编排逻辑,用来把 Git 操作和 Azure DevOps 的 API 调用串起来。
这个项目就是在这个背景下诞生的。它适合那些每天跟 Git 和 Azure DevOps 打交道、厌倦了重复点击、想用自动化手段提升效率的开发者。哪怕你之前没接触过 MCP,只要你会基本的 Git 命令,也能看懂我是怎么一步步把它搭起来的。
提示:本文提到的所有工具和平台均为通用开发工具,不涉及任何特定网络环境或敏感配置,请放心参考。
2. 整体设计思路与核心选型考量
2.1 为什么选 MCP 而不是直接写脚本
一开始我确实想过用 Python 写个脚本,调 subprocess 执行 git 命令,再用 requests 调 Azure DevOps 的 REST API。但很快发现两个问题:第一,脚本的输入输出格式很死,每次改需求都要动代码;第二,如果以后想接入其他工具(比如自动生成 PR 描述、自动跑代码检查),脚本会变得越来越臃肿。
MCP 的好处在于它定义了一套标准的“工具调用”接口。我可以把“创建分支”“提交文件”“发起 PR”分别做成独立的 MCP 工具,每个工具只负责一件事,输入输出都是结构化的 JSON。这样上层无论是用命令行、还是用某个支持 MCP 的客户端来调用,都不需要关心底层实现。换句话说,MCP 让我的自动化逻辑变成了“可插拔”的模块。
2.2 Skill 层到底解决了什么问题
光有 MCP 工具还不够,因为实际场景里这些工具是有先后顺序和条件依赖的。比如“发起 PR”之前必须先“推送分支”,而“推送分支”之前必须先“提交更改”。如果让调用方自己去编排这些顺序,那跟直接写脚本也没区别了。
所以我加了一层 Skill。Skill 在我的设计里就是一个 YAML 文件,里面定义了任务步骤、每个步骤调用哪个 MCP 工具、参数从哪里来、失败后怎么处理。这样我只需要写一次 Skill 描述,就能重复执行同一套流程。而且 Skill 文件可以版本化管理,改流程不用改代码,这对团队协作来说非常友好。
2.3 Git 与 Azure DevOps 的职责边界
在这个项目里,Git 负责本地的一切:建分支、暂存文件、提交、推送到远端。Azure DevOps 负责远端的一切:接收推送、管理 PR、触发流水线、通知审核人。两者之间的桥梁是 Azure DevOps 提供的 REST API,我用一个轻量的 HTTP 客户端来调用它。
这里有个关键决策:我没有用 Azure DevOps 的官方 SDK,而是直接调 REST API。原因是 SDK 的版本更新往往滞后于平台功能,而且引入额外依赖会增加部署复杂度。直接调 API 虽然要自己处理认证和错误码,但胜在灵活、透明,出问题也容易排查。
注意:调用 Azure DevOps API 需要 Personal Access Token(PAT),这个 Token 的权限要控制好,建议只授予“代码读取与写入”和“PR 管理”权限,不要给全量权限。
3. 核心细节解析与实操要点
3.1 MCP 工具的定义与注册
MCP 工具的核心是一个 JSON Schema,用来描述这个工具接受什么参数、返回什么结果。以“创建分支”为例,我定义的输入参数有三个:仓库路径、分支名称、基于哪个分支创建。输出则是一个布尔值加上错误信息(如果有的话)。
在实际注册时,我把每个工具写成一个独立的 Python 函数,然后用装饰器标注它的 Schema。这样 MCP 服务启动后,会自动扫描这些函数并暴露成可调用的工具。这里有个细节:参数校验一定要在函数入口做,不要等到调用 Git 命令时才报错。比如分支名称要检查是否包含非法字符,仓库路径要检查是否存在.git目录。
另一个容易踩坑的地方是错误处理。Git 命令失败时返回的是非零退出码,但 stderr 里的信息可能很模糊。我的做法是捕获subprocess.CalledProcessError,然后把 stderr 和 stdout 一起打包返回,这样调用方能看到完整上下文。
3.2 Skill 文件的编写规范
Skill 文件我用 YAML 格式,结构上分为三部分:name(任务名称)、steps(步骤列表)、on_failure(失败策略)。每个步骤包含tool(调用的 MCP 工具名)、params(参数映射)、depends_on(依赖的前置步骤)。
参数映射支持两种写法:一种是直接写死值,比如branch_name: "auto-pr-001";另一种是引用前一步的输出,比如branch_name: "{{ steps.create_branch.output.branch_name }}"。这种模板语法让我可以在运行时动态传递数据,而不需要提前知道所有值。
写 Skill 时最容易犯的错误是步骤顺序搞错。比如把“推送分支”放在“提交更改”前面,那推送的就是空分支。我的建议是先在纸上画出流程图,确认每个步骤的输入都来自前一步的输出,然后再写 YAML。
3.3 Azure DevOps API 的认证与调用
Azure DevOps 的 REST API 认证方式很简单:在请求头里加一个Authorization: Basic <base64>,其中<base64>是":" + PAT的 Base64 编码。注意用户名部分留空,只填 PAT。
调用创建 PR 的接口时,需要构造一个 JSON body,包含sourceRefName(源分支)、targetRefName(目标分支)、title、description、reviewers等字段。这里有个坑:分支名称必须带refs/heads/前缀,否则 API 会返回 400 错误。我第一次调的时候就是漏了这个前缀,排查了半天。
另外,Azure DevOps 的 API 版本号要写在 URL 里,比如?api-version=7.0。不同版本的行为可能有差异,建议固定一个稳定版本,不要用latest。
3.4 参数计算与选择过程
在自动创建分支时,分支名称的生成规则需要仔细设计。我最初用时间戳,比如auto-pr-20250101-120000,但后来发现这样可读性太差,而且如果同一秒内触发两次会冲突。后来改成auto-pr-<短随机码>-<日期>,比如auto-pr-a3f2-20250101。短随机码用 4 位十六进制,冲突概率极低,同时保持了可读性。
PR 的标题和描述也做了模板化处理。标题格式是[AutoPR] <修改摘要>,描述里自动附上修改的文件列表和每个文件的变更行数。这些信息通过git diff --stat命令获取,解析后填入模板。这样审核人一眼就能看出这次 PR 改了什么,不需要点进去逐个文件看。
4. 实操过程与核心环节实现
4.1 环境准备与依赖安装
先确保本地已经装好 Git 和 Python 3.9 以上版本。Git 的安装教程网上很多,这里不展开,只提醒一点:安装完后要配置user.name和user.email,否则提交时会报错。
Python 依赖方面,我只需要三个库:mcp(MCP 协议的 Python 实现)、requests(调 Azure DevOps API)、pyyaml(解析 Skill 文件)。用 pip 安装即可:
pip install mcp requests pyyaml如果你用的是虚拟环境,建议先创建再安装,避免污染全局环境。我习惯用python -m venv .venv然后激活,这样每个项目独立管理依赖。
4.2 编写 MCP 工具函数
先写最基础的 Git 操作工具。以“创建分支”为例,核心逻辑就是调git checkout -b <branch_name> <base_branch>。但要注意,如果分支已存在,这个命令会失败。所以我在前面加了一步检查:先用git branch --list <branch_name>判断分支是否存在,如果存在就先删除再创建,或者直接切换过去。
“提交更改”工具稍微复杂一点。我需要先git add指定文件,然后git commit -m <message>。这里有个细节:如果没有任何更改,git commit会返回非零退出码。我的处理方式是先跑git status --porcelain,如果输出为空就直接返回“无更改可提交”,不执行 commit。
“推送分支”工具就是git push origin <branch_name>。如果远端已有同名分支,需要加--force或者先拉取合并。我默认不加 force,因为强制推送可能会覆盖别人的提交,风险太大。
4.3 实现 Azure DevOps PR 创建逻辑
创建 PR 的函数接收五个参数:组织名、项目名、仓库名、源分支、目标分支。先拼接 URL:
https://dev.azure.com/{org}/{project}/_apis/git/repositories/{repo}/pullrequests?api-version=7.0然后构造请求体。sourceRefName和targetRefName都要加refs/heads/前缀。reviewers字段是一个数组,每个元素包含id(用户的唯一标识)和vote(投票值,创建时设为 0)。如果不知道用户 ID,可以先调用户查询接口获取。
发送 POST 请求后,检查返回状态码。201 表示创建成功,400 表示参数错误,401 表示认证失败,403 表示权限不足。每种错误码对应不同的排查方向,我在代码里做了映射,方便快速定位问题。
4.4 用 Skill 串联完整流程
Skill 文件的内容大致如下:
name: auto-pr-workflow steps: - name: create_branch tool: git_create_branch params: repo_path: "/path/to/repo" branch_name: "auto-pr-{{ random_hex(4) }}-{{ today() }}" base_branch: "main" - name: commit_changes tool: git_commit depends_on: [create_branch] params: repo_path: "/path/to/repo" files: ["config/app.yaml", "pipeline/build.yml"] message: "chore: auto update config and pipeline" - name: push_branch tool: git_push depends_on: [commit_changes] params: repo_path: "/path/to/repo" branch_name: "{{ steps.create_branch.output.branch_name }}" - name: create_pr tool: azure_create_pr depends_on: [push_branch] params: org: "my-org" project: "my-project" repo: "my-repo" source_branch: "{{ steps.create_branch.output.branch_name }}" target_branch: "main" title: "[AutoPR] Update config and pipeline" description: "Automated PR created by MCP tool."执行时,Skill 引擎会按顺序调用每个工具,并把前一步的输出注入到后一步的参数里。如果某一步失败,根据on_failure策略决定是回滚还是中止。
4.5 实操现场记录与验证
第一次跑通完整流程时,我特意观察了每个步骤的耗时。创建分支大约 0.3 秒,提交更改 0.5 秒,推送分支 1.2 秒(取决于网络),创建 PR 0.8 秒。整体下来不到 3 秒,比手动操作快了至少 400 倍。
验证方式是去 Azure DevOps 网页上刷新 PR 列表,确认新 PR 已经出现,并且标题、描述、审核人都正确。我还特意检查了 PR 的源分支和目标分支,确保没有搞反。另外,我让一位同事用他的账号测试了审核人指派功能,确认通知能正常收到。
提示:第一次运行时建议在测试仓库里操作,不要直接在主仓库上跑。确认流程无误后再切换到生产仓库。
5. 常见问题与排查技巧实录
5.1 Git 相关高频问题
问题一:推送时提示“ssh认证失败”。这个通常是因为本地没有配置 SSH 密钥,或者密钥没有加到 Azure DevOps 的 SSH 公钥列表里。解决办法是生成一对新密钥,然后把公钥粘贴到 Azure DevOps 的个人设置里。如果你用的是 HTTPS 方式,则需要检查 PAT 是否过期。
问题二:.gitignore文件不生效。最常见的原因是文件已经被跟踪了,.gitignore只对未跟踪的文件有效。解决办法是先执行git rm -r --cached .,然后重新git add .和git commit。
问题三:分支合并时出现冲突。自动化工具有时候会遇到目标分支已经更新、源分支落后于目标分支的情况。我的处理方式是在创建 PR 之前先执行一次git pull origin main --rebase,把最新更改合并进来。如果 rebase 失败,就中止流程并通知人工处理。
5.2 Azure DevOps API 常见错误
| 错误码 | 可能原因 | 解决办法 |
|---|---|---|
| 401 | PAT 无效或过期 | 重新生成 PAT 并更新配置 |
| 403 | 权限不足 | 检查 PAT 是否包含代码写入和 PR 管理权限 |
| 400 | 分支名称缺少前缀 | 确保sourceRefName和targetRefName带refs/heads/ |
| 404 | 仓库或项目不存在 | 核对组织名、项目名、仓库名是否拼写正确 |
| 409 | 同名 PR 已存在 | 先关闭旧 PR 或修改源分支名称 |
5.3 Skill 执行中的典型故障
故障一:步骤依赖解析失败。如果某个步骤的depends_on引用了不存在的步骤名,Skill 引擎会直接报错。排查方法是检查 YAML 里的步骤名是否拼写一致,注意大小写敏感。
故障二:参数模板渲染为空。比如{{ steps.create_branch.output.branch_name }}渲染后变成空字符串。这通常是因为前一步没有正确返回branch_name字段。解决办法是在 MCP 工具函数里确保返回的 JSON 包含所有声明的输出字段。
故障三:并发执行导致冲突。如果同时触发多个 Skill 实例,可能会在同一个仓库上产生冲突。我的做法是在 Skill 引擎里加一个简单的文件锁,同一时间只允许一个实例操作同一个仓库。
5.4 独家避坑技巧
第一个技巧:在提交之前先跑一次git diff --check,这个命令能检测出空白字符错误和冲突标记。如果输出不为空,就中止提交,避免把有问题的代码推上去。
第二个技巧:Azure DevOps 的 PR 描述支持 Markdown,我利用这一点自动生成一个表格,列出修改的文件和变更行数。审核人看起来一目了然,减少来回沟通的成本。
第三个技巧:给自动创建的 PR 打上特定标签,比如auto-pr。这样在 PR 列表里可以快速筛选,也方便后续做统计和分析。
第四个技巧:如果 PAT 即将过期,可以设置一个提醒机制。我是在 Skill 里加了一个步骤,每次执行时检查 PAT 的剩余有效期,如果少于 7 天就发一封邮件提醒自己更换。
6. 后续扩展与个人经验分享
这个工具目前只覆盖了“创建 PR”这一个场景,但实际上可以扩展的方向很多。比如自动给 PR 添加评论、自动触发流水线、自动合并通过审核的 PR 等等。每个扩展只需要新增一个 MCP 工具,然后在 Skill 里加一个步骤就行,不需要改动已有代码。
我在实际使用中发现,最大的收益不是省了多少时间,而是减少了人为失误。以前手动操作时,偶尔会忘记指派审核人,或者把目标分支选错。现在这些步骤都是固定的,只要 Skill 文件写对了,就不会出错。
另外,Skill 文件的版本化管理也带来了一个意外的好处:新同事入职时,我直接把 Skill 文件发给他,他就能复现同样的流程,不需要我口头解释每一步怎么做。这比写文档高效多了。
最后分享一个小技巧:如果你也在用 Azure DevOps,建议把常用的 API 调用封装成独立的 MCP 工具,然后按场景组合成不同的 Skill。这样你的自动化能力会像搭积木一样越积越多,而不是每次都要从头写脚本。