mcpField
F
Forgejo / Codeberg / Gitea

Forgejo / Codeberg / Gitea

本地服务
codeberg.org版本控制11 天前更新

关于 Forgejo / Codeberg / Gitea

面向 Claude Code、Claude Desktop 等 MCP 客户端的 Rust 服务,可通过 Forgejo、Codeberg 和 Gitea REST API 读取用户、仓库及协作记录。默认只读;配置独立写入令牌并启用限时写入模式后,才开放受保护的修改操作。

主要功能

  1. 01读取用户、仓库、议题和拉取请求
  2. 02搜索组织、通知、评论与评审
  3. 03查看 Actions 工作流运行
  4. 04限时启用受保护的写入操作
  5. 05通过 REST API 连接 Forgejo 和 Gitea

适用场景

  1. 01查找仓库、议题或拉取请求
  2. 02检查 Actions 持续集成运行状态
  3. 03在启用写入模式后创建议题或合并请求

MCP 相关

该项目是通过 stdio 传输、仅提供工具的 MCP 服务,供 MCP 客户端调用。它连接 Forgejo、Codeberg 或 Gitea 实例,不提供资源或提示词。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

暂未收录安装方式

我们还没有获取到这个服务的安装包或远程地址,请按项目 README 中的说明安装。

代码仓库
安装包
cargoforgejo-mcp-rs0.26.0

工具

还没有获取到工具列表

本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。

详细介绍

forgejo-mcp-rs

CI

适用于 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否autoforgejo、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。

两种代码托管平台的差异,以及服务对此的处理方式:

ForgejoGitea处理方式
按 git 引用筛选运行ref,完整限定名称branch,裸名称已转换;refs/heads/main 在两者上均有效
按工作流筛选运行workflow_id 查询参数单独的 …/actions/workflows/{file}/runs 路径已转换
运行结果仅 statusstatus(阶段)加 conclusion已规范化:将 conclusion 提升为 status,并按原样保留
运行字段名称commit_sha、prettyref、index_in_repo、title、created、trigger_eventhead_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 ContentForgejo 会直接传回运行信息;在 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 则用于向另一个实例进行身份验证——也就是你要从中复制内容的实例。

  1. 在源实例(其“Settings” → “Applications”)上创建令牌,而不是在你自己的实例上创建。
  2. 仅授予读取范围——read:repository,如果要迁移议题和 PR,还要授予 read:issue。它永远不需要写入权限:迁移只会从源读取数据。在 Forgejo 和 Gitea 上,还要将其仓库访问权限设为所有仓库,而不只是公开仓库,否则它将无法访问私有源。
  3. 在目标实例的服务中,将该令牌设为 FORGEJO_MIGRATE_TOKEN,并与其他令牌放在同一个 env 块中:该服务会请求目标实例执行复制,目标实例再将令牌提交给源实例。
  4. 调用 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_branchesreadowner/repo 中的分支(自动分页,精简为名称、提交和受保护状态)
list_branch_protectionsread仓库的保护规则:针对分支名称或 glob 规则,各规则会强制执行哪些要求(require_signed_commits、直接推送、审批、状态检查)。不包含允许列表;空列表表示没有任何分支受保护
create_branchwrite创建分支(owner/repo/new_branch,可选 old_ref)
delete_branchwrite删除分支(owner/repo/branch)。代码托管平台会拒绝删除默认分支和受保护分支
list_commitsread某个引用上的提交(owner/repo,可选 sha 和 path;自动分页)。精简为 sha、主题、作者、日期和签名状态——原始对象包含 base64 编码的 signature/payload 数据块,以及每次提交的 files/stats
get_commitread单个提交(owner/repo/sha),字段相同。signature 的值为 verified、unverified 或 unsigned;前者包含 signer,后者包含 reason
get_commit_statusesread针对某次提交/分支/标签的各项状态:上下文、状态、说明、target_url。get_combined_status 会将这些状态汇总为一个判定
get_combined_statusread某个引用汇总后的 CI 判定——一个状态,以及每项检查的最新状态。用于确认“CI 是否通过?”的工具;get_commit_statuses 则提供状态变更历史
议题
工具说明
list_issues / get_issuereadowner/repo 中的议题(默认仅显示未关闭的),不包含代码托管平台也会列为议题的拉取请求。可按 labels、milestones、author、assignee、mentioned、search 和 since/before 筛选;如果标签或里程碑名称未知,则会拒绝请求,因为代码托管平台会列出所有内容。sort(oldest、recentupdate、mostcomment、nearduedate、……)在 Forgejo 上可用;Gitea 的议题列表不支持排序,因此会拒绝该参数。精简字段包括:状态、作者、标签、受指派人、里程碑、评论数和日期;get_issue 还会返回正文
list_issue_commentsread议题/PR 的评论(精简版)
create_issuewrite创建议题(owner/repo/title,可选 body)
edit_issuewrite原地编辑议题(owner/repo/index):state 为 open/closed、title、body、assignees、milestone、due_date、labels(名称)。只修改传入的字段;也适用于拉取请求的编号,因此 state: closed 会在未合并的情况下关闭 PR
comment_on_issuewrite在议题/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_releasesread仓库的发布版本,按时间从新到旧排列(自动分页),精简为标识信息以及 asset_count 和 asset_names
get_releaseread按 Git 标签查找一个发布版本——让发布脚本具备幂等性的查询。返回相同的精简字段,另加说明和 target_commitish
list_release_assetsread按 release_id 列出发布版本附带的文件(id、名称、大小、下载 URL)
create_releasewrite在标签上创建发布版本(tag_name;如果标签不存在,target_commitish 会创建该标签)
edit_releasewrite按 release_id 就地编辑发布版本——可修改 body(说明)、name、tag_name、draft、prerelease。只会发送传入的字段,因此省略某个字段就会保持其原样
upload_release_assetwrite将本地文件附加到发布版本。仅限 FORGEJO_UPLOAD_ROOT 指定的范围;未设置时会完全禁用
delete_release_assetwrite按 attachment_id 删除一个附件——替换同名文件时需要用到,否则 Forgejo 会将新旧文件并列保留
推送镜像
工具说明
list_push_mirrorswrite列出仓库的推送镜像(仅限管理员;不会返回密钥)
add_push_mirrorwrite将仓库自动推送到外部远程端(例如 GitHub 镜像);凭据来自 FORGEJO_MIRROR_TOKEN,或使用 use_ssh=true
delete_push_mirrorwrite按 remote_name 移除推送镜像
sync_push_mirrorswrite立即触发推送镜像同步
分页与精简结果

读取列表的工具接受可选参数 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 的拼写相同:

内容ForgejoGitea此服务端
运行计数index_in_reporun_numberrun_number
标题titledisplay_titletitle
结果statusconclusionstatus
阶段—status合并到 status
工作流文件workflow_idpath 中的文件部分workflow
引用prettyrefhead_branch,或 path 中的引用ref
提交commit_shahead_shacommit_sha
触发者trigger_usertrigger_actor、actor丢弃(包含电子邮件)
时间started、stopped、createdstarted_at、completed_at、created_atstarted、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,因此在两者上判断“是否通过?”都只需看一个字段。

最后一列中的输出字段名是此服务端自己的字段名,不是任一代码托管平台的字段名。不要将其视为网络传输格式。

请求也有差异

未知查询参数会被忽略,而不是拒绝;因此,使用错误的拼写会返回未筛选的结果,而不是报错:

筛选条件ForgejoGitea
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 都有一个对方没有的最后提交字段:

ForgejoGitea
上次更改时间last_commit_whenlast_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

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。