zhengchen.tao 8f35bf5b15
Build Docker Image / build (push) Successful in 10m45s
Build Docker Image / deploy (push) Failing after 53s
fix(http): set CircuitBreaker.SamplingDuration to 60s
Polly StandardResilienceHandler validates SamplingDuration >= 2 * AttemptTimeout
at startup. Default SamplingDuration is 30s and our AttemptTimeout is 30s, so
the container failed to boot with OptionsValidationException.

Set SamplingDuration explicitly to 60s while keeping AttemptTimeout at 30s.
2026-05-16 16:53:18 +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
       ↓
  gitea-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)
       ↓
  gitea-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 143 KiB
Languages
C# 98.1%
Dockerfile 1.9%