内容
# devops-mcp
[](https://pypi.org/project/devops-mcp/)
[](https://pypi.org/project/devops-mcp/)
[](LICENSE)
MCP 服务器,暴露 Azure DevOps 作为 LLMs 的工具 — 包括流水线、仓库、拉取请求和工作项。 使用 FastMCP 构建,通过 stdio 传输。
通过 **stdio** 通信,适用于 GitHub Copilot、Claude Code 和任何兼容 MCP 的客户端。
---
## 快速开始
**1. 安装依赖**
```bash
uv sync
```
**2. 配置** — 添加到您的 MCP 客户端配置(参见下面的 [MCP 客户端设置](#mcp-client-setup))。
**3. 运行服务器**
```bash
uv run devops-mcp
```
---
## 安装
### 先决条件
- Python `>=3.10`
- [uv](https://docs.astral.sh/uv/)(推荐)
- 具有访问 Azure DevOps 权限的 Microsoft Entra ID 身份
### 安装依赖
```bash
uv sync
```
### 构建软件包
```bash
uv run python -m build
```
---
## 配置
所有配置均通过环境变量驱动 — 代码中没有机密,没有硬编码的组织名称或租户 ID。
| 变量 | 是否必需 | 默认值 | 描述 |
|---|---|---|---|
| `AZDO_AUTH_TYPE` | 否 | `default` | 身份验证方法。可以是:`default`、`azure_cli`、`interactive`、`client_secret`、`managed_identity`。`default` 按顺序尝试所有凭据源(环境变量、Azure CLI、托管身份),是本地开发的正确选择。参见 [身份验证](#authentication)。 |
| `AZDO_ORGANIZATION` | 否 | — | 默认 Azure DevOps 组织名称。可以按工具调用覆盖。 如果未按调用提供,则需要此项。 |
| `AZDO_PROJECT` | 否 | — | 默认 Azure DevOps 项目名称。可以按工具调用覆盖。 如果未按调用提供,则需要此项。 |
| `AZDO_TENANT_ID` | 条件 | — | Microsoft Entra ID 租户 ID。对于 `client_secret` 需要此项。对于 `interactive` 建议提供此项,以将登录限制在正确的租户中。 |
| `AZDO_CLIENT_ID` | 仅 `client_secret` | — | 服务主体客户端 ID。 |
| `AZDO_CLIENT_SECRET` | 仅 `client_secret` | — | 服务主体客户端机密。 |
| `AZDO_LOG_LEVEL` | 否 | `INFO` | 日志详细程度:`DEBUG`、`INFO`、`WARNING`、`ERROR`。所有日志都输出到 stderr;stdout 保留用于 MCP stdio 传输。 |
| `AZDO_ALLOW_WRITE` | 否 | off | 设置为 `true` 以注册创建、更新、标记、链接和评论(写入)工具。未设置时,服务器为只读 — 代理看不到写入工具。 |
| `AZDO_ALLOW_DELETE` | 否 | off | 设置为 `true` 以注册删除工具。未设置时,删除工具对代理不可见。 |
| `AZDO_EPHEMERAL_TOKEN` | 否 | `false` | **仅限交互式身份验证**。当为 `false`(默认值)时,MSAL令牌缓存通过操作系统的机密存储(Windows DPAPI、macOS 钥匙串、Linux libsecret)持久化到磁盘,并且 `AuthenticationRecord` 边车写入到 `~/.devops-mcp/auth-record.json`,因此后续服务器重启可以在不提示新的浏览器窗口的情况下默默地进行身份验证。设置为 `true`、`1` 或 `yes` 以使用仅内存缓存(无磁盘缓存,无边车)- 每次重启都提示。无效值回落到 `false`,并带有记录的警告。对于除 `interactive` 之外的任何身份验证类型,此设置均无效。 |
| `AZDO_TOKEN_CACHE_PROFILE` | 否 | — | **仅限交互式身份验证**。一个文件名安全的后缀(`[A-Za-z0-9_-]`)附加到 MSAL 缓存名称和 `AuthenticationRecord` 边车,因此两个登录到同一主机上的 **不同租户/帐户** 的服务器实例将保持单独的缓存,而不是覆盖彼此的固定帐户。省略(或留空)适用于单一租户设置 - 使用原始共享文件名。字符超出 `[A-Za-z0-9_-]` 将引发错误,而不是被静默删除(清理可能会将两个不同的配置文件合并到一个共享缓存中)。 |
| `AZDO_AUTH_TIMEOUT_SECONDS` | 否 | `30` | 在身份验证错误之前等待凭据获取的最长时间(秒)。适用于所有身份验证类型。无效或非正值回落到 `30`。在慢速网络或 MFA 密集型环境中增加此值。 |
---
## 身份验证
服务器使用 **Microsoft Entra ID(Azure AD)OAuth 2.0**,通过 [`azure-identity`](https://pypi.org/project/azure-identity/) 库。设置 `AZDO_AUTH_TYPE` 以选择身份验证方法。
| `AZDO_AUTH_TYPE` | 描述 | 最适合 |
|---|---|---|
| `default` *(默认)* | `DefaultAzureCredential` — 按顺序尝试环境变量、Azure CLI 会话、托管身份和其他来源。不在进程中提示。 | **推荐 - 适用于所有情况** |
| `azure_cli` | 使用活动 Azure CLI 会话(`az login`)。不在进程中提示。 | 使用现有 CLI 会话进行本地开发 |
| `interactive` | 打开浏览器进行交互式登录。支持 MFA 和多帐户使用。从持久令牌缓存(默认开启)中受益:第一次启动时提示;后续重启在令牌有效期内默默地重用缓存的刷新令牌。 | 无 CLI 会话的本地开发 |
| `client_secret` | 带有客户端机密的服务主体。需要 `AZDO_TENANT_ID`、`AZDO_CLIENT_ID` 和 `AZDO_CLIENT_SECRET`。 | CI/CD,无人值守自动化 |
| `managed_identity` | Azure 托管身份。无需管理凭据。 | Azure 托管工作负载(VM、函数、容器应用) |
**适用于 `default` / 本地开发:** 运行 `az login` 一次 - `DefaultAzureCredential` 将自动获取它。
**适用于 `interactive`:** 在第一次使用时打开浏览器窗口。设置 `AZDO_TENANT_ID` 以将登录限制在特定的 Entra ID 租户中(建议在多个帐户共存时使用)。默认启用持久令牌缓存,因此后续重启是沉默的;设置 `AZDO_EPHEMERAL_TOKEN=true` 以选择退出。
**适用于 `client_secret`:** 还要设置 `AZDO_TENANT_ID`、`AZDO_CLIENT_ID` 和 `AZDO_CLIENT_SECRET`。
---
## 安全
### 默认情况下安全的写入和删除防护
写入和删除工具 **默认情况下不注册** - 它们根本不出现在代理中,直到显式启用。服务器在设置 `AZDO_ALLOW_WRITE=true` 和/或 `AZDO_ALLOW_DELETE=true` 之前为只读。
### 环境变量驱动的配置
所有配置均通过环境变量提供。没有机密、组织名称、项目名称或租户 ID 被硬编码。`.vscode/mcp.json` 被 gitignore,因为它可能包含凭据。
### Stdout 保留用于 MCP 传输
Stdout 专门保留用于 MCP stdio 传输消息。所有服务器日志(包括身份验证事件)都通过 Python `logging` 模块输出到 stderr。永远不要将 stdout 重定向到日志文件。
### 令牌缓存注意事项(`interactive` 身份验证)
默认情况下,MSAL 令牌缓存使用操作系统的机密存储(Windows DPAPI、macOS 钥匙串、Linux libsecret)加密存储。在没有机密存储的无头 Linux 上(例如,没有安装 GNOME 钥匙串 / libsecret),服务器日志记录一个可行的警告,并回落到仅内存缓存。设置 `AZDO_EPHEMERAL_TOKEN=true` 以抑制警告并始终在这些主机上使用仅内存缓存。
### 单个主机上的多个租户/帐户(`interactive` 身份验证)
默认缓存和边车文件名(`devops-mcp.cache`、`~/.devops-mcp/auth-record.json`)按主机共享,因此登录到 **不同租户/帐户** 的两个 `interactive` 会话将覆盖彼此的固定帐户。给每个会话一个不同的 `AZDO_TOKEN_CACHE_PROFILE`(例如 `prod`、`dev`),以保持它们的缓存和 `AuthenticationRecord` 边车分开。配置文件是租户范围的缓存密钥:每个条目仅登录一次(其自己的浏览器提示),然后以其自己的帐户身份静默重启,而工具仍然接收特定的 `organization`/`project` 按调用。配置文件永远不会冲突。
注册两个服务器条目,每个条目都有自己的配置文件和(推荐的)匹配的 `AZDO_TENANT_ID`:
```json
{
"servers": {
"devops-mcp-prod": {
"type": "stdio",
"command": "uv",
"args": ["run", "devops-mcp"],
"env": {
"AZDO_AUTH_TYPE": "interactive",
"AZDO_TENANT_ID": "<prod-tenant-id>",
"AZDO_TOKEN_CACHE_PROFILE": "prod"
}
},
"devops-mcp-dev": {
"type": "stdio",
"command": "uv",
"args": ["run", "devops-mcp"],
"env": {
"AZDO_AUTH_TYPE": "interactive",
"AZDO_TENANT_ID": "<dev-tenant-id>",
"AZDO_TOKEN_CACHE_PROFILE": "dev"
}
}
}
}
```
### 弹性行为
这些行为是内置的,无需配置:
- **自动重试** - 收到 `429`(限流)或瞬时网关错误(`502`、`503`、`504`)的请求将自动重试,带有退避和 `Retry-After` 头部尊重。**非幂等写入(POST、PATCH)不会在 `5xx` 上重试** - 网关错误可能在服务器已经提交操作后到达;只有 `429`(保证请求在处理前被拒绝)才是安全重试所有方法。
- **响应大小上限** - 大于 **5 MB** 的响应将被错误替换,要求代理缩小查询。对于大型流水线日志,优先使用 `devops_get_run_timeline` 来排查而无需拉取日志文本;`devops_get_run_log_content` 默认绑定到 500 行页面(`max_lines`、`tail`、`start_line`/`end_line`),带有分页信封,`devops_search_run_log` 仅返回匹配的行。
- **身份验证超时** - 凭据获取受 `AZDO_AUTH_TIMEOUT_SECONDS`(默认 30 秒)限制。慢速或挂起的身份验证调用释放每个作用域的锁,因此后续调用者不会无限期序列化。
---
## MCP 客户端设置
### GitHub Copilot(VS Code)
将以下内容添加到项目根目录的 `.vscode/mcp.json` 中。注意:`.vscode/mcp.json` 被 gitignore,因为它可能包含机密。
**默认 / 本地开发(推荐):**
```json
{
"servers": {
"devops-mcp": {
"type": "stdio",
"command": "uv",
"args": ["run", "devops-mcp"],
"env": {
"AZDO_ORGANIZATION": "<your-org>",
"AZDO_PROJECT": "<your-project>"
}
}
}
}
```
**启用写入工具:**
```json
{
"servers": {
"devops-mcp": {
"type": "stdio",
"command": "uv",
"args": ["run", "devops-mcp"],
"env": {
"AZDO_ORGANIZATION": "<your-org>",
"AZDO_PROJECT": "<your-project>",
"AZDO_ALLOW_WRITE": "true"
}
}
}
}
```
**服务主体(CI/CD):**
```json
{
"servers": {
"devops-mcp": {
"type": "stdio",
"command": "uv",
"args": ["run", "devops-mcp"],
"env": {
"AZDO_AUTH_TYPE": "client_secret",
"AZDO_TENANT_ID": "<your-tenant-id>",
"AZDO_CLIENT_ID": "<your-client-id>",
"AZDO_CLIENT_SECRET": "<your-client-secret>",
"AZDO_ORGANIZATION": "<your-org>",
"AZDO_PROJECT": "<your-project>"
}
}
}
}
```
---
## 工具
**6 个领域共 49 种工具**。带有防护的工具仅在设置了相应的环境标志时注册。
| 防护 | 含义 |
|---|---|
| `default` | 始终注册(读取和安全查询)。 |
| `write` | 仅在 `AZDO_ALLOW_WRITE=true` 时注册。 |
| `delete` | 仅在 `AZDO_ALLOW_DELETE=true` 时注册。 |
### 流水线(10 种工具)
| 工具 | 防护 | 描述 |
|---|---|---|
| `devops_list_pipelines` | 默认 | 列出项目中定义的流水线 |
| `devops_list_pipeline_runs` | 默认 | 列出特定流水线的运行 |
| `devops_get_pipeline_run` | 默认 | 获取特定流水线运行的详细信息 |
| `devops_get_build` | 默认 | 通过 `buildId` 获取构建详细信息(解析构建 URL 到流水线信息) |
| `devops_list_run_logs` | 默认 | 列出按 `buildId` 的构建日志元数据 |
| `devops_get_run_timeline` | 默认 | 紧凑的、过滤的构建时间线,带有内联错误消息 - 推荐的第一个停止点,用于回答“为什么构建失败”,通常需要零日志获取 |
| `devops_get_run_log_content` | 默认 | 获取特定日志的纯文本内容;默认绑定到 `max_lines`(默认 500),带有 `start_line`/`end_line`/`tail` 窗口和分页信封 |
| `devops_search_run_log` | 默认 | 在进程中搜索日志并返回仅匹配的行加上上下文 - 非匹配行永远不会到达模型 |
| `devops_list_build_artifacts` | 默认 | 列出构建生成的工件 |
| `devops_run_pipeline` | 写入 | 触发新的流水线运行;可选地覆盖分支、模板参数或排队时变量 |
### 仓库(7 种工具)
| 工具 | 防护 | 描述 |
|---|---|---|
| `devops_list_repositories` | 默认 | 列出项目中的 Git 仓库 |
| `devops_get_repository` | 默认 | 获取特定仓库的详细信息 |
| `devops_list_branches` | 默认 | 列出仓库中的分支 |
| `devops_get_file_content` | 默认 | 获取文件的文本内容;支持可选的 `branch` 或 `commit_id`;二进制文件返回错误 |
| `devops_list_repository_items` | 默认 | 浏览文件和文件夹;使用 `recursion_level` 控制深度(`oneLevel`、`full` 等) |
| `devops_list_commits` | 默认 | 列出提交,可选过滤器用于分支、作者和日期范围 |
| `devops_get_commit` | 默认 | 获取特定提交的详细信息;设置 `change_count` 以包含更改的文件路径 |
### 拉取请求(17 种工具)
| 工具 | 防护 | 描述 |
|---|---|---|
| `devops_get_pull_request` | 默认 | 获取特定拉取请求的详细信息 |
| `devops_list_pull_requests` | 默认 | 列出拉取请求,可选过滤器(状态、分支、创建者、审阅者、标签) |
| `devops_create_pull_request` | 写入 | 创建新的拉取请求,可选地链接工作项 |
| `devops_update_pull_request` | 写入 | 更新标题、描述、状态、草稿状态、目标分支、自动完成或完成选项 |
| `devops_complete_pull_request` | 写入 | 完成(合并)拉取请求 - 不可逆;请先确认合并策略和源分支删除,以避免不想要的合并类型或历史记录丢失 |
| `devops_abandon_pull_request` | 写入 | 放弃拉取请求而不合并 |
| `devops_vote_pull_request` | 写入 | 投票(10 赞成,5 赞成并带有建议,0 重置,-5 等待中,-10 拒绝) |
| `devops_tag_pull_request` | 写入 | 为拉取请求添加标签/标记 |
| `devops_link_work_items_to_pull_request` | 写入 | 将 Azure Boards 工作项链接到拉取请求 |
| `devops_list_pull_request_threads` | 默认 | 列出拉取请求上的评论线程 |
| `devops_get_pull_request_thread` | 默认 | 获取具有评论的单个评论线程 |
| `devops_create_pull_request_thread` | 写入 | 启动评论线程 - 常规或通过线程上下文内联到文件/行 |
| `devops_set_pull_request_thread_status` | 写入 | 设置线程的状态(`active`、`fixed`、`wontFix`、`closed`、`byDesign`、`pending`) |
| `devops_add_pull_request_comment` | 写入 | 回复现有的评论线程 |
| `devops_update_pull_request_comment` | 写入 | 编辑现有评论的文本 |
| `devops_list_pull_request_iterations` | 默认 | 列出拉取请求的历史记录(推送历史) |
| `devops_get_pull_request_changes` | 默认 | 列出 PR 历史记录的更改文件(路径 + 更改类型) |
### 工作项(10 种工具)
| 工具 | 防护 | 描述 |
|---|---|---|
| `devops_get_work_item` | 默认 | 通过 ID 获取单个工作项 |
| `devops_list_work_items` | 默认 | 按 ID 批量获取最多 200 个工作项 |
| `devops_query_work_items` | 默认 | 使用 WIQL 查询工作项,自动获取完整详细信息 |
| `devops_create_work_item` | 写入 | 创建新的工作项(大文本字段默认保存为 markdown;`format=html` 退出) |
| `devops_update_work_item` | 写入 | 更新现有工作项的字段(大文本字段默认保存为 markdown;`format=html` 退出) |
| `devops_update_work_item_tags` | 写入 | 添加和/或删除现有工作项上的标签(不区分大小写匹配;与 `devops_update_work_item` 的 `tags` 字段不同,此操作不会替换整个标签集) |
| `devops_add_work_item_comment` | 写入 | 添加评论到工作项(默认 markdown;`format=html` 退出) |
| `devops_update_work_item_comment` | 写入 | 更新现有工作项评论(默认 markdown;`format=html` 退出) |
| `devops_list_work_item_types` | 默认 | 列出工作项类型(例如 Bug、Task、Epic)和它们的参考名称 |
| `devops_list_work_item_fields` | 默认 | 列出工作项类型或工作项类型的所有字段的字段定义 |
### 发现(2 种工具)
| 工具 | 防护 | 描述 |
|---|---|---|
| `devops_list_projects` | 默认 | 列出组织中的项目;当项目名称未知时使用 |
| `devops_list_teams` | 默认 | 列出项目中的团队;支持 `mine=true` 过滤到身份验证用户的团队 |
### 高级安全(3 种工具)
需要为存储库启用 GitHub 高级安全功能(GHAzDo)。
| 工具 | 防护 | 描述 |
|---|---|---|
| `devops_list_advanced_security_alerts` | 默认 | 列出存储库的安全警报;按 `alert_type`(`secret`、`dependency`、`code`)、状态、严重性、规则、工具或分支过滤 |
| `devops_get_advanced_security_alert` | 默认 | 通过 ID 获取单个警报。`expand=validationFingerprint` 可以返回明文机密值 - 除非需要,否则保持未设置 |
| `devops_update_advanced_security_alert` | 写入 | 解除、重新激活或将警报标记为已修复;解除需要解除原因 |
---
## API 参考
所有工具都使用 [Azure DevOps REST API](https://learn.microsoft.com/en-us/rest/api/azure/devops/)。管道、仓库和发现工具使用 **v7.1**。工作项模式工具(`devops_list_work_item_types`、`devops_list_work_item_fields`)使用 **v7.1**。拉取请求工具(获取/列出/创建/更新/标记/链接)和工作项写入操作使用 **v7.2-preview**。高级安全警报工具使用 **v7.2-preview.1**,位于 `advsec.dev.azure.com` 主机上。
**注意:** `run_id` 和 `build_id` 共享相同的数字值 - 流水线 API 的 `run_id` 与构建 API 的 `buildId` 对于相同的运行是相同的。这使得跨 API 调用(例如,使用 `devops_list_run_logs` 获取日志 ID,然后使用相同的 `build_id` 调用 `devops_get_run_log_content`)。
### 高效日志检索
管道日志可能非常庞大且检索成本高。按最便宜到最贵的顺序工作:
1. **`devops_get_run_timeline`** - 从这里开始,用于回答“为什么失败”。返回一个紧凑的、过滤掉失败的时序,带有内联错误消息,通常只需 **零日志获取** 即可回答问题。
2. **`devops_search_run_log`** - 在进程中 grep 特定日志;仅返回匹配的行和上下文 - 非匹配内容永远不会到达模型。
3. **`devops_get_run_log_content`** - 直接读取日志文本。默认绑定到 `max_lines`(默认 500),带有 `start_line`/`end_line`/`tail` 窗口和分页信封,以分页浏览大日志或仅获取 `tail`。
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器