Rework the JSON runtime-profile config from the earlier ad-hoc schema
(profiles + token_env) to the canonical single-file model in #19, so every LLM
launcher can reference one shared Gitea profiles file instead of duplicating
GITEA_USER_*/GITEA_PASS_* blocks or embedding tokens.
Canonical schema (gitea_config.py):
- top-level "version" (1) + "profiles" map.
- each profile: base_url, username, default_owner, execution_profile, and a
typed auth reference:
{ "type": "keychain", "id": "..." } -> macOS keychain (security(1))
{ "type": "env", "name": "..." } -> named environment variable
- inline "token"/"password" keys are rejected (never accepted or echoed).
- select via GITEA_MCP_CONFIG (path) + GITEA_MCP_PROFILE (name).
gitea_auth integration:
- get_profile() overlays env over the selected profile (env wins; JSON fills
the rest); profile_name <- execution_profile; token_source_name <- the
non-secret auth reference name (env var name or "keychain:<id>"); now also
surfaces username + default_owner.
- get_auth_header() resolves the profile's auth reference (env/keychain) as a
token fallback after explicit env tokens; a ConfigError there fails closed.
Security / safety:
- Secrets referenced only (keychain id / env name); token values never stored
in or returned as metadata. Errors never print file contents, tokens, or
passwords (JSONDecodeError context suppressed).
- Missing file / invalid JSON / unsupported version / unknown-or-unset profile
/ unresolvable secret reference all raise a clear, safe ConfigError.
- No network calls during config parsing; keychain lookup is on-demand and
injectable for tests.
- Backwards compatible: GITEA_MCP_CONFIG unset => legacy env-only mode
(existing get_profile/get_auth_header tests unchanged).
Docs: README canonical-profile + thin-launcher (Claude/Gemini/Codex) sections
and a migration note away from duplicated GITEA_PASS_* blocks; .env.example and
gitea-mcp.example.json updated to the canonical shape (safe placeholders only).
Tests: tests/test_config.py (31 cases) — legacy env-only, JSON selection,
multiple profiles, missing/unset profile, invalid JSON, unsupported version,
env-override precedence, keychain + env auth-reference parsing and resolution,
missing-secret errors, inline token/password redaction, and no-network parse.
Refs #10. Completes the closed#19 (env-based profiles) by adding the canonical
shared-file model. Supersedes this PR's earlier simpler JSON schema.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>