Skip to main content
Glama
6shihab

bitbucket-pr-review-mcp

by 6shihab

bitbucket-pr-review-mcp

一个 MCP 服务器,让语言模型能够读取 Bitbucket Cloud 的拉取请求并在其上留下评论——锚定在评论所涉及的行上,外加顶部的一条总结。

该服务器提供素材并发布文字。它不形成观点:审查由调用方模型完成,这里不包含任何审查提示词或模型凭据。

它不能做什么

它可以创建和更新评论。它不能批准、拒绝或合并拉取请求,不能写入分支或文件,也从不删除评论。

Bitbucket 不提供将评论与合并分开的权限。 你给这个服务器的凭据能够合并你的拉取请求;没有任何令牌或作用域机制能阻止这一点。阻止它的是:没有工具请求这样做,HTTP 客户端中的一个单一检查点无论请求如何构造都会拒绝它,以及——这部分不依赖于本代码是否正确——你在仓库上自行配置的分支限制。参见 ADR-0002,并阅读开始之前。

Related MCP server: Bitbucket MCP Server

开始之前

在你允许列表中的任何仓库上配置分支限制。 在 Bitbucket 中:Repository settings → Branch restrictions。限制谁可以合并到你的默认分支,并要求你的团队期望的审批。无论这个服务器做什么,Bitbucket 都会强制执行这些限制,这使得它成为本仓库中唯一在出现 bug 时仍然有效的保证。这只需要一分钟,而且它是"我们相信这段代码是谨慎的"与"它是否谨慎并不重要"之间的区别。

安装

需要 uv 和 Python 3.13。同样的三条命令适用于 Windows、macOS 和 Linux:

git clone <this repository>
cd bitbucket-pr-review-mcp
uv sync

然后告诉它可以访问哪些仓库:

cp config/repositories.yaml.example config/repositories.yaml

...并编辑它。该文件列出 workspace/repo 条目,不包含任何机密,并且应该被提交:

repositories:
  - streamstech/db-explorer
  - streamstech/lent-manager

整个工作区可以写成 jantrik/*。这是这里最宽的条目——它涵盖了你写入之后创建的仓库——所以服务器在每次启动时都会说明这一点:

repositories:
  - jantrik/*

服务器在没有列表的情况下拒绝启动。缺失列表与允许访问你的凭据能到达的每个仓库是无法区分的。比整个工作区更窄的模式(*/db-explorer、streamstech/db-*)也因同样的原因被拒绝:它们是对命名的猜测,而且会把任何以这种方式命名的东西都纳入进来。

连接你的 Bitbucket 账户

运行设置并打开它打印的链接:

uv run bb-pr-mcp --setup

它会在你自己的机器上提供一个页面——仅限回环,随机端口,一次性链接,五分钟后失效——要求输入你的 Atlassian 账户邮箱(不是你的 Bitbucket 用户名,也不是你给令牌起的名字)和一个 API 令牌。它会向 Bitbucket 验证这对凭据,并在存储任何内容之前显示你的显示名称,然后将凭据放入你的操作系统钥匙串并自行关闭。

在 https://id.atlassian.com/manage-profile/security/api-tokens 创建令牌,恰好使用以下四个作用域:

作用域

原因

read:user:bitbucket

让服务器知道哪些评论是自己的——没有它,每次重新审查都会堆积重复评论

read:repository:bitbucket

读取变更周围的文件和提交

read:pullrequest:bitbucket

读取拉取请求本身——细粒度作用域不嵌套,所以下面的写入作用域不涵盖这一点

write:pullrequest:bitbucket

发布和更新评论

不要更宽。一个还能写入仓库、管理仓库或运行管道的令牌会在表单处被拒绝,也会在启动时再次被拒绝。

设置时输入令牌的过期日期,你会在它失效前一周收到警告,而不是在审查中途遇到 401。

你永远不需要显式运行 --setup:在没有存储凭据的情况下,每个工具都会用设置 URL 而不是错误来回答。

随时检查:

uv run bb-pr-mcp --check

这会验证允许列表、凭据及其作用域,打印你以谁的身份发布评论,并以 shell 状态码退出——0 正常,1 没有可用的凭据,2 令牌作用域错误。

移除它

uv run bb-pr-mcp --forget

这会从本设备的钥匙串中删除凭据,仅此而已——令牌在 Atlassian 那边仍然存在,直到你在那里撤销它,命令也会说明这一点。下一次工具调用会给你一个新的设置链接。

这里刻意没有工具。一个删除凭据的工具就是一个拉取请求描述可以说服模型调用的工具,而且没有任何收益:想要它消失的人已经在终端前了。

模型永远看不到令牌

令牌从你的浏览器进入钥匙串,再从那里进入 Authorization 头。它永远不会是工具的参数,永远不会出现在工具的答案中,永远不会出现在错误消息中,也不在设置 URL 中——那个 URL 携带的是另一个一次性令牌,它只授予在这台机器上填写一个表单的权利。tests/test_the_token_never_reaches_the_model.py 会在所有这些地方寻找它。

如果钥匙串不可用

凭据放在操作系统钥匙串中,没有其他地方——绝不放在文件中。在 macOS 和 Windows 上这开箱即用。在 Linux 上你需要一个正在运行且已解锁的 Secret Service(gnome-keyring 或 KWallet);如果没有,服务器会停止而不是回退到文件(ADR-0003)。

运行它

服务器通过 stdio 使用 MCP 协议。将你的客户端指向它:

{
  "mcpServers": {
    "bitbucket-pr-review": {
      "command": "uv",
      "args": ["run", "--directory", "/path/to/bitbucket-pr-review-mcp", "bb-pr-mcp"]
    }
  }
}

在 Windows 上使用相同的形式,但使用 Windows 路径("C:\\path\\to\\bitbucket-pr-review-mcp")。平台之间没有其他区别。

对于 Claude Code:

claude mcp add bitbucket-pr-review -- uv run --directory /path/to/bitbucket-pr-review-mcp bb-pr-mcp

在 Docker 中运行它

镜像像其他一切一样通过 stdio 使用 MCP 协议,所以没有端口,也没有需要 up 的东西。构建它,然后用 -i 运行并与之对话。

docker build -t bitbucket-pr-review-mcp:local .

容器没有钥匙串,设置页面也无济于事:它绑定的是容器内部的回环端口,你的浏览器无法访问。因此容器化运行是被给予凭据而不是存储凭据。这是一个真正的降级——环境变量对 docker inspect 和任何能读取进程的东西都是可见的——而且这是一个决定而不是回退:没有任何东西会降级到它,两个变量都必须设置,启动时每次都会说明。参见 ADR-0007。

将凭据放在一个不在本仓库中的文件里:

BB_MCP_EMAIL=you@yourcompany.com
BB_MCP_API_TOKEN=ATATT...
BB_MCP_TOKEN_EXPIRES_ON=2027-08-24

然后检查它,并将其接入客户端:

docker run --rm \
  --env-file /path/to/env.docker \
  -v /path/to/repositories.yaml:/config/repositories.yaml:ro \
  bitbucket-pr-review-mcp:local --check
{
  "mcpServers": {
    "bitbucket-pr-review": {
      "command": "docker",
      "args": [
        "run", "--rm", "-i",
        "--env-file", "/path/to/env.docker",
        "-v", "/path/to/repositories.yaml:/config/repositories.yaml:ro",
        "bitbucket-pr-review-mcp:local"
      ]
    }
  }
}

docker-compose.yaml 将相同的标志一次性写下来:docker compose run --rm bitbucket-pr-review,从本目录读取 .env.docker(已被 gitignore)。

有几件值得知道的事情:

  • 在 Windows 上,在 -v 中使用 Windows 风格的路径(d:/path/to/repositories.yaml:/config/...)。在 Git Bash 下,在命令前加上 MSYS_NO_PATHCONV=1,否则路径会被重写。

  • 允许列表是挂载的,不是烘焙进去的。 它列出服务器可以访问的仓库;该列表属于运行镜像的人,而不是镜像本身。

  • --setup 在容器中退出码为 2,并说明可以在哪里运行设置。轮换令牌意味着用新令牌重启。

  • 容器以非 root 用户运行,只读,所有能力都被丢弃。

连接到共享服务器

共享部署——多个人,一个服务器,每个人有自己的 Bitbucket 账户——运行在单一源之后。MCP 端点和 Keycloak 都由它提供,因为签发者是一个字符串,必须在令牌、发现文档、浏览器和配置中表示相同的东西。

Claude Desktop 和 Claude Code 都不会通过 localhost 直接访问服务器。 Claude 自定义连接器是由 Anthropic 的基础设施获取的,而不是由你的机器获取的,所以你笔记本电脑上的服务器无论如何配置都是不可达的。本地回路通过 mcp-remote 进行:一个运行在你机器上的 stdio 桥接器,在你的浏览器中执行 OAuth 流程,并通过 HTTP 与服务器通信。一旦服务器有了公共 https 地址,自定义连接器就可以直接访问它,不再需要桥接器。

哪个客户端 ID 用在哪里

realm 持有三个 OAuth 客户端,因为三种不同的东西需要认证,它们不可互换。使用错误的客户端会在第一步就失败,在 Keycloak 错误页面上显示 Invalid parameter: redirect_uri——它指出的是参数而不是客户端,而且每种原因都是相同的消息。

你在配置什么

客户端 ID

密钥

自定义连接器,在 Claude 的设置中

bitbucket-pr-review

BB_MCP_CONNECTOR_CLIENT_SECRET

mcp-remote,用于 Claude Code 或 Desktop

bitbucket-pr-review-cli

无——它是公共客户端

无。此服务器自己使用它,用于 /connect

bitbucket-pr-review-web

BB_MCP_OIDC_CLIENT_SECRET

这种划分遵循回调的落点。连接器的回调是 https://claude.ai/api/mcp/auth_callback,在 Anthropic 的基础设施上,所以该客户端是机密的,其密钥存在于那里。桥接器的回调是某人笔记本电脑上的回环端口,所以该客户端根本不持有密钥——笔记本电脑配置文件中的密钥不是密钥,PKCE 才是保护回环流程的东西。将托管回调注册到公共客户端上会把机密流程交给一个无法保守任何东西的客户端,所以它没有被注册,Keycloak 会拒绝它。

将 .env.example 复制为 .env,然后启动它:

cp .env.example .env      # fill in the secrets; the defaults are the loopback stack
docker compose --profile shared up -d

这会启动 Postgres、Keycloak、nginx 和审查服务器。docker compose --profile shared ps 应该显示四个健康的容器,http://localhost:8080/mcp 应该以带有 WWW-Authenticate 头的 401 响应,该头指明 bitbucket:review 作用域——未认证的请求被拒绝正是系统在正常工作。

Claude Code

用 -s user 为每个项目注册一次桥接器:

claude mcp add bitbucket-pr-review -s user -- npx -y mcp-remote http://localhost:8080/mcp 3334 --allow-http --static-oauth-client-info "{\"client_id\":\"bitbucket-pr-review-cli\"}"

-s user 将它写入 ~/.claude.json 中的顶层 mcpServers 键,这适用于每个目录。替代方案是 -s local(仅此项目,也在 ~/.claude.json 中,在 projects 下)和 -s project(一个已提交的 .mcp.json)。不要在多个作用域注册:配置是分开的,用户作用域优先,你之后编辑的那个可能不是正在使用的那个。

检查它:

claude mcp get bitbucket-pr-review

应该报告 Scope: User config 和 Status: ✔ Connected。会话在启动时获取 MCP 服务器,所以已经运行的 Claude Code 在重启之前不会看到新注册的服务器。

给某人一个账户

realm 自带一个用户,dev / dev-only-not-for-production,这是一个开发凭据,并且明确说明了这一点。其他每个人都需要先在 Keycloak 中有一个账户才能登录——这与之后连接他们的 Bitbucket 账户是分开的事情,后者由他们自己在 /connect 完成。

在管理控制台中。 打开 http://localhost:8080/admin,使用引导管理员(来自你 .env 文件的 KC_BOOTSTRAP_ADMIN_USERNAME / KC_BOOTSTRAP_ADMIN_PASSWORD)登录,将领域选择器从 master 切换为 streamstech,然后选择 用户 → 添加用户。填写用户名、电子邮件、名字和姓氏,勾选 电子邮件已验证,然后创建。接着 凭据 → 设置密码,并关闭 临时 选项,除非你希望他们在首次登录时被提示修改密码。

或者从命令行,这样更容易重复:

docker exec bitbucket-pr-review-mcp-keycloak-1 /opt/keycloak/bin/kcadm.sh \
  config credentials --server http://localhost:8080 --realm master \
  --user admin --password "$KC_BOOTSTRAP_ADMIN_PASSWORD"

docker exec bitbucket-pr-review-mcp-keycloak-1 /opt/keycloak/bin/kcadm.sh \
  create users -r streamstech \
  -s username=somebody -s email=somebody@example.com -s emailVerified=true \
  -s firstName=Some -s lastName=Body -s enabled=true

docker exec bitbucket-pr-review-mcp-keycloak-1 /opt/keycloak/bin/kcadm.sh \
  set-password -r streamstech --username somebody --new-password 'their-password'

在 Git Bash 下,需要为每条命令加上 MSYS_NO_PATHCONV=1 前缀,否则 /opt/keycloak/... 会被改写为 Windows 路径,docker exec 会报告文件不存在。

有两件事很容易出错,而且都是在 登录 时失败,而不是在创建时,错误信息并不会指出原因:

  • 名字和姓氏是必需的。 Keycloak 的用户资料将其视为必填项,因此没有这些信息创建的账户在认证时会报 invalid_grant: Account is not fully set up。创建账户时不会有任何警告。

  • 不带 -t 的 kcadm.sh set-password 已经是永久密码;加上 -t 会使其变为临时密码,并留下相同的必需操作待处理。

无需角色或组成员身份。新账户会自动获得 default-roles-streamstech,这就足够了——此服务器基于 bitbucket:review 范围进行授权,该范围在 OAuth 流程中被请求并同意,而不是预先授予。

账户存储在 Keycloak 的数据库中,该数据库是一个卷。它们可以存活于重启,但无法在 docker volume rm bitbucket-pr-review-mcp_keycloak-db 后存活。

Claude Desktop

Claude Desktop 没有 mcp add 命令;你需要手动编辑 claude_desktop_config.json。其位置取决于 Claude 的安装方式:

安装方式

路径

Windows

%APPDATA%\Claude\claude_desktop_config.json

Windows, Microsoft Store

%LOCALAPPDATA%\Packages\Claude_*\LocalCache\Roaming\Claude\claude_desktop_config.json

macOS

~/Library/Application Support/Claude/claude_desktop_config.json

Store 路径是最容易让人犯错的地方——从 Store 安装的版本会完全忽略 %APPDATA% 文件,编辑错误的文件不会产生任何变化,也不会报错。

{
  "mcpServers": {
    "bitbucket-pr-review": {
      "command": "cmd",
      "args": [
        "/c", "npx",
        "-y", "mcp-remote",
        "http://localhost:8080/mcp",
        "3335",
        "--allow-http",
        "--static-oauth-client-info", "{\"client_id\":\"bitbucket-pr-review-cli\"}"
      ]
    }
  }
}

deploy/claude_desktop_config.example.json 包含相同的内容。其中有两处细节起着实际作用:

  • 在 Windows 上,npx 前需加 cmd /c。 Claude Desktop 不会通过 shell 生成进程,因此裸的 "command": "npx" 会解析为一个批处理文件,而其自身路径中包含空格,整个命令会以 'C:\Program' is not recognized as an internal or external command 的错误在日志中失效。在 macOS 上,去掉 cmd 和 /c,使用 "command": "npx"。

  • 端口使用 3335 而不是 3334。 该数字是桥接器自身的回环端口,而 Claude Code 的注册已使用 3334。两个桥接器使用同一端口意味着后启动的那个无法接收其 OAuth 回调。领域注册了 http://127.0.0.1:*/oauth/callback,因此任何空闲端口都可以。

完全重启 Claude Desktop——从托盘完全退出,因为关闭窗口后它仍在运行——然后检查它是否正常启动:

tail -f "$LOCALAPPDATA/Packages/Claude_*/LocalCache/Roaming/Claude/logs/mcp-server-bitbucket-pr-review.log"

Proxy established successfully between local STDIO and remote StreamableHTTPClientTransport 是表示桥接器正在与服务器通信的行。Server transport closed unexpectedly 表示它已退出——原因就在其上几行。

第一次工具调用会打开 Keycloak 登录(开发领域的 dev / dev-only-not-for-production,或你上面创建的账户)。登录后,工具调用会返回一个 /connect 链接,你可以在那里连接你的 Atlassian 账户——该页面也会让浏览器登录,因此该链接在记录中可见是安全的。

一些值得了解的事项:

  • 桥接器是 公开 的 OAuth 客户端,没有密钥。 笔记本电脑上配置文件中的客户端密钥并不是秘密;PKCE 才是保护回环流程的机制。

  • 其回调是 http://127.0.0.1:3334/oauth/callback——使用 IP 字面量而不是 localhost,并且使用 /oauth/callback 而不是 Claude Code 的 /callback。领域注册了所有这些,因为弄错会在流程的最后一步失败。

  • 服务器使用纯 http 时需要 --allow-http。真正的部署使用 https,而此服务器除了回环地址外,拒绝通过 http 描述自身。

  • 3334 参数是桥接器自身的端口,领域在该端口上注册回调。两个 Claude 客户端同时桥接时需要不同的端口。

  • shared 配置文件的 默认值 是开发配置——引导管理员的密码位于 docker-compose.yaml 中,领域包含一个密码位于领域文件中的用户,以及回环上的纯 http。所有这些都有默认变量,因此真正的部署会在 .env 中覆盖它们,而不是编辑任一文件。参见 docs/deploying-the-shared-server.md。

  • 更改源需要重新导入领域。 领域只导入一次,进入 Keycloak 的数据库;--import-realm 不会改动已有的领域。按名称删除该卷——docker volume rm bitbucket-pr-review-mcp_keycloak-db——且切勿使用 down -v,那会连同凭据保险库一起删除。

运维

uv run bb-pr-mcp --health
uv run bb-pr-mcp --rotate-key /path/to/new.key

--health 报告部署是否适合运行——TLS、保险库密钥、存储、允许列表、授权服务器是否可达,以及有多少人连接——退出码 0 表示健康,1 表示需要查看,2 表示无法启动。

--rotate-key 会用新密钥重新密封所有已存储的凭据,无需任何人重新注册,然后告诉你其余操作的顺序。

在任何真实环境中运行之前,请阅读 docs/deploying-the-shared-server.md。 它说明了主机被攻破一次的成本——这比看起来要大——以及应该如何处理。

谁已连接,以及如何移除某个人

uv run bb-pr-mcp --who
uv run bb-pr-mcp --revoke alice@streamstech.com

--who 列出所有已连接 Bitbucket 账户的人:不透明 ID、Atlassian 电子邮件、连接时间以及令牌过期时间。它会解密保险库来回答,然后打印除一个值得解密查看的字段之外的所有内容。

--revoke 删除某人的已存储凭据。它接受电子邮件或足够明确的不透明 ID,并且当名称匹配两个人时会拒绝猜测而不是猜测。撤销在下次工具调用时生效,包括对已经运行的服务器——共享服务器会直接读取凭据而不是持有它,正是为了让另一个终端中的操作员无需等待重启。

它 没有 做的部分值得一读。某人离开后,有三个地方会存有内容,而此命令只负责其中一个:

  • 这里。 已存储的凭据被删除。

  • Keycloak。 他们仍然可以登录并连接新令牌。在那里禁用他们的账户以阻止此行为。

  • Atlassian。 他们的 API 令牌仍然存在并且在其他任何地方仍然有效。只有他们本人或 Atlassian 管理员才能撤销它。

该命令每次都会说明这三项,因为在第一步之后就被勾选的离职检查清单比没有清单更糟糕。

两者都不是工具,这是有意为之:拉取请求描述不能说服 Caller 断开同事的连接。

工具

工具

作用

bitbucket_get_pull_request

标题、状态、作者、分支以及 Review Basis

bitbucket_get_pull_request_changes

每个更改的文件及计数,标记二进制、生成文件和锁文件条目

bitbucket_get_pull_request_diff

完整差异,或单个文件——带有锚点侧边栏显示每行的编号

bitbucket_get_pr_comments

现有对话,带有锚点、过时标记,以及哪些评论是服务器自身的

bitbucket_add_pr_comment

发布一条发现或完整审查;在发送任何内容之前完全验证

bitbucket_update_pr_comment

发布或更新一条摘要评论

bitbucket_get_repository

元数据和默认分支

bitbucket_get_file

允许列表仓库中的任何文件,在指定 ref 下

bitbucket_get_directory

目录列表,在指定 ref 下

bitbucket_get_commits

某个 ref 或拉取请求的历史

bitbucket_search_code

在允许列表仓库内进行代码搜索

拉取请求由一个字符串命名:要么是 Bitbucket URL,要么是简写形式 workspace/repo/id。

配置

以下所有项都有可用的默认值。你可以在环境变量或 .env 文件中设置,全部以 BB_MCP_ 为前缀:

设置

默认值

作用

BB_MCP_REPOSITORIES_FILE

config/repositories.yaml

允许列表所在位置

BB_MCP_LOG_LEVEL

INFO

日志输出到 stderr,绝不输出到 stdout

BB_MCP_LOG_JSON

false

每行一个 JSON 对象,用于发送给聚合器

BB_MCP_REQUEST_TIMEOUT_SECONDS

30

每次请求的超时时间

BB_MCP_MAX_DIFF_CHARACTERS

60000

差异响应上限

BB_MCP_MAX_FILE_CHARACTERS

40000

文件响应上限

BB_MCP_MAX_CHANGED_FILES

300

清单行数

BB_MCP_MAX_DIRECTORY_ENTRIES

200

目录行数

BB_MCP_MAX_COMMITS

50

提交行数

BB_MCP_MAX_SEARCH_RESULTS

25

搜索匹配

BB_MCP_MAX_COMMENTS

200

每个拉取请求读取的评论数

还有两个设置存在,但在共享部署完成之前为空——上面描述的按设备安装不需要它们,因为 stdio 只有一个调用者:

设置

作用

BB_MCP_PUBLIC_URL

Claude 所连接的地址,必须与输入到连接器中的内容完全一致。令牌必须以它作为 audience 名称。

BB_MCP_OIDC_ISSUER

签发这些令牌的 Keycloak realm。它必须与 realm 发现文档中的 issuer 完全匹配——末尾多一个斜杠都算差异。

BB_MCP_VAULT_FILE

存放个人凭据的位置。使用 BB_MCP_VAULT_KEY 加密;该变量不算设置项,因为它不能与数据一同存放在 .env 文件中。

BB_MCP_OIDC_CLIENT_ID

此服务器用于用户登录的 Keycloak client,以便收集 API 令牌的页面可以询问他们是谁。

BB_MCP_OIDC_CLIENT_SECRET

该 client 的密钥。连接页面必需;没有它,没有凭据的调用者会被告知设置不可用,而不是被引导到某个无用的地方。

每个上限在触顶时都会在响应中明确说明。截断绝不静默:无法区分截断 diff 与完整 diff 的 Caller,会将缺失的那一半当作没有问题来审查。

开发

uv run pytest                    # the suite
uv run pytest --cov=src          # with coverage
uv run ruff check src tests      # lint
uv run ruff format src tests     # format

测试从不接触网络。接缝只在 HTTP 传输层,别无其他,因此 guard、allowlist 和每一个响应读取器都会作为生产代码被实际执行。tests/recorded/ 存放着从真实 pull request 捕获的响应——为什么这一点比听上去更重要,请参阅 docs/architecture.md。

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables AI assistants to interact with Bitbucket Cloud repositories, allowing users to manage pull requests, comments, tasks, and branches through natural language commands.
    4,764 npm
    1
    MIT
  • A
    license
    A
    quality
    D
    maintenance
    Enables management of Bitbucket Cloud pull requests through natural language, including creating, reviewing, approving, and commenting on PRs with automatic default reviewer support.
    79
    1
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables AI assistants to programmatically manage Bitbucket Cloud resources, including pull requests, repositories, and branches, automating code review workflows.
    11 npm
    MIT