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>
This commit is contained in:
commit
916d149ab5
18 files changed
+1494
No files matched your search
@@ -0,0 +1,78 @@
|
||||
# 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는 모킹) |
|
||||
Reference in new issue
Block a user