b3728c54ce237d4445a01d97c6e2413893e1f2cb
3 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
835fbbf324 |
feat: interactive setup menu for canonical Gitea MCP profiles (#31)
Add an interactive utility so users create/edit/validate canonical runtime profiles and generate safe LLM launcher snippets without hand-editing JSON or pasting tokens into Claude/Gemini/Codex configs. Run: `python gitea_config.py menu` (or `python gitea_config_menu.py`). gitea_config.py — pure, testable authoring helpers: - is_valid_profile_name, build_profile, keychain_auth/env_auth, empty_config - validate_config (reports missing base_url/auth, inline token/password — never echoing the secret value) - add_profile (preserves existing, rejects dup/invalid name/missing base_url), upsert_profile, remove_profile - save_config: mkdir parents + atomic temp-then-os.replace, pretty JSON - launcher_entry: thin MCP entry (command/args + GITEA_MCP_CONFIG/PROFILE only) - keychain_set: store a token via `security add-generic-password` (token passed as an arg, never returned/printed/logged; injectable runner) - `menu` __main__ dispatch gitea_config_menu.py — interactive loop with fully injectable IO/secret/HTTP/ keychain so it is testable without a real terminal, keychain, or network: - list / add / edit / remove / validate profiles - test authentication + show authenticated user (calls /user only on request) - reviewer-eligibility helper (authenticated user vs PR author, open state) — read-only, never approves/merges - launcher snippets for Claude / Gemini / Codex (no secrets) Security: tokens are never written to profiles.json, launcher snippets, logs, or errors — only keychain ids / env var names are stored. Backwards compatible: menu is optional; env-only mode and MCP server startup are unchanged. Tests: tests/test_config_menu.py (21 cases) — name validation, preserve-on-add, dup/invalid/missing-field rejection, atomic write (+ replace-failure leaves the original intact, no temp debris), keychain_set stores-without-printing, launcher snippets secret-free, eligibility eligible/self-author/closed, and a full menu add→list→quit flow proving the token value never reaches disk or stdout. Stacked on #30 (canonical profiles); base branch feat/json-runtime-profiles. Refs #10, #19. Closes #31. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
b88ca0c929 |
feat: canonical shared runtime-profiles config with typed auth refs (#19)
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) <noreply@anthropic.com> |
||
|
|
3aaba73127 |
feat: JSON multi-profile runtime config for Gitea MCP (roadmap #10)
Let one MCP server select among named Gitea runtime profiles from a JSON file
instead of editing code or juggling many .env files:
GITEA_MCP_CONFIG=/path/to/gitea-mcp.json
GITEA_MCP_PROFILE=dev
- New gitea_config.py: load/validate the JSON, select the named profile, and
resolve its token by env-var reference. Profiles supply base_url,
profile_name, token_env, owner/repo, allowed/forbidden operations, and audit
label.
- gitea_auth.get_profile() now overlays env over the selected JSON profile:
explicit env vars win, the JSON profile fills only what env leaves unset.
- gitea_auth.get_auth_header() gains a JSON token_env fallback after explicit
env tokens (env still wins).
Security / safety:
- Tokens are referenced by env-var NAME (token_env); an inline "token" is
rejected and never echoed. The value is never stored in or returned as
profile metadata.
- Fail-safe errors: missing file / invalid JSON / unknown or unset selected
profile raise a clear ConfigError that never prints file contents or tokens
(JSONDecodeError context is suppressed so the raw file text can't surface).
- No network calls during config parsing.
- Real config files are gitignored (gitea-mcp*.json), example kept.
Backwards compatible: with GITEA_MCP_CONFIG unset, behaviour is exactly the
prior env-only behaviour (all existing get_profile/get_auth_header tests pass
unchanged).
Docs: README JSON-profiles section + env table rows, .env.example placeholders,
gitea-mcp.example.json.
Tests: tests/test_config.py (22 cases) — env-only, selection, multiple
profiles, env-override precedence, missing file, invalid JSON, missing/unset
profile, inline-token rejection + redaction, and no-network-during-parse.
Refs #10. Note: issue #19 (env-based profiles) was already implemented and
closed; this JSON-file capability is adjacent new scope tracked under the
roadmap rather than reopening #19.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|