关于 Forgejo / Codeberg / Gitea
面向 Claude Code、Claude Desktop 等 MCP 客户端的 Rust 服务,可通过 Forgejo、Codeberg 和 Gitea REST API 读取用户、仓库及协作记录。默认只读;配置独立写入令牌并启用限时写入模式后,才开放受保护的修改操作。
主要功能
- 01读取用户、仓库、议题和拉取请求
- 02搜索组织、通知、评论与评审
- 03查看 Actions 工作流运行
- 04限时启用受保护的写入操作
- 05通过 REST API 连接 Forgejo 和 Gitea
适用场景
- 01查找仓库、议题或拉取请求
- 02检查 Actions 持续集成运行状态
- 03在启用写入模式后创建议题或合并请求
MCP 相关
该项目是通过 stdio 传输、仅提供工具的 MCP 服务,供 MCP 客户端调用。它连接 Forgejo、Codeberg 或 Gitea 实例,不提供资源或提示词。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
我们还没有获取到这个服务的安装包或远程地址,请按项目 README 中的说明安装。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
forgejo-mcp-rs
适用于 Forgejo、Codeberg 和 Gitea 的 Model Context Protocol 服务。它允许 MCP 客户端(Claude Code、Claude Desktop 等)通过 Forgejo/Gitea REST API 读取你的代码托管平台信息,包括已认证用户、仓库、议题和拉取请求。
状态:默认只读,可选择启用受保护的写入操作(自 v0.2 起)。 提供代码托管平台范围内的读取工具,涵盖用户、仓库、议题、拉取请求、搜索、组织、通知、评论、评审和 Actions(CI)运行;此外还提供受保护的写入操作(
create_repo、edit_repo、create_branch、delete_branch、create_issue、edit_issue、comment_on_issue、create_pull_request、create_pull_request_review、merge_pull_request、create_or_update_file、delete_file、delete_repo、migrate_repo、推送镜像管理、dispatch_workflow、cancel_workflow_run和rerun_workflow)。这些操作需要单独的写入令牌,并且必须主动启用有时限的写入模式。完整设计请参阅SPECIFICATION.md。
它通过自研的小型客户端(src/forgejo/client.rs,使用
src/mcp_core/ 传输层)直接调用 Forgejo REST API——这是基于公开文档 API 的独立实现,并非移植自其他服务。信任链中没有第三方代码托管平台 SDK,因此,持有你令牌的工具代码可以从头到尾阅读和审计。
该服务使用 MCP 协议版本 2026-07-28(自 v0.16 起),并可协商降级至
2024-11-05,因此较旧的客户端无需更改即可继续使用。它通过 stdio 运行,且仅提供工具——不提供资源、提示词、采样或根。
构建
cargo build --release # target/release/forgejo-mcp-rs
cargo install --path . # install to ~/.cargo/bin
可在日常开发和使用的 macOS 上运行,也可在 Linux 上构建并运行(Glama 使用 Debian 构建并检查此服务)。Windows 尚未测试。
Woodpecker CI
本 crate 曾附带一个用于 Woodpecker CI 的配套服务,从 v0.13.0 到 v0.17.0 期间以第二个 woodpecker-mcp 二进制文件的形式提供。该服务现已迁至独立的仓库和 crate: woodpecker-mcp。
Woodpecker 是一个独立系统——它既能驱动 Forgejo,也能驱动 Gitea、GitHub、GitLab 和 Bitbucket,而且该服务从未调用 Forgejo API——因此将它与此处打包在一起,会让所有未运行 Forgejo 的用户都无法发现它。迁移没有改变任何功能;如果你之前使用此 crate 中的 woodpecker-mcp 二进制文件,请改为从新的 crate 安装,并将 MCP 客户端指向新的路径。
配置
服务通过环境变量配置:
| 变量 | 必填 | 默认值 | 含义 |
|---|---|---|---|
FORGEJO_TOKEN_READ_ONLY | 是 | — | 只读令牌(或 FORGEJO_TOKEN)。只读权限范围已足够。 |
FORGEJO_TOKEN_WRITE | 否 | — | 具有写入/删除权限范围的令牌。提供此令牌即可启用写入工具;若要使用纯只读服务,请省略。 |
FORGEJO_WRITE_MINUTES | 否 | 10 | 写入模式的默认时间窗口(分钟,最长 60 分钟)。 |
FORGEJO_MIRROR_TOKEN | 否 | — | add_push_mirror 作为远程端密码发送的凭据(例如 GitHub PAT)。此凭据不会进入对话,也绝不会作为工具参数传递。如果镜像只使用 use_ssh=true,请省略。 |
FORGEJO_MIGRATE_TOKEN | 否 | — | migrate_repo 向其读取内容的源实例发送的凭据。此凭据同样绝不会作为工具参数传递。它特意与 FORGEJO_MIRROR_TOKEN 分开设置:后者用于向推送目标进行身份验证,若共用一个变量,就会把凭据发送到未签发该凭据的主机。如果只迁移公开仓库,请省略。 |
FORGEJO_UPLOAD_ROOT | 否 | — | upload_release_asset 可读取文件的目录。未设置此变量时,上传功能会被禁用——请参阅发行版资源。 |
FORGEJO_UPLOAD_MAX_MB | 否 | 100 | 单个上传资源的大小上限(MiB)。文件会读入内存后再发送。 |
FORGEJO_URL | 否 | https://codeberg.org | 实例的基础 URL。 |
FORGEJO_FLAVOR | 否 | auto | forgejo、gitea,或根据实例版本自动检测的 auto。只有 Actions(CI)工具会使用此变量——请参阅 Forgejo 和 Gitea。 |
在 Codeberg → Settings → Applications(或实例中相应的位置)创建令牌。对于读取工具,只读权限范围(read:repository、read:issue、read:user)即可。写入令牌需要 write:repository(包括删除权限,以及仓库管理员级别的推送镜像端点权限)。
必须提供只读令牌:服务不会仅凭写入令牌启动;而且只读令牌必须与 FORGEJO_TOKEN_WRITE 是不同的令牌——不能复用写入令牌来执行读取操作。
Forgejo 15+ 令牌权限范围。 Forgejo 15.0 收紧了许多仓库 API 的授权要求,使其与细粒度、限定仓库范围的访问令牌保持一致。经典的宽泛令牌不受影响,但如果创建的是限定仓库范围的令牌,它必须实际包含上述权限范围——尤其是推送镜像工具所需的仓库管理员权限,否则会返回
403。令牌必须涵盖你计划访问的每个仓库。
Forgejo 和 Gitea
Forgejo 最初是 Gitea 的分支项目,两者的 REST 接口仍几乎完全相同。此服务调用的约 25 个端点中,只有 Actions(CI)端点有所不同。 Issues、拉取请求、差异、PR 文件、分支、文件内容、搜索、组织、通知、推送镜像和迁移,在两者中的路径、方法和响应结构都相同,因此无需特殊处理,也不会进行特殊处理。
服务会在首次需要时,从实例的 GET /version 延迟检测其类型——只有 Actions 工具会触发检测。如果实例检测结果有误,可将 FORGEJO_FLAVOR 设为 forgejo 或 gitea,以固定类型。
此检测方式看起来有些反常,这是有意为之:Forgejo 会在自己的版本字符串中写入 Gitea 的名称(Codeberg 报告的版本为
16.0.0-dev-785-465b60ef+gitea-1.22.0),以表明 API 兼容性;而 Gitea 从不在版本字符串中写入自己的名称(29.0.0+dev-160-g55186ca39b)。因此,出现gitea-标记表示 Forgejo,没有该标记则表示 Gitea。主版本号无法作为判断依据:Gitea 已脱离 1.x 版本线,而在 v0.21.0 之前,这曾导致 gitea.com 被误判为 Forgejo。
两种代码托管平台的差异,以及服务对此的处理方式:
| Forgejo | Gitea | 处理方式 | |
|---|---|---|---|
| 按 git 引用筛选运行 | ref,完整限定名称 | branch,裸名称 | 已转换;refs/heads/main 在两者上均有效 |
| 按工作流筛选运行 | workflow_id 查询参数 | 单独的 …/actions/workflows/{file}/runs 路径 | 已转换 |
| 运行结果 | 仅 status | status(阶段)加 conclusion | 已规范化:将 conclusion 提升为 status,并按原样保留 |
| 运行字段名称 | commit_sha、prettyref、index_in_repo、title、created、trigger_event | head_sha、head_branch、run_number、display_title、created_at、event | 已规范化为统一格式 |
| 工作流文件和引用 | 分开:workflow_id 和 prettyref | 合并为 path,例如 ci.yml@refs/heads/main | 已拆分;引用会缩短(main、v0.16.0),拉取请求引用则保持完整 |
| 未知工作流筛选条件 | 空列表 | 404 workflow "x.yml" not found | 按原样显示;工具说明会指出各自对应的情况 |
workflow_dispatch 回复 | 已创建的运行(return_run_info) | 204 No Content | Forgejo 会直接传回运行信息;在 Gitea 上,工具会返回确认信息,并告知调用方使用 list_workflow_runs 查找该运行 |
| 工作流目录 | .forgejo/workflows、.github/workflows | .gitea/workflows、.github/workflows | 在 dispatch_workflow 工具说明中提及 |
| 运行的作业 | 裸数组;没有时间戳或步骤 | { jobs, total_count },包含时间戳和步骤 | 已解包并按运行信息的方式规范化;Forgejo 未提供的内容仍保持缺失 |
| 按分支筛选拉取请求 | base、head(Forgejo 16+) | 仅 base_branch | 已转换;在 Gitea 上,通过遍历列表来应用 head。每个结果都会同时针对两者进行检查,因为 Forge 会忽略它不支持的筛选条件 |
| 取消和重新运行的回复 | 204 No Content | 运行信息(200/201);重新运行作业时则返回作业信息 | 有回复时使用其中的运行信息,否则重新读取;按 list_workflow_runs 的格式报告 |
| 重新运行单个作业 | …/actions/jobs/{job_id}/rerun | …/actions/runs/{run}/jobs/{job_id}/rerun | 已转换;两者都需要 run_id |
| 取消已完成的运行 | 204,保持不变 | 未验证;其规范列出 409 Conflict | 错误按原样显示;成功时,返回运行信息中的 status 会说明发生了什么 |
刻意进行转换,而不是同时发送两种写法:未知查询参数会被忽略,而不是拒绝,因此将 Forgejo 的 ref 发送给 Gitea 会悄无声息地返回未筛选的运行结果——看似应用了筛选条件,实际上却没有。
Gitea 的 path 值得单独提醒,因为这个名称具有误导性:它不是文件系统路径,而是工作流文件名、一个 @ 符号和完整限定的引用——ci.yml@refs/heads/main 或 test-pr.yml@refs/pull/1117/head。它也是唯一可靠的引用来源,因为 head_branch 在分支运行中会被填充,在标签和拉取请求中则为 null。
无论使用哪种类型,还有两点需要注意。即使是公开仓库,Gitea 的 Actions API 也需要令牌。get_workflow_run 返回的格式与 list_workflow_runs 相同,均为规范化格式。
写入模式
该服务默认只读。只有在满足以下条件时,create_repo / edit_repo / delete_repo 才会生效:(a)已配置 FORGEJO_TOKEN_WRITE,并且(b)通过 enable_write_mode 主动进入了写入模式——这是一个有时限的权限提升(默认 10 分钟,最长 60 分钟),每次写入都会延长时限,到期后自动恢复。write_status 会报告当前状态;delete_repo 还要求 confirm 参数等于 "owner/repo"。
enable_write_mode、disable_write_mode 和 write_status 都会报告其适用的 instance,权限提升说明也会在文字中标明该实例,因此提示会显示“写入模式已在 https://gitea.com/ 上启用”,而不是简单地说“写入模式已开启”。当你同时运行多个此类服务时,这一点就很重要——例如,一个连接 Codeberg,另一个连接 Gitea。它们各自拥有独立的写入模式,而没有注明实例的警告无法告诉你哪个 Forge 现在可写。write_status 刻意不会调用实例,因此在其他操作检测到实例类型之前,其 flavor 为 null。完整设计请参阅 SPECIFICATION.md。
迁移源令牌(可选)
只有在通过 migrate_repo 操作私有源时才需要。公开源无需凭据——可完全跳过此项。
请注意方向:此处其他所有变量都是用于向你的实例(FORGEJO_URL)进行身份验证。FORGEJO_MIGRATE_TOKEN 则用于向另一个实例进行身份验证——也就是你要从中复制内容的实例。
- 在源实例(其“Settings” → “Applications”)上创建令牌,而不是在你自己的实例上创建。
- 仅授予读取范围——
read:repository,如果要迁移议题和 PR,还要授予read:issue。它永远不需要写入权限:迁移只会从源读取数据。在 Forgejo 和 Gitea 上,还要将其仓库访问权限设为所有仓库,而不只是公开仓库,否则它将无法访问私有源。 - 在目标实例的服务中,将该令牌设为
FORGEJO_MIGRATE_TOKEN,并与其他令牌放在同一个env块中:该服务会请求目标实例执行复制,目标实例再将令牌提交给源实例。 - 调用
migrate_repo时设置authenticate: true。否则不会发送该令牌。
设置前请先了解此令牌会被发送到哪里。 你的读写令牌会通过
Authorization标头发送到FORGEJO_URL,不会发送到其他地方。这个令牌则不同:它会放在请求正文中,然后由目标实例代你提交给源主机。目标实例可以明文看到它。这是任何服务端迁移 API 都固有的行为,并非此服务的选择——但这意味着你也必须信任目标实例的运营方。如果源实例支持,请将令牌权限限制到单个仓库,并在迁移完成后撤销令牌。使用
mirror: true时,目标实例必须保留凭据以便持续重新拉取,因此凭据会保存在该实例的数据库中,而不是使用一次后丢弃。除非你确实需要持续拉取镜像,否则建议使用一次性迁移。
将其接入 Claude Code
claude mcp add --scope user forgejo /path/to/target/release/forgejo-mcp-rs \
--env FORGEJO_URL=https://codeberg.org \
--env FORGEJO_TOKEN_READ_ONLY=your_read_token_here
# add --env FORGEJO_TOKEN_WRITE=… only if you want the (gated) write tools
# add --env FORGEJO_MIGRATE_TOKEN=… only to migrate from a *private* source instance
或 Claude Desktop
{
"mcpServers": {
"forgejo": {
"command": "/path/to/target/release/forgejo-mcp-rs",
"env": { "FORGEJO_URL": "https://codeberg.org", "FORGEJO_TOKEN_READ_ONLY": "your_read_token_here" }
}
}
}
将 FORGEJO_URL 指向 Gitea 实例即可,其他功能均相同——系统会自动检测 flavor:
{
"mcpServers": {
"gitea": {
"command": "/path/to/target/release/forgejo-mcp-rs",
"env": { "FORGEJO_URL": "https://gitea.com", "FORGEJO_TOKEN_READ_ONLY": "your_read_token_here" }
}
}
}
日志会写入标准错误(标准输出用于 MCP 传输);可通过 RUST_LOG 调整详细程度,例如 RUST_LOG=forgejo_mcp_rs=debug。
工具
按主题分组。第二列标记需要写入模式的工具。
账户和实例
| 工具 | 备注 | |
|---|---|---|
whoami | 读取 | 已通过身份验证的用户(验证令牌) |
version | 读取 | 此 MCP 服务的版本、已连接实例的版本,以及其 flavor(forgejo 或 gitea) |
list_orgs | 读取 | 你所属的组织 |
list_notifications | 读取 | 你的通知会话,精简版(all=true 表示已读和未读) |
list_keys | 读取 | 账户的 SSH 和 GPG 密钥(省略 username 表示查询你自己的密钥)。verified 决定签名是否可以通过验证,但 Gitea 会在 SSH 密钥中省略此字段,因此字段缺失表示未报告,而非未验证。GPG 条目包含密钥涵盖的地址,以及每个地址是否已激活 |
写入模式开关
| 工具 | 备注 | |
|---|---|---|
write_status | 读取 | 报告写入模式状态(是否已配置令牌?是否已启用?还剩多少分钟?) |
enable_write_mode / disable_write_mode | 进入/退出限时写入模式 |
仓库
| 工具 | 备注 | |
|---|---|---|
list_my_repos | 读取 | 你的仓库(自动分页,精简版) |
search_repos | 读取 | 按关键词搜索仓库 |
get_repo | 读取 | 单个仓库的详情(包括默认分支、以 KiB 为单位的大小,以及 has_* 单元开关),精简版 |
create_repo | 写入 | 创建仓库(默认设为私有) |
edit_repo | 写入 | 编辑仓库设置——可见性、描述、网站、默认分支、单元开关(议题、PR、Wiki、发布、Actions、软件包、项目)、归档。只有提供的字段会更改;不支持重命名。某个单元关闭时,其整个端点系列会返回 404,而非空结果。因此,如果这些工具报告仓库缺失,但仓库明明存在,就应设置 has_releases / has_actions |
delete_repo | 写入 | 删除仓库(需要 confirm = "owner/repo") |
migrate_repo | 写入 | 从另一个实例复制仓库——唯一可以跨实例迁移议题/PR 的工具。导入完成后返回结果,并指出失败后留下的任何空仓库;不会更改源仓库;凭据来自 FORGEJO_MIGRATE_TOKEN |
文件
| 工具 | 备注 | |
|---|---|---|
get_repo_tree | 读取 | 仓库的 Git 树(可选 sha、recursive)。条目包含路径/类型/大小/sha;在确认文件不存在之前,请先检查 truncated |
get_file_contents | 读取 | 读取文件(解码文本)或列出目录(owner/repo/path,可选 ref)。可选的 start_line / end_line 用于指定从 1 开始、包含边界的行范围,并会限制在文件范围内;始终会报告 total_lines |
create_or_update_file | 写入 | 将文件创建或替换为一次提交(owner/repo/path/content,内容为 UTF-8 文本)。更新时传入 get_file_contents 返回的当前 sha;过期的 sha 会被拒绝。可选 message、branch、new_branch |
delete_file | 写入 | 通过一次提交删除文件(owner/repo/path/sha)。可选 message、branch、new_branch |
分支和提交
| 工具 | 说明 | |
|---|---|---|
list_branches | read | owner/repo 中的分支(自动分页,精简为名称、提交和受保护状态) |
list_branch_protections | read | 仓库的保护规则:针对分支名称或 glob 规则,各规则会强制执行哪些要求(require_signed_commits、直接推送、审批、状态检查)。不包含允许列表;空列表表示没有任何分支受保护 |
create_branch | write | 创建分支(owner/repo/new_branch,可选 old_ref) |
delete_branch | write | 删除分支(owner/repo/branch)。代码托管平台会拒绝删除默认分支和受保护分支 |
list_commits | read | 某个引用上的提交(owner/repo,可选 sha 和 path;自动分页)。精简为 sha、主题、作者、日期和签名状态——原始对象包含 base64 编码的 signature/payload 数据块,以及每次提交的 files/stats |
get_commit | read | 单个提交(owner/repo/sha),字段相同。signature 的值为 verified、unverified 或 unsigned;前者包含 signer,后者包含 reason |
get_commit_statuses | read | 针对某次提交/分支/标签的各项状态:上下文、状态、说明、target_url。get_combined_status 会将这些状态汇总为一个判定 |
get_combined_status | read | 某个引用汇总后的 CI 判定——一个状态,以及每项检查的最新状态。用于确认“CI 是否通过?”的工具;get_commit_statuses 则提供状态变更历史 |
议题
| 工具 | 说明 | |
|---|---|---|
list_issues / get_issue | read | owner/repo 中的议题(默认仅显示未关闭的),不包含代码托管平台也会列为议题的拉取请求。可按 labels、milestones、author、assignee、mentioned、search 和 since/before 筛选;如果标签或里程碑名称未知,则会拒绝请求,因为代码托管平台会列出所有内容。sort(oldest、recentupdate、mostcomment、nearduedate、……)在 Forgejo 上可用;Gitea 的议题列表不支持排序,因此会拒绝该参数。精简字段包括:状态、作者、标签、受指派人、里程碑、评论数和日期;get_issue 还会返回正文 |
list_issue_comments | read | 议题/PR 的评论(精简版) |
create_issue | write | 创建议题(owner/repo/title,可选 body) |
edit_issue | write | 原地编辑议题(owner/repo/index):state 为 open/closed、title、body、assignees、milestone、due_date、labels(名称)。只修改传入的字段;也适用于拉取请求的编号,因此 state: closed 会在未合并的情况下关闭 PR |
comment_on_issue | write | 在议题/PR 中发表评论(owner/repo/index/body) |
拉取请求
| 工具 | 说明 | |
|---|---|---|
list_pull_requests / get_pull_request | 读取 | owner/repo 中的拉取请求(默认仅显示开放的请求);可按 base 和 head 分支筛选(head 可以是 owner:branch),以查询某个分支是否已有开放的拉取请求;也可按 author 筛选;两种代码托管平台均支持按 oldest、recentupdate、recentclose、mostcomment 等排序。精简后的信息包括:分支、状态、head_sha、作者、标签和日期;get_pull_request 还会提供说明、审查者和合并详情 |
list_pull_request_files | 读取 | 拉取请求更改的文件(自动分页),包含每个文件的增删行数和重命名信息。Forgejo 不会在此处提供代码块,请使用 get_pull_request_diff 获取内容 |
get_pull_request_diff | 读取 | 拉取请求的统一差异。file_path 可将结果限定为一个文件(重命名前后任一名称均可匹配);否则结果会在 64 KiB 处截断,可通过 max_bytes 调高限制 |
list_pull_request_reviews | 读取 | 拉取请求的审查记录——包含批准/请求更改/评论的结论和摘要正文(内联评论以数量显示) |
create_pull_request | 写入 | 创建拉取请求(owner/repo/title/head/base,可选 body) |
create_pull_request_review | 写入 | 审查拉取请求(owner/repo/index/verdict):可选择 approve、request_changes 或 comment,并提供摘要 body 和可选的 commit_id |
merge_pull_request | 写入 | 合并拉取请求(owner/repo/index):style 可选 merge/rebase/rebase-merge/squash/fast-forward-only;还可选填标题/消息、设置 delete_branch,并提供 head_sha 防护,若 head 已变更则拒绝合并。返回拉取请求当前的状态 |
Actions(CI)
| 工具 | 说明 | |
|---|---|---|
list_workflow_runs | 读取 | owner/repo 中的 Actions(CI)运行记录;两种代码托管平台的结果均采用精简的统一格式;默认按最新优先,每页 30 条;可按 head_sha/ref/status/event/workflow_id 筛选。每条运行记录的 status 表示其结果 |
get_workflow_run | 读取 | 根据 run_id 获取一条工作流运行记录,格式与 list_workflow_runs 相同 |
list_workflow_jobs | 读取 | 获取某次运行中的作业,两种代码托管平台的结果均采用统一格式;每个作业的 id 可用于调用 get_job_log。Forgejo 16+ |
get_job_log | 读取 | 通过 HTTP Range 获取某个作业日志的一部分——默认获取最后 32 KiB(max_bytes 最高可设为 1 MiB);结果会截取到完整行,并去除颜色代码和时间戳。使用 earlier/later 获取前一个/后一个窗口。Forgejo 16+ |
dispatch_workflow | 写入 | 通过 workflow_dispatch 触发 Actions 工作流(owner/repo/workflow 文件名/ref,可选 inputs);Forgejo 返回已创建的运行记录,Gitea 返回确认信息 |
cancel_workflow_run | 写入 | 取消待处理或正在运行的记录(owner/repo/run_id);返回该记录当前的状态。Forgejo 16+ |
rerun_workflow | 写入 | 重新运行已结束的记录;也可通过 job_id 重新运行单个作业及依赖它的作业;返回运行记录。Forgejo 17+——若实例不支持此端点,会报告此情况及其版本,并且不会重新运行任何作业 |
发布版本
| 工具 | 说明 | |
|---|---|---|
list_releases | read | 仓库的发布版本,按时间从新到旧排列(自动分页),精简为标识信息以及 asset_count 和 asset_names |
get_release | read | 按 Git 标签查找一个发布版本——让发布脚本具备幂等性的查询。返回相同的精简字段,另加说明和 target_commitish |
list_release_assets | read | 按 release_id 列出发布版本附带的文件(id、名称、大小、下载 URL) |
create_release | write | 在标签上创建发布版本(tag_name;如果标签不存在,target_commitish 会创建该标签) |
edit_release | write | 按 release_id 就地编辑发布版本——可修改 body(说明)、name、tag_name、draft、prerelease。只会发送传入的字段,因此省略某个字段就会保持其原样 |
upload_release_asset | write | 将本地文件附加到发布版本。仅限 FORGEJO_UPLOAD_ROOT 指定的范围;未设置时会完全禁用 |
delete_release_asset | write | 按 attachment_id 删除一个附件——替换同名文件时需要用到,否则 Forgejo 会将新旧文件并列保留 |
推送镜像
| 工具 | 说明 | |
|---|---|---|
list_push_mirrors | write | 列出仓库的推送镜像(仅限管理员;不会返回密钥) |
add_push_mirror | write | 将仓库自动推送到外部远程端(例如 GitHub 镜像);凭据来自 FORGEJO_MIRROR_TOKEN,或使用 use_ssh=true |
delete_push_mirror | write | 按 remote_name 移除推送镜像 |
sync_push_mirrors | write | 立即触发推送镜像同步 |
分页与精简结果
读取列表的工具接受可选参数 state(open/closed/all)和 page/limit。不指定分页参数时,调用
list_my_repos / list_issues / list_pull_requests 会自动遍历全部结果,并返回 { returned, total, truncated, items } 封装;显式传入 page 或
limit 可获取单页结果,此时返回 { page, limit, returned, total, items }(对于不提供计数的 search_repos,total 为 null)。结果会精简为重要字段,写入操作的返回结构与对应的读取工具相同。写入
工具需要启用写入模式。
在不同实例间迁移仓库
migrate_repo 封装了 Forgejo 的 POST /repos/migrate,调用时需在目标实例上执行 —
clone_addr 指向源实例。推送镜像只会同步 git 引用,而此工具还可以连同议题、PR、标签、里程碑、发行版和 Wiki 一并迁移。需要了解以下三点:
- 设置
service。 默认值为git,这会进行仅复制引用的裸克隆。指定源代码托管平台(Forgejo 或 Codeberg 源需设为gitea— 没有forgejo这个值),以启用基于 API 的导入器;元数据标志依赖此导入器。 - 内容标志默认关闭。
issues、pull_requests、labels、milestones、releases、wiki和lfs均需显式启用,这与 API 本身的默认值一致。 - 迁移会在调用期间完成,而且是复制。 导入完成后,实例才会响应(Codeberg 上的小型仓库只需数秒)。导入失败时,可能会在新名称下留下一个空仓库,导致无法重试;错误信息会指出该仓库,必须先将其删除。源仓库不会被修改 — 是否停用源仓库是另一个需要明确执行的步骤。
传入 mirror: true,可将结果保留为拉取镜像,定期从源实例重新获取内容,而不是只复制一次。
对于私有源,设置 authenticate: true 后,服务端会将 FORGEJO_MIGRATE_TOKEN 作为凭据发送:对于 gitea、github、gitlab、gogs 和 gitbucket,它会作为令牌使用;对于 git(搭配 auth_username,默认值为 oauth2)、onedev 和 codebase,它会作为克隆密码使用(这些服务需要设置 auth_username)。对于令牌服务,系统会拒绝用户名,因为这些服务的导入器会忽略令牌,导致迁移失败。与推送镜像一样,令牌不会作为工具参数传递,因此不会出现在对话中。如何创建和限定令牌的权限,请参阅迁移源令牌(可选);其中还会说明令牌最终存放的位置 — 它与其他令牌的存放位置不同。
发行版资产
在构建完成前编写的说明,应通过 edit_release 修正,而不是重新创建发行版 — 删除发行版会连同其资产一并删除。该工具只会发送你传入的字段,因此更改 body 不会影响标题和草稿状态。
发布构建需要调用三次:通过 get_release 查询标签;若返回 404,则调用 create_release;然后对每个文件调用一次 upload_release_asset。只要先删除同名资产,就可以安全地重新运行 — 否则 Forgejo 会保留两个资产,而不是替换。发行版只显示资产名称;list_release_assets 会提供用于删除的 attachment_id。
upload_release_asset 是唯一一个会读取本地磁盘的工具,而它读取的任何内容都会成为可公开下载的文件。因此,该工具采用严格限制,而非默认信任:
- 默认关闭,除非完成配置。 未设置
FORGEJO_UPLOAD_ROOT时,所有上传都会被拒绝。它不会回退到工作目录:MCP 服务的当前工作目录取决于客户端从何处启动它,不能据此判断哪些内容可以发布。 - 仅限指定根目录。 路径会先经过符号链接解析,再进行检查,因此无论是
..路径遍历还是指向目录树之外的符号链接,都无法绕过限制。 - 限制大小和类型。 仅允许普通文件,大小不得超过
FORGEJO_UPLOAD_MAX_MB(默认值为 100)。 - 仅允许普通名称。 发布名称默认使用文件自身的名称,且不得包含路径分隔符,因为 Forgejo 会原样使用该名称。
请将根目录指向你实际用于发布的目录树,而不是 $HOME:
"env": {
"FORGEJO_UPLOAD_ROOT": "/Users/you/Developer",
"FORGEJO_UPLOAD_MAX_MB": "100"
}
Forgejo 与 Gitea
服务端通过一个二进制文件与两者通信,并从 GET /version 检测具体类型。它调用的约 25 个端点中,两个代码托管平台除 Actions 外都一致;但在存在差异的地方,两者的差异完全不同,因此这里会说明哪些内容会被转换,哪些不会。
以下所有内容都根据两个已发布的规范逐一定义进行核对:codeberg.org/swagger.v1.json 和
gitea.com/swagger.v1.json。标记为 实时数据 的三处内容无法从规范中得出:规范将相关字段都定义为纯字符串。
每个代码托管平台上的工作流运行都是不同的对象
Gitea 复制了 GitHub 的术语,Forgejo 则保留了自己的术语。两者的定义甚至名称都不一样——Forgejo 的 ActionRun 对应 Gitea 的 ActionWorkflowRun;在值得关注的字段中,只有 id、event 和 html_url 的拼写相同:
| 内容 | Forgejo | Gitea | 此服务端 |
|---|---|---|---|
| 运行计数 | index_in_repo | run_number | run_number |
| 标题 | title | display_title | title |
| 结果 | status | conclusion | status |
| 阶段 | — | status | 合并到 status |
| 工作流文件 | workflow_id | path 中的文件部分 | workflow |
| 引用 | prettyref | head_branch,或 path 中的引用 | ref |
| 提交 | commit_sha | head_sha | commit_sha |
| 触发者 | trigger_user | trigger_actor、actor | 丢弃(包含电子邮件) |
| 时间 | started、stopped、created | started_at、completed_at、created_at | started、stopped、created |
只有通过实时数据才能看到三处情况:
- 实时数据 —
path并非路径。 Gitea 返回ci.yml@refs/heads/main:工作流文件、@、完全限定的引用。若将其视为文件路径,就会把head当作“工作流”,这正是此服务端首次实时探测时返回的结果。 - 实时数据 — 标签和拉取请求运行的
head_branch为 null,因此必须从path中获取引用。 - 实时数据 — 未设置的值是
"",而不是 null。 正在运行的 Gitea 运行会返回conclusion: ""和completed_at: ""。将这些值解码为日期的使用方会因空字符串而失败,因此单个排队中的运行就可能导致整个列表获取失败。
Gitea 的 status 只报告 queued / in_progress / completed,运行结束后才会在 conclusion 中单独返回结果。此服务端会将 conclusion 提升到 status 中,Forgejo 本来就将结果放在该字段里;同时也会原样保留 conclusion,因此在两者上判断“是否通过?”都只需看一个字段。
最后一列中的输出字段名是此服务端自己的字段名,不是任一代码托管平台的字段名。不要将其视为网络传输格式。
请求也有差异
未知查询参数会被忽略,而不是拒绝;因此,使用错误的拼写会返回未筛选的结果,而不是报错:
| 筛选条件 | Forgejo | Gitea |
|---|---|---|
| git 引用 | ref,完全限定 | branch,裸名称 |
| 工作流文件 | workflow_id 查询参数 | …/actions/workflows/{file}/runs 路径 |
head_sha、status、event | 相同 | 相同 |
不带筛选条件时,两者的列表路径相同。dispatch_workflow 也有所不同:
return_run_info 是 Forgejo 的扩展,Gitea 会拒绝该端点请求体中的未知字段,因此只会向 Forgejo 发送此字段——这就是 Gitea 返回确认信息而不是运行信息的原因。
其他内容
问题、拉取请求、差异、PR 文件、分支、内容、搜索、组织、通知、推送
镜像和迁移在路径、方法和响应结构上都相同。每个 Forge 都有对方没有的字段,但这些只是额外字段,并非不一致之处——Forgejo 在用户对象上有 pronouns,在发布版本上有 archive_download_count;Gitea 在问题上有 time_estimate,在仓库上有 branch_count。
Actions 之外唯一真正的例外是内容端点:每个 Forge 都有一个对方没有的最后提交字段:
| Forgejo | Gitea | |
|---|---|---|
| 上次更改时间 | last_commit_when | last_author_date、last_committer_date |
| 提交消息 | — | last_commit_message |
此服务不会读取这些字段,但任何用于呈现文件列表的程序都需要支持这两种字段名称,否则其中一个 Forge 的日期列就会为空。
安全性
令牌仅从环境中读取——绝不记录日志,也绝不写入磁盘(客户端会将其保存在清零缓冲区中,并将 Authorization 标头标记为敏感信息)。默认只读,因此除非另外提供写入令牌并启用写入模式,否则此服务无法修改你的账户。工具输出是不可信的、源自仓库的文本——此服务会将其标记为数据,而非指令。请参阅 SPECIFICATION.md。
两个远程凭据——FORGEJO_MIRROR_TOKEN 和 FORGEJO_MIGRATE_TOKEN——在进程内采用相同的处理方式(仅存在于环境中、清零、绝不会作为工具参数,也绝不会返回),但它们不是标头凭据,因此无法在你与自己的实例之间保密:每个凭据都会放在请求正文中发送到你的实例,再转发给第三方主机,后者可以看到明文。这是 Forgejo 镜像和迁移 API 的工作方式,并非此服务的选择。请严格限制这两个凭据的权限,并视其为已向远程运营方披露。
质量检查
cargo fmt --all
cargo clippy --all-targets -- -D warnings
cargo test
cargo deny check # cargo install cargo-deny
CI 在 Codeberg Forgejo Actions 上运行相同的检查;已纳入版本控制的 .githooks/pre-push 钩子可在本地执行相同检查
(git config core.hooksPath .githooks)。
历史
各版本的变更记录见 CHANGELOG.md;每项变更背后的设计理由见 SPECIFICATION.md。
截至 v0.5 的版本均基于 Cyborus 开发的 forgejo-api crate 构建。forgejo-mcp-rs 现在通过自己的轻量客户端调用 Forgejo REST API,不再携带第三方 Forge SDK。
此处曾随附的 Woodpecker CI 服务,从 v0.13.0 到 v0.17.0 作为第二个二进制文件发布;从 v0.18.0 起,它已迁移到自己的仓库——
woodpecker-mcp。
许可证
MIT——详情请参阅 LICENSE.md。
MCP 注册表
MCP 注册表 的所有权验证令牌(从 crates.io 上此 crate 渲染后的 README 中读取):
注册表所有权令牌:
mcp-name: io.github.brechanbech/forgejo-mcp-rs
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。