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>
67 lines
2.0 KiB
Python
67 lines
2.0 KiB
Python
from __future__ import annotations
|
|
|
|
from functools import lru_cache
|
|
|
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
|
|
|
|
def parse_allowed_mcp_clients(raw: str) -> dict[str, set[str]]:
|
|
out: dict[str, set[str]] = {}
|
|
for part in raw.split(","):
|
|
part = part.strip()
|
|
if not part:
|
|
continue
|
|
client_id, _, redirect_uri = part.partition("|")
|
|
client_id = client_id.strip()
|
|
redirect_uri = redirect_uri.strip()
|
|
if not client_id or not redirect_uri:
|
|
continue
|
|
out.setdefault(client_id, set()).add(redirect_uri)
|
|
return out
|
|
|
|
|
|
class Settings(BaseSettings):
|
|
model_config = SettingsConfigDict(env_file=".env", extra="ignore")
|
|
|
|
public_base_url: str = "http://127.0.0.1:48080"
|
|
listen_host: str = "0.0.0.0"
|
|
listen_port: int = 48080
|
|
upstream_mcp_url: str = "http://gitea-mcp:8080/mcp"
|
|
oidc_issuer: str = ""
|
|
oidc_client_id: str = ""
|
|
oidc_client_secret: str = ""
|
|
oidc_scopes: str = (
|
|
"openid profile email write:repository write:issue write:user"
|
|
" write:organization write:notification write:package write:misc"
|
|
)
|
|
allowed_mcp_clients: str = ""
|
|
token_encryption_key: str = ""
|
|
data_dir: str = "./data"
|
|
dcr_enabled: bool = True
|
|
dcr_max_clients: int = 2000
|
|
# Forward bearer tokens the bridge did not issue (e.g. Gitea PATs) unchanged.
|
|
passthrough_unknown_tokens: bool = True
|
|
# Optional Host header for the upstream request (gitea-mcp rejects other hosts).
|
|
upstream_host_header: str = ""
|
|
|
|
@property
|
|
def issuer(self) -> str:
|
|
return self.public_base_url.rstrip("/")
|
|
|
|
@property
|
|
def mcp_resource_url(self) -> str:
|
|
return f"{self.issuer}/mcp"
|
|
|
|
@property
|
|
def allowed_clients(self) -> dict[str, set[str]]:
|
|
return parse_allowed_mcp_clients(self.allowed_mcp_clients)
|
|
|
|
@property
|
|
def session_secret(self) -> bytes:
|
|
return self.token_encryption_key.encode("utf-8")
|
|
|
|
|
|
@lru_cache
|
|
def get_settings() -> Settings:
|
|
return Settings()
|