# 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 ↓ git.zhengchentao.win ←── Gitea ``` See [gitea-mcp 设计.md](../Coding/gitea-mcp/gitea-mcp%20设计.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 ```bash 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: ```bash dotnet user-jwts create \ --issuer https://auth.zhengchentao.win \ --audience gitea \ --name tao \ --claim sub=tao \ --claim scope=read:gitea ``` Or use [jwt.io](https://jwt.io) with alg=HS256 and the key from `Jwt:SigningKeyCurrent`. ### 3. Test with MCP Inspector ```bash npx @modelcontextprotocol/inspector # Transport: Streamable HTTP # URL: http://localhost:5000/mcp # Bearer Token: ``` ### 4. Run unit tests ```bash 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=`. If the PAT is compromised: revoke in Gitea → generate new → update `.env.shared` → `docker compose up -d gitea-mcp`. ## Docker Compose (NAS deployment) ```yaml # /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 ``` ## Related Documents - [gitea-mcp 设计](../Obsidian%20Vault/Coding/gitea-mcp/gitea-mcp%20设计.md) - [MCP 实现指南](../Obsidian%20Vault/Coding/obsidian-mcp/MCP%20实现指南.md) - [nas-auth 设计](../Obsidian%20Vault/Coding/nas-auth/nas-auth%20设计.md) - [Gitea Actions Build-Deploy 模板](../Obsidian%20Vault/Coding/Gitea%20Actions%20Build-Deploy%20模板.md)