zhengchen.tao bbe1ccecd1
Build Docker Image / build (push) Successful in 9m29s
Build Docker Image / deploy (push) Failing after 24s
fix(docker): 用 useradd 替换 adduser(slim 镜像无 adduser)
Debian bookworm-slim 不带 adduser(perl wrapper 包),
导致 build 阶段 exit 127 (command not found)。
改用预装 passwd 包提供的 useradd,语义等价。
2026-05-06 23:28:37 +08:00

gitea-mcp

Read access to all Gitea repos (public + private, personal + org) via MCP. OAuth via nas-auth (DCR + PKCE + JWT HS256), admin PAT held internally — Claude never touches the PAT.

Architecture

  Claude.ai (MCP client)
       │
       │ ① GET /.well-known/oauth-authorization-server
       ↓
  git-mcp.zhengchentao.win   ←── this service
       │
       │ ② DCR + PKCE auth flow redirect
       ↓
  auth.zhengchentao.win      ←── nas-auth
       │
       │ ③ JWT (aud=gitea, scope=read:gitea)
       ↓
  Claude.ai → Bearer JWT
       │
       │ ④ POST /mcp  (MCP Streamable HTTP)
       ↓
  git-mcp.zhengchentao.win
       │ Bearer <GITEA_ADMIN_PAT>
       ↓
  git.zhengchentao.win       ←── Gitea

See gitea-mcp 设计.md for full design rationale.

Tools

Tool Description
list_repos List all repos (personal + org, public + private)
read_repo Repo metadata: topics, stars, default branch, mirror flag
list_tree File tree at a ref (recursive optional, max 500 entries)
read_file Raw file content (UTF-8, truncated at 1MB)
search_code Code search via Gitea indexer (requires indexer enabled)
list_branches Branch list + last commit per branch
list_commits Recent commits with author + message
read_commit Full commit details + per-file diff (max 50 files)
list_issues Issues filtered by state (open/closed/all)
read_issue Issue body + all comments
list_pulls Pull requests filtered by state
read_pull PR body + review comments + changed files
list_orgs All organizations visible to admin token
read_org Org metadata
list_packages Packages in registry by owner (container/generic/npm/...)
read_package Package version metadata
list_workflow_runs Gitea Actions workflow run history
read_run_log Run details + job list + log (truncated at 1MB)

All tools require a valid JWT with scope=read:gitea issued by nas-auth.

Configuration (env vars)

Variable Default Description
Gitea__BaseUrl https://git.zhengchentao.win Gitea backend URL
Gitea__AdminPat (required) Gitea read-only PAT — see PAT Setup below
Gitea__RepoBlacklist (empty) Comma-separated owner/repo pairs to hide from Claude
Gitea__DefaultLimit 50 Default page size for list operations
Gitea__MaxFileBytes 1048576 Max file read size in bytes (1MB)
Jwt__Issuer https://auth.zhengchentao.win Expected JWT issuer
Jwt__Audience gitea Expected JWT audience
Jwt__SigningKeyCurrent (required) HS256 signing key (shared with nas-auth)
Jwt__SigningKeyPrevious (empty) Previous key for rotation window
ASPNETCORE_ENVIRONMENT Production Use Development locally

All secrets come from /volume1/docker/compose/.env.shared on NAS — never hardcode them.

Local Development

1. Restore and run

dotnet restore
dotnet run
# Listens on http://localhost:5000

2. Generate a dev JWT

Use dotnet user-jwts to sign a token without running nas-auth:

dotnet user-jwts create \
  --issuer https://auth.zhengchentao.win \
  --audience gitea \
  --name tao \
  --claim sub=tao \
  --claim scope=read:gitea

Or use jwt.io with alg=HS256 and the key from Jwt:SigningKeyCurrent.

3. Test with MCP Inspector

npx @modelcontextprotocol/inspector
# Transport: Streamable HTTP
# URL: http://localhost:5000/mcp
# Bearer Token: <token from step 2>

4. Run unit tests

dotnet test gitea-mcp.Tests/

PAT Setup (Gitea → Settings → Applications)

Generate a token with only these scopes (principle of least privilege):

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

Do NOT grant write:* or admin:* scopes. Store the generated token in .env.shared as GITEA_MCP_PAT=<token>.

If the PAT is compromised: revoke in Gitea → generate new → update .env.shareddocker compose up -d gitea-mcp.

Docker Compose (NAS deployment)

# /volume1/docker/compose/gitea-mcp/docker-compose.yml
services:
  gitea-mcp:
    image: git.zhengchentao.win/zhengchen.tao/gitea-mcp:latest
    container_name: gitea-mcp
    restart: unless-stopped
    ports:
      - "9092:8080"
    volumes:
      - /volume1/docker/gitea-mcp/logs:/app/logs
    environment:
      - ASPNETCORE_ENVIRONMENT=Production
      - Gitea__BaseUrl=https://git.zhengchentao.win
      - Gitea__AdminPat=${GITEA_MCP_PAT}
      - Gitea__RepoBlacklist=
      - Jwt__Issuer=https://auth.zhengchentao.win
      - Jwt__Audience=gitea
      - Jwt__SigningKeyCurrent=${JWT_SIGNING_KEY_CURRENT}
      - TZ=Asia/Shanghai
    env_file:
      - ../.env.shared
S
Description
No description provided
Readme 67 KiB
Languages
C# 98.1%
Dockerfile 1.9%