Files
gitea-mcp-oauth-bridge/README.md
T
ysj7705andClaude Opus 5.5 916d149ab5 Gitea MCP OAuth 브리지 (DCR, Gitea 로그인)
Gitea에 동적 클라이언트 등록이 없어 MCP 클라이언트가 URL만으로 OAuth 연결을
못 하는 문제를 이 브리지가 인가 서버 역할을 대신해 해결한다. G3X의
plane-mcp-oauth-bridge(upstream_oauth 모드)에서 가져와 Gitea 전용으로 정리했다:
Plane PAT 연결 페이지·워크스페이스 헤더·bridge_mode 분기를 없앴다.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 18:20:02 +09:00

79 lines
3.6 KiB
Markdown

# gitea-mcp-oauth-bridge
Gitea에는 동적 클라이언트 등록(DCR)이 없어서, claude.ai·Cursor 같은 MCP 클라이언트가 URL만으로 OAuth 연결을 못 한다.
이 브리지가 OAuth 인가 서버 역할(DCR 포함)을 대신하고, 로그인은 Gitea로 받아 사용자별 Gitea 토큰을 암호화해 둔 뒤
MCP 요청을 gitea-mcp로 넘긴다. 각 사용자는 자기 Gitea 계정 권한으로 동작한다.
- 주소: `https://git-mcp.sejong.dev` (MCP 엔드포인트는 `/mcp`)
- 구성: 클라이언트 → Caddy → 브리지(NAS 13332) → `gitea-mcp-oauth` 컨테이너(호스트 포트 없음, 도커 네트워크 `gitea-mcp-oauth_default`)
```
클라이언트 --OAuth(MCP, DCR)--> 브리지 --OIDC--> Gitea (git.sejong.dev)
|-- Gitea 토큰 암호화 저장 (SQLite, data/bridge.db)
`-- MCP 프록시 --> http://gitea-mcp-oauth:8080/mcp
```
브리지가 발급하지 않은 Bearer 토큰(Gitea PAT 등)은 그대로 gitea-mcp로 넘긴다(`PASSTHROUGH_UNKNOWN_TOKENS`, 기본 켜짐).
그래서 PAT를 넣어 쓰던 클라이언트도 계속 동작한다.
## 클라이언트 연결
- claude.ai: 설정 → 커넥터 → 커스텀 커넥터 추가 → URL `https://git-mcp.sejong.dev/mcp`. 고급 설정은 비워 둔다.
- Claude Code: `claude mcp add --transport http gitea https://git-mcp.sejong.dev/mcp` 후 `/mcp`에서 인증.
- Cursor: `"gitea": { "url": "https://git-mcp.sejong.dev/mcp" }`
## 환경 변수
`.env.example` 참고. `.env`는 NAS에만 두고(권한 600) 리포에는 올리지 않는다.
| 이름 | 설명 |
|---|---|
| `PUBLIC_BASE_URL` | `https://git-mcp.sejong.dev` (발급자·콜백 주소의 기준) |
| `UPSTREAM_MCP_URL` | `http://gitea-mcp-oauth:8080/mcp` |
| `UPSTREAM_HOST_HEADER` | `git-mcp.sejong.dev`. gitea-mcp `-oauth`는 `-oauth-public-url` 호스트만 받는다 |
| `OIDC_ISSUER` | `https://git.sejong.dev` |
| `OIDC_CLIENT_ID` / `OIDC_CLIENT_SECRET` | 아래 Gitea OAuth2 앱 |
| `OIDC_SCOPES` | 기본 `openid profile email write:repository write:issue write:user write:organization write:notification write:package write:misc` |
| `ALLOWED_MCP_CLIENTS` | 고정 클라이언트 `client_id\|redirect_uri` 콤마 구분. 비우면 DCR만 |
| `TOKEN_ENCRYPTION_KEY` | Gitea 토큰 암호화·세션 키 (`openssl rand -hex 32`). 바꾸면 모두 다시 로그인해야 한다 |
| `PASSTHROUGH_UNKNOWN_TOKENS` | 기본 `true` |
| `DCR_ENABLED` | 기본 `true` |
## Gitea OAuth2 앱
Gitea → 설정 → 애플리케이션 → OAuth2 애플리케이션 생성.
- 이름: `gitea-mcp-oauth-bridge`
- Redirect URI: `https://git-mcp.sejong.dev/oidc/callback`
- **Confidential Client 체크** (브리지가 client secret으로 토큰을 받는다)
## 운영 (NAS)
```
cd /volume1/docker/gitea-mcp-oauth-bridge
git pull --ff-only
sudo -n /usr/local/bin/docker compose up -d --build
sudo -n /usr/local/bin/docker compose logs -f bridge
```
`gitea-mcp-oauth`(`/volume1/docker/gitea-mcp-oauth`)가 먼저 떠 있어야 한다(외부 네트워크 `gitea-mcp-oauth_default`).
## 개발
```
python -m venv .venv && . .venv/bin/activate
pip install -r requirements.txt
pytest
```
## 코드 레이아웃
| 경로 | 역할 |
|---|---|
| `app/main.py` | FastAPI: OAuth AS(메타데이터·DCR·authorize·token), Gitea OIDC 로그인, `/mcp` 프록시 |
| `app/dcr.py` | RFC 7591 등록 검증·레이트 리밋 |
| `app/config.py` | 환경 변수 |
| `app/db.py` | SQLite: 사용자·Gitea 토큰·OAuth 코드·액세스 토큰·등록 클라이언트 |
| `app/crypto.py` | 토큰 Fernet 암호화 |
| `tests/` | pytest (Gitea·gitea-mcp는 모킹) |