Files
gitea-mcp/README.zh-CN.md
T
zhengchen.tao 52c46e738f docs(readme): CI 介绍句补 deploy step,与 DEPLOY_PATH bullet 对齐
上一笔 03474c8 加 vars.DEPLOY_PATH 时只动了 bullet,介绍句还是
'builds and pushes the image',与新 bullet 描述的 deploy job 不对齐。
补一句 'then optionally redeploys ... (controlled by vars.DEPLOY_PATH)'。
2026-05-20 11:51:47 +08:00

8.2 KiB
Raw Blame History

gitea-mcp

English | 简体中文

通过 MCPModel Context Protocol 协议以只读方式访问 Gitea 实例,访问由 OAuth 签发的 JWT bearer token 控制。

MCP server 内部持有一个只读的 Gitea Personal Access TokenPAT),绝不暴露给 MCP client。Client 改为用 OAuth 签发的 JWT 向本 server 认证。

支持两种 JWT 签名模式:

  • HS256(默认)—— AS 与本 server 共享对称密钥。用于自建的极简 AS。
  • RS256 —— 自动通过 <Issuer>/.well-known/openid-configuration 的 OIDC discovery 拉取 JWKS。可对接任意标准 providerLogtoZITADELKeycloakAuth0 等。

部署指引见下方 Choosing an AS

Architecture

MCP client (Claude.ai, etc.)
    │
    │ ① GET /.well-known/oauth-authorization-server  (RFC 8414)
    │ ② OAuth Authorization Code + PKCE  (against your AS)
    │ ③ Bearer JWT (aud=gitea, scope=read:gitea)
    │
    ▼
gitea-mcp /mcp
    │  JWT verify (HS256, shared key with AS)
    │  Bearer <GITEA_ADMIN_PAT>  (held server-side, never leaves the process)
    │
    ▼
Gitea REST API

Tools

工具 说明
list_repos 列出 PAT 可见的所有仓库(个人 + 组织,公开 + 私有)
read_repo 仓库元数据:topics、stars、默认分支、mirror 标记
list_tree 指定 ref 下的文件树(可递归,最多 500 条)
read_file 文件原始内容(UTF-8,按 MaxFileBytes 截断)
search_code 通过 Gitea indexer 做代码搜索(需实例开启 indexer)
list_branches 分支列表 + 每个分支的最新 commit
list_commits 最近的 commit(含 author + message
read_commit 完整 commit 详情 + per-file diff(最多 50 个文件)
list_issues 按 stateopen/closed/all)过滤的 issue 列表
read_issue issue 正文 + 所有评论
list_pulls 按 state 过滤的 PR 列表
read_pull PR 正文 + review comments + 变更文件
list_orgs PAT 可见的所有 organization
read_org organization 元数据
list_packages 按 owner 列出 registry 内的 packagescontainer/generic/npm/...
read_package package 版本元数据
list_workflow_runs Gitea Actions workflow run 历史
read_run_log run 详情 + job 列表 + 日志(按 MaxFileBytes 截断)

所有工具调用都需要带有 scope=read:gitea 的有效 JWT。

Configuration

变量 默认值 必填 说明
Gitea__BaseUrl Gitea 根 URL,结尾不带斜杠
Gitea__AdminPat Gitea 只读 PAT —— 见下方 PAT setup
Gitea__RepoBlacklist (空) 逗号分隔的 owner/repo 列表,用于屏蔽仓库
Gitea__DefaultLimit 50 list 类操作的默认分页大小
Gitea__MaxFileBytes 1048576 文件 / 日志读取上限(1 MB
Jwt__Algorithm HS256 HS256RS256
Jwt__Issuer 期望的 iss claim —— 你 AS 的 issuer URL
Jwt__Audience gitea 期望的 aud claim
Jwt__SigningKey__Current 仅 HS256 HS256 签名密钥,与你的 AS 共享
Jwt__SigningKey__Previous 轮换窗口内的旧 HS256 密钥
Mcp__OAuthDiscovery__Issuer /.well-known 中的 issuer 字段
Mcp__OAuthDiscovery__AuthorizationEndpoint 你 AS 的 /authorize URL
Mcp__OAuthDiscovery__TokenEndpoint 你 AS 的 /token URL
Mcp__OAuthDiscovery__RegistrationEndpoint 你 AS 的 /register URLDCR
Mcp__OAuthDiscovery__ResourceUrl request host RFC 9728 中的 resource 标识
ASPNETCORE_ENVIRONMENT Production Development 启用详细日志

配置 key 用 ASP.NET Core 的 double-underscore 嵌套约定(Jwt__SigningKey__CurrentJwt:SigningKey:Current)。

PAT setup (Gitea → Settings → Applications)

按"最小权限原则"生成 token仅勾选以下 scope

  • read:repository
  • read:organization
  • read:package
  • read:issue
  • read:user

不要授予 write:*admin:* 任何 scope。

PAT 一旦泄露:在 Gitea 撤销 → 生成新的 → 更新 Gitea__AdminPat 环境变量 → 重启容器。

Local development

# 1. Restore 与配置
dotnet restore
export Gitea__BaseUrl=https://gitea.example.com
export Gitea__AdminPat=<你的只读 PAT>
export Jwt__Issuer=https://your-auth-server.example.com
export Jwt__SigningKey__Current=dev-secret-key-at-least-32-chars-long
export Mcp__OAuthDiscovery__Issuer=https://your-auth-server.example.com
export Mcp__OAuthDiscovery__AuthorizationEndpoint=https://your-auth-server.example.com/authorize
export Mcp__OAuthDiscovery__TokenEndpoint=https://your-auth-server.example.com/token

dotnet run
# 监听 http://localhost:5000

# 2. 生成开发用 JWT
dotnet user-jwts create \
  --issuer https://your-auth-server.example.com \
  --audience gitea \
  --name tester \
  --claim sub=tester \
  --claim scope=read:gitea

# 3. 用 MCP Inspector 测试
npx @modelcontextprotocol/inspector
# Transport: Streamable HTTP
# URL: http://localhost:5000/mcp
# Bearer Token: <步骤 2 得到的 token>

# 4. 跑单元测试
dotnet test gitea-mcp.Tests/

Docker

docker build -t gitea-mcp .

docker run --rm -p 8080:8080 \
  -e Gitea__BaseUrl=https://gitea.example.com \
  -e Gitea__AdminPat=$GITEA_PAT \
  -e Jwt__Issuer=https://your-auth-server.example.com \
  -e Jwt__SigningKey__Current=$JWT_SIGNING_KEY \
  -e Mcp__OAuthDiscovery__Issuer=https://your-auth-server.example.com \
  -e Mcp__OAuthDiscovery__AuthorizationEndpoint=https://your-auth-server.example.com/authorize \
  -e Mcp__OAuthDiscovery__TokenEndpoint=https://your-auth-server.example.com/token \
  gitea-mcp

仓库内的 .gitea/workflows/build-image.yml 在每次推送到 main 时构建并推送镜像,并可选在 runner 主机上重启容器(由下方 vars.DEPLOY_PATH 控制)。需要在仓库设置中配置:

  • vars.REGISTRY —— registry 主机名(例如 ghcr.io,自建 Gitea Container Registry 写 git.example.com
  • vars.IMAGE_OWNER —— registry 的 owner / namespace
  • secrets.PACKAGES_TOKEN —— registry 推送 token
  • vars.DEPLOY_PATH —— (可选) runner 主机上某个 docker-compose 目录的路径。配上之后 workflow 会跑一个 deploy 后续 jobcd 到这个目录后 docker compose up -d 拉新镜像。留空只 build & push。

Choosing an AS

Claude.ai 网页端强制走完整的 OAuth Authorization Code + PKCE 流程,对接你 MCP server 的 /.well-known/oauth-authorization-server 端点 —— 没有 bearer token 捷径可用。在下面几条路径中选一条:

Hosted (fastest start, recommended for new setups) —— RS256 模式:

Provider 免费额度 备注
Logto Cloud 5000 MAU 最轻量,约 30 分钟搭好
ZITADEL Cloud 25k auths / 月 功能更全,文档稍重

设置 Jwt__Algorithm=RS256Jwt__Issuer=<你的 tenant issuer URL>。公钥会自动从 <Issuer>/.well-known/openid-configuration 拉取。

Self-hosted, full-featured —— RS256 模式: KeycloakZITADELLogtoAuthentik

Self-hosted, minimal —— HS256 模式: 参见 nas-auth —— 本 server 在开发过程中对接的那个约 500 行 LoC 的参考 AS。或者自己写一个。MCP server 的 Jwt__SigningKey__Current 必须与 AS 的签名密钥保持一致。

不论选哪条,AS 必须支持:

  • OAuth 2.1 + PKCERFC 7636
  • Dynamic Client RegistrationRFC 7591)—— 让 Claude.ai 能自助注册
  • resource 参数(RFC 8707)—— 用于签发 audience-bound token
  • 自定义 scope 支持(read:gitea

License

MIT