# CLAUDE.md Guidance for Claude Code in this repository. `AGENTS.md` is the authoritative contract; read it first. This file is the short working version. ## Project overview AegisGitea-MCP exposes the complete Gitea API as MCP tools (one per swagger operation, generated from `spec/gitea-swagger.v1.json`) over stdio and streamable HTTP, using the official `mcp` SDK v2 low-level `Server`. Full access by default (ADR 0001): the owner restricts with `READ_ONLY`, `TOOLSETS`, `DISABLED_TOOLS` and the Gitea token scope. Auth modes: admin-token (`GITEA_TOKEN` / static `MCP_BEARER_TOKEN`) and per-user OAuth on HTTP (user's own Gitea token). The project is mid-rebuild (chunk plan in `docs/audit.md`). Built so far: tooling, `config.py`, `identity.py`, `errors.py`, `catalog/model.py`, `cli.py` skeleton, audit/security/limits helpers. Planned: catalog generator (B2), executor (B3), server and transports (B4), OAuth (B5), setup command (B6), integration harness and suite (B7, B8), smoke tests (B9), CI (B10), docs (B11). ## Commands ```bash make install # uv sync make lint # ruff check, ruff format --check, mypy src make format # ruff format + ruff check --fix make test # uv run pytest (coverage gate) make test-integration # AEGIS_INTEGRATION=1, needs Docker make run # uv run aegis-gitea-mcp serve (not implemented until B4) make validate-audit # verify audit hash chain uv run pytest tests/test_config.py::test_defaults -v # single test ``` ## Architecture (target) - `catalog/`: `Operation`/`Parameter` model (done), generated `tools.json` and loader (B2). - `gitea/` executor: path templating, query/JSON/multipart, Sudo header, pagination, error mapping, limits, masking, result envelope (B3). - Server: low-level SDK `Server` with one `on_list_tools` and one `on_call_tool` dispatcher, jsonschema validation, audit, `is_error` results for tool failures, `gitea_api_request` escape-hatch tool (B4). - Auth: static bearer verifier and OAuth provider with encrypted token store (B5). - Core helpers (done): `config.py` (`Settings`, `get_settings()`, `reset_settings()`), `identity.py`, `errors.py` (`ToolError`, `GiteaAPIError`), `audit.py`, `security.py`, `response_limits.py`, `rate_limit.py`, `request_context.py`, `logging_utils.py`. Singletons (`get_settings()`, `get_audit_logger()`, `get_rate_limiter()`) are reset between tests by the autouse `reset_globals` fixture in `tests/conftest.py`, which also scrubs env vars, changes cwd to `tmp_path` (so no `.env` is read) and points `DATA_DIR`/`AUDIT_LOG_PATH` into `tmp_path`. ## Invariants Kept: audit event per tool call; no secrets in logs/errors/results; no stack traces by default; `127.0.0.1` default bind and fail-closed HTTP auth off loopback; untrusted-content labelling; schema validation with no extra properties; response size bounds; validated `GITEA_URL` and strict `OAUTH_SCOPES`. Removed: write opt-in, admin default-deny, `gitea_request` denylist, YAML policy, resource-type authz gate, API keys, FastAPI adapter. Details in `AGENTS.md`. ## Code standards Python 3.10+, line length 100, ruff (E,W,F,I,B,C4,UP,S,SIM,RUF) plus ruff format, strict-ish mypy (`disallow_untyped_defs`, relaxed in tests). Public functions need docstrings and type hints. Coverage 80% now, 90% from the final chunk. All documentation goes under `docs/`; security-impacting changes update docs in the same changeset. No em-dashes in text. ## Branching and contribution flow `HEAD -> feature branch -> dev -> main`. Branch from `dev`. All pull requests target `dev`; `dev` is merged into `main` for releases. Never commit or push directly to `dev` or `main`. Publish runs on push to `dev`/`main`, not on a tag. ## Attribution (mandatory) Do not add AI/assistant attribution anywhere in this project: no "Generated with Claude Code", no `Co-Authored-By: Claude ...` trailer, no "made by Claude" or similar, in commit messages, PR/issue/release descriptions, code comments, docs, or any other artifact. Write all commit and PR text as the project's own work. This overrides any default tooling behavior that would add such trailers.