Files
Latte 624a3c79ee
docker / test (push) Successful in 25s
docker / lint (push) Successful in 30s
test / test (push) Successful in 32s
lint / lint (push) Successful in 33s
docker / docker-test (push) Successful in 10s
docker / docker-publish (push) Successful in 6s
fix: surface Gitea auth errors and document the service PAT
Two related issues made the connected MCP server return a bare "Internal
server error" for tools that need real Gitea API access (e.g.
list_repositories), while public-repo-by-path reads worked:

1. Gitea OIDC access tokens only carry openid/profile/email and cannot call
   the repository REST API, so pure-OAuth mode fails for most tools. A service
   PAT (GITEA_TOKEN) is required in practice; per-user permission is still
   enforced before each call, so this does not weaken authorization.
2. The tool handlers caught GiteaError broadly and re-raised it as RuntimeError.
   Because GiteaAuthenticationError/GiteaAuthorizationError subclass GiteaError,
   a clean 401/403 was masked as a generic internal error and the server's
   re-authorization guidance never fired.

Changes:
- read_tools.py / repository.py / write_tools.py: re-raise the auth/authz
  subclasses before the broad GiteaError catch so server.py returns actionable
  guidance instead of a generic 500.
- .env.example + README.md: document GITEA_TOKEN as a least-privilege bot PAT,
  explain why it's needed and that OAuth remains authoritative, and note that
  list_repositories is intentionally unavailable in service-PAT mode.
- tests: assert tool handlers propagate auth errors unwrapped.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 16:47:10 +02:00

84 lines
3.0 KiB
Bash

# Runtime environment
ENVIRONMENT=production
# Gitea OAuth/OIDC resource server
GITEA_URL=https://git.hiddenden.cafe
# OAuth mode (recommended and required for per-user repository isolation)
OAUTH_MODE=true
GITEA_OAUTH_CLIENT_ID=your-gitea-oauth-client-id
GITEA_OAUTH_CLIENT_SECRET=your-gitea-oauth-client-secret
# Server secret used to HMAC-sign the OAuth proxy state parameter.
# Required when OAUTH_MODE=true; must be at least 32 characters.
# Generate with: openssl rand -hex 32
OAUTH_STATE_SECRET=
# Optional explicit audience override; defaults to GITEA_OAUTH_CLIENT_ID
OAUTH_EXPECTED_AUDIENCE=
# OIDC discovery and JWKS cache TTL
OAUTH_CACHE_TTL_SECONDS=300
# Where dynamically registered OAuth clients (RFC 7591 /register) are stored.
# This file MUST live on a writable, persistent volume. The default below is
# mounted as the `aegis-mcp-data` volume in docker-compose; if you run the
# container read-only without that volume the OAuth flow returns 500 because the
# directory is not writable. Point this at any writable path if you deploy
# differently.
DCR_STORAGE_PATH=/var/lib/aegis-mcp/dcr_clients.json
# MCP server configuration
MCP_HOST=127.0.0.1
MCP_PORT=8080
# Public, externally-reachable base URL of THIS MCP server (no trailing slash).
# Used to build OAuth metadata and the /oauth/callback URL behind a reverse proxy.
# This is the host you give to Claude (its MCP URL is PUBLIC_BASE_URL + /mcp).
PUBLIC_BASE_URL=https://gitea-mcp.hiddenden.cafe
ALLOW_INSECURE_BIND=false
# Logging / observability
LOG_LEVEL=INFO
AUDIT_LOG_PATH=/var/log/aegis-mcp/audit.log
METRICS_ENABLED=true
EXPOSE_ERROR_DETAILS=false
STARTUP_VALIDATE_GITEA=true
# Authentication failure controls
MAX_AUTH_FAILURES=5
AUTH_FAILURE_WINDOW=300
# Request rate limiting
RATE_LIMIT_PER_MINUTE=60
TOKEN_RATE_LIMIT_PER_MINUTE=120
# Tool output limits
MAX_FILE_SIZE_BYTES=1048576
MAX_TOOL_RESPONSE_ITEMS=200
MAX_TOOL_RESPONSE_CHARS=20000
REQUEST_TIMEOUT_SECONDS=30
# Security controls
SECRET_DETECTION_MODE=mask # off|mask|block
POLICY_FILE_PATH=policy.yaml
# Write mode (disabled by default)
WRITE_MODE=false
WRITE_REPOSITORY_WHITELIST=
WRITE_ALLOW_ALL_TOKEN_REPOS=false
# Automation mode (disabled by default)
AUTOMATION_ENABLED=false
AUTOMATION_SCHEDULER_ENABLED=false
AUTOMATION_STALE_DAYS=30
# Service PAT for Gitea REST execution (recommended in OAuth mode).
# Gitea's OIDC access tokens carry only openid/profile/email and CANNOT call the
# repository REST API, so without this most tools fail. Set GITEA_TOKEN to a
# Personal Access Token from a DEDICATED bot account with least privilege:
# - scope: read:repository (add write:repository only if WRITE_MODE=true)
# The user's OAuth identity is still authoritative: before every repository call
# the server checks that the signed-in user has permission on the target repo and
# denies it otherwise — the PAT only performs the API call after that check.
GITEA_TOKEN=
# API-key mode only (used when OAUTH_MODE=false). Leave unset in OAuth mode.
# MCP_API_KEYS=
# AUTH_ENABLED=true