feat: load profiles.json v2 contexts shape with enabled enforcement and LLM-safe output (#120)

Support the canonical contexts-shape version 2 config (contexts / profiles /
projects / rules) alongside the existing environments shape and v1:

- Require a boolean 'enabled' on every context, profile, service, and
  project. Disabled entries are surfaced in audits but fail closed at
  selection/resolution — never a silent fallback to another profile,
  service, or credential source.
- Resolve the active identity from GITEA_MCP_PROFILE via the existing
  select_profile path; profile base_url falls back to the context's enabled
  gitea block.
- Add resolve_service() and project_for_path() for context service and
  project-to-context resolution (internal use; fail closed on disabled).
- get_auth_header now propagates ConfigError when GITEA_MCP_CONFIG is set
  instead of silently degrading to Basic auth.
- Hide endpoint URLs and keychain ids from normal LLM-facing output:
  gitea_whoami / gitea_get_profile report logical names and auth status
  only; new gitea_audit_config tool reports enabled/disabled state and safe
  one-line service summaries. The GITEA_MCP_REVEAL_ENDPOINTS opt-in (and
  'python3 gitea_config.py audit --reveal-endpoints' locally) restores
  endpoints and auth source names for admin diagnostics; token values are
  never printed on any path.
- Ship gitea-mcp.v2-contexts.example.json (synthetic values) and validate
  it in tests.

Implements #120

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-03 02:19:39 -04:00
parent fbf1bc5f5c
commit ff920a6496
8 changed files with 1127 additions and 22 deletions
+5 -3
View File
@@ -284,11 +284,13 @@ class TestAuthIntegration(_ConfigBase):
self.assertEqual(header, "token process-token")
def test_auth_header_unresolvable_ref_fails_closed(self):
# env token ref points at an unset var -> ConfigError inside resolve is
# swallowed to "no token"; auth falls through to (mocked-empty) basic.
# env token ref points at an unset var -> with GITEA_MCP_CONFIG set the
# ConfigError propagates (fail closed, #120): no silent fallback to
# Basic auth or another credential source.
with patch.dict(os.environ, self._env("mdcps-env"), clear=True):
with patch("gitea_auth.get_credentials", return_value=("", "")):
self.assertIsNone(gitea_auth.get_auth_header("gitea.example.com"))
with self.assertRaises(gitea_config.ConfigError):
gitea_auth.get_auth_header("gitea.example.com")
# ---------------------------------------------------------------------------