@yumiku/mcp-forgejo
v0.2.3
Published
Model Context Protocol (MCP) server for self-hosted Forgejo. Exposes 32 tools for repos, branches, files, issues, PRs, releases, tags, and wikis via the Forgejo REST API.
Maintainers
Readme
@yumiku/mcp-forgejo
Model Context Protocol (MCP) server for self-hosted Forgejo (and Gitea-compatible) instances. Exposes 32 tools covering repositories, branches, files, issues, pull requests, releases, tags, and wikis.
工具清单(33)
| 类别 | 工具 | 备注 |
|---|---|---|
| 身份 (1) | get_current_user | 返 {login, source: "env"\|"probe", is_admin} |
| 仓库 (5) | list_repos, get_repo, create_repo, edit_repo, delete_repo | delete_repo 需 destructive=true;list_repos 省略 owner 时 fallback 到 identity |
| 分支 (4) | list_branches, get_branch, create_branch, delete_branch | delete_branch 需 destructive=true |
| 文件 (3) | list_files, read_file, write_file | write_file 不支持删除(用 git CLI) |
| Issue (5) | list_issues, get_issue, create_issue, edit_issue, comment_issue | Forgejo 把 PR 当 issue;comment_issue 同时给 PR 评论 |
| PR (7) | list_pull_requests, get_pull_request, create_pull_request, merge_pull_request, set_issue_state, set_pull_request_state, review_pull_request | set_*_state 拆为 issue / PR 两条;review_pull_request 支持 APPROVE / REQUEST_CHANGES / COMMENT / PENDING |
| Release (3) | list_releases, get_release, create_release | |
| Tag (2) | list_tags, delete_tag | delete_tag 需 destructive=true |
| Wiki (3) | list_wiki_pages, get_wiki_page, edit_wiki_page | create/update 分流:POST /wiki/new + PATCH /wiki/page/{sub_url} |
v0.1.1 新行为(list 工具)
所有 list_* 工具现在返回统一信封:
{
"ok": true,
"data": [...],
"pagination": {
"page": 1,
"limit": 30,
"total": 237,
"has_more": true,
"next_page": 2
},
"view": "summary"
}view 字段(可选,默认 "summary"):
"summary":去除email、压缩 user/author 对象为{login, id},减小 LLM 上下文"full":返回 Forgejo 原始字段(含 email、avatar_url、bio 等)
list_issues 额外支持 type 字段(可选,默认 all):
"issue":只返回真 issue"pull_request":只返回 PR(Forgejo 把 PR 存为 issue,所以也走这个端点)"all":默认行为
错误信封:
- 默认:上游 body 不暴露(避免内网 IP / 邮箱 / token 等敏感信息泄露到 LLM 上下文)
- 设置
FORGEJO_DEBUG=1后:debug.upstream_body字段出现,便于排错
v0.1.0 → v0.1.1 升级说明
list_* 工具的响应从 {ok, data} 变为 {ok, data, pagination, view}。如果你之前直接读 result.data,仍然兼容(数组还是数组),但 data 里的对象默认字段少了——需要全字段请显式传 view: "full"。
v0.1.1 → v0.2.0 升级说明
- 新增
get_current_user工具:返{login, source: "env"|"probe", is_admin}。identity 在 startup 解析一次(不每次实时探测)。 - 新增
FORGEJO_USER配置:可选。不设 = 现状(启动GET /user探测)。设了 = 显式身份 + 探测仅作 drift 检测。 - 新增
identity概念:ToolCtx上挂identity: {login, source}和isAdmin: boolean。list_repos省略 owner 时 fallback 链变成args.owner → ctx.identity.login → ctx.defaultOwner → /api/v1/user/repos。 - 行为变更:env 设了
FORGEJO_USER后,启动时 probe 失败 不再 exit 1,改为 stderr 警告 + 用 env 值继续。这是 v0.2.0 的 breaking change——若需保留旧行为,去掉FORGEJO_USER。 - 新增 error code:
identity_unknown(get_current_user在 identity 缺失时抛)。
v0.2.0 → v0.2.1 升级说明
补丁版,无新工具、无 API 变更、无字段重命名(v0.2.0 用户的 .env 零变动)。只做:
- 代码去敏化:v0.2.0 几个 test 文件误用了真实账号
yumiko作为 fixture,已替换为通用假名alice。 - ToolCtx 重构:8 个工具文件 +
listWrapper.ts各有本地interface ToolCtx重复定义(v0.1.1 → v0.2.0 累积 9 处)。v0.2.1 抽到src/tools/_ctx.ts共享。未来加 ToolCtx 字段只需改 1 处。 - 文档澄清:README 加"USER vs DEFAULT_OWNER"专题 + 4 场景例子,避免两个字段的命名歧义(identity vs guard 兜底)。
v0.2.1 → v0.2.2 升级说明
安全补丁版,无新工具、无 API 变更、零字段重命名。修复 4 项安全漏洞 + 1 项 minimatch hardening:
- F-001 Path traversal 防御——
get_repo(owner="..", repo="admin/users")之前能被规整成/api/v1/admin/users。v0.2.2 加validateSegment+validatePathSegments拒./../控制字符。8 个工具文件 72 处 user-input path segment 调它。 - F-002
create_repoallow-list 绕过——之前create_repo不走assertRepoAllowed。v0.2.2 加FORGEJO_ALLOW_CREATE_REPO=1flag(默认 false——拒绝任何create_repo调用)。需要创建仓的用户需显式加这一行 env。 - F-004 错误 message 通用化——之前
ForgejoError.message默认含上游 body 文本(HTML/JSON 含内网 IP/邮箱/诊断)。v0.2.2 改为HTTP <status> <code-name>通用格式。body 仍存upstream.body(需FORGEJO_DEBUG=1暴露)。 - F-006 summary 实际剥 email——
stripEmail之前写了但applyView没调。v0.2.2 修——view=summary现在递归剥 email。 - F-013 minimatch
nonegate——之前 minimatch 默认支持!pat当 deny-list(!alice/private实际允许其他)。v0.2.2 加nonegate: true。
.env 影响:唯一新变量是 FORGEJO_ALLOW_CREATE_REPO(可选)。其他变量 0 变动。
v0.2.2 → v0.2.3 升级说明
卫生+剩余安全补丁。1 个 breaking change(F-015):
- F-008 Identity
verified字段 —get_current_user工具返的 envelope 多一个is_verified: boolean字段。LLM 看到is_verified: false知道 env 与 probe 不一致或 probe 失败,identity 不可全信。 - F-009 日志 sanitize — stderr 日志里的 env 值 / probe 消息控制字符(含
\n)被 strip,防止日志伪造。 - F-005 debug cap + redact —
FORGEJO_DEBUG=1模式下debug.upstream_body64KB 截断、敏感 key(email/token/password 等)替[redacted]、控制字符替?。 - F-007 DoS size cap —
buildPath拒 path > 2KB、query > 100;上游 body 64KB 截断。 - F-010 lockfile 根 name 修 —
package-lock.json根从@local/mcp-forgejo改@yumiku/mcp-forgejo。 - F-011 prepublishOnly 加 dry-run —
npm run typecheck && npm test && npm run build && npm pack --dry-run强制 tarball 校验。 - F-015 destructive 范围扩展(breaking) —
merge_pull_request/write_file/review_pull_request3 个工具必须传destructive: true才能调用。HIGH_RISK_TOOLS3 → 6。v0.2.x 用户在 ZCode/Cursor 调这 3 工具会突然失败——请通知并升级 client 调用方式。
.env 影响:0(FORGEJO_DEBUG 已存在;F-007/008/009/010 全部代码层)。
安装(ZCode / Cursor / Claude Desktop 一致)
通过 npm(推荐):
npm install -g @yumiku/mcp-forgejo或本地开发:
git clone <this-repo-url>
cd forgejo-mcp
npm install
npm run build配置
需要 4–5 个环境变量。占位符约定:<your-forgejo-host> 替换为你的实例主机名或 IP(如 forgejo.example.com),<port> 替换为实际端口(默认 3000),<owner> / <username> 替换为你的 Forgejo 用户名,<your-token> 替换为你的 Personal Access Token。
| 变量 | 必填 | 默认 | 说明 |
|---|---|---|---|
| FORGEJO_BASE_URL | ✅ | — | 例:http://<your-forgejo-host>:<port> |
| FORGEJO_TOKEN | ✅ | — | Forgejo Personal Access Token |
| FORGEJO_ALLOWED_REPOS | ❌ | '' | 逗号分隔 minimatch 模式,例:<owner>/* |
| FORGEJO_DEFAULT_OWNER | ❌ | — | list_repos 省略 owner 时的次级 fallback(identity 优先) |
| FORGEJO_USER | ❌ | — | 当前用户显式身份(不设则启动时 GET /user 探测)。设了之后:get_current_user 返它;list_repos 省略 owner 时用它。若与 token 实际归属不符,stderr 输出 drift 警告 |
| FORGEJO_DEBUG | ❌ | '' | 1 启用调试;错误信封含 debug.upstream_body |
| FORGEJO_HTTP_TIMEOUT_MS | ❌ | 15000 | HTTP 超时(毫秒) |
FORGEJO_USER vs FORGEJO_DEFAULT_OWNER
两个字段都涉及"用户名",但用途不同——把它们指向同一逻辑用户才能避免怪行为。
| 字段 | 答的问题 | 影响范围 |
|---|---|---|
| FORGEJO_USER | 我是谁(identity) | get_current_user 工具 + list_repos owner fallback(优先) |
| FORGEJO_DEFAULT_OWNER | 白名单为空时默认放行谁(guard 兜底) | 写工具的白名单匹配(patterns 空时隐式放行 <owner>/*) |
4 个典型配置:
最小配置(最常见)——只配白名单:
FORGEJO_ALLOWED_REPOS=<owner>/* # FORGEJO_USER 不设 → 启动时 GET /user 探测 # FORGEJO_DEFAULT_OWNER 不设 → guard 无兜底显式身份——避免启动时探测:
FORGEJO_USER=<owner> FORGEJO_ALLOWED_REPOS=<owner>/*get_current_user返source: "env";token 实际归属不同时会 stderr drift 警告。懒得写白名单——用
defaultOwner兜底:FORGEJO_USER=<owner> FORGEJO_ALLOWED_REPOS= # 留空 FORGEJO_DEFAULT_OWNER=<owner> # guard 隐式放行 <owner>/*不一致示例(应当避免)——
USER和DEFAULT_OWNER指向不同用户名:FORGEJO_USER=alice # 显式身份 alice FORGEJO_ALLOWED_REPOS=yumiko/* # 但白名单只要 yumiko FORGEJO_DEFAULT_OWNER=yumiko # guard 也只允 yumikoalice token 配上面 → alice 自己的仓被 guard 拒。不推荐——
USER和DEFAULT_OWNER应指向同一逻辑用户。
白名单匹配(使用 minimatch):
- 字符比较前强制 lowercase
<owner>/<repo>— 字面<owner>/*— 同 owner 下任意仓库(不跨 owner)*— 全放行- 多个 pattern 用逗号分隔,任一命中即放行
FORGEJO_ALLOWED_REPOS留空时:若设了DEFAULT_OWNER,放行<owner>/*;否则拒绝所有写
ZCode 集成
~/.zcode/cli/config.json(ZCode):
{
"mcp": {
"servers": {
"forgejo": {
"command": "npx",
"args": ["-y", "@yumiku/mcp-forgejo"],
"env": {
"FORGEJO_BASE_URL": "http://<your-forgejo-host>:<port>",
"FORGEJO_TOKEN": "<your-token>",
"FORGEJO_ALLOWED_REPOS": "<owner>/*",
"FORGEJO_DEFAULT_OWNER": "<owner>"
}
}
}
}
}或 Cursor(~/.cursor/mcp.json):
{
"mcpServers": {
"forgejo": {
"command": "npx",
"args": ["-y", "@yumiku/mcp-forgejo"],
"env": {
"FORGEJO_BASE_URL": "http://<your-forgejo-host>:<port>",
"FORGEJO_TOKEN": "<your-token>",
"FORGEJO_ALLOWED_REPOS": "<owner>/*",
"FORGEJO_DEFAULT_OWNER": "<owner>"
}
}
}
}CI 用法(SDK 子路径)
SDK 模块独立暴露,可绕过 MCP 层直接用:
import { Forgejo, ForgejoError, toEnvelope } from "@yumiku/mcp-forgejo/sdk";
const fg = Forgejo.fromEnv(); // 读 process.env
// 或:
const fg = new Forgejo({
baseUrl: "http://<your-forgejo-host>:<port>",
token: process.env.FORGEJO_TOKEN!,
allowedPatterns: ["<owner>/*"],
});
const issues = await fg.http.get("/api/v1/repos/<owner>/<repo>/issues", { state: "open" });SDK 也导出守卫 + 错误:
import { assertRepoAllowed, requireDestructive, HIGH_RISK_TOOLS } from "@yumiku/mcp-forgejo/sdk";测试
npm test132 个 vitest 用例覆盖:
errors.test.ts(7) — 13 个 ErrorCode 归一化 + 错误信封脱敏(debug on/off)guard.test.ts(11) — 白名单(大小写、多 pattern、通配符、defaultOwner 回退)+ destructive gatehttp.test.ts(18) — URL 构造、状态码 → ErrorCode 映射、getWithHeaders、parsePagination(X-Total-Count / Link)transforms.test.ts(15) — 8 种资源类型 summary 投影 + email 递归剥离list_envelope.test.ts(5) —withListEnvelopeHOF 集成(pagination + view)tools_write_happy_path.test.ts(17) — 每个写工具一个 mock fetch 验证issues_type.test.ts(3) —list_issuestype字段透传config.test.ts(6) —loadConfig读 7 个 env vars(baseUrl, token, allowedPatterns, defaultOwner, httpTimeoutMs, debug, user)identity.test.ts(5) —resolveIdentity5 个场景(env wins / drift / env+probe fail / probe only / probe fail exit)get_current_user.test.ts(4) —get_current_user工具(identity=env/probe/missing/缺 isAdmin)list_repos_fallback.test.ts(5) —list_reposowner fallback 链(owner/identity/defaultOwner/都没有/owner 优先)
安全
- 高危工具(
delete_repo/delete_branch/delete_tag)必须显式传destructive=true。 - 写操作受
FORGEJO_ALLOWED_REPOS白名单约束。 - PAT 通过环境变量注入,绝不入 git 仓库。
- 启动时
GET /api/v1/user验证 token;401/403 立即退出码 1。
License
MIT
