Files
AegisGitea-MCP/CLAUDE.md
T

4.6 KiB

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 rebuild is complete: catalog, executor, server, both transports, per-user OAuth, setup command, CI, security suite, MCP smoke tests and the live integration suite are in. The remaining open items are tracked in docs/roadmap.md.

Commands

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
uv run python scripts/generate_catalog.py --check   # catalog matches the spec
make validate-audit     # verify audit hash chain
uv run pytest tests/test_config.py::test_defaults -v   # single test

Architecture

  • catalog/ (done): model.py (Operation, Parameter), build.py (swagger to catalog, MCP annotations), naming.py, schema.py, generated tools.json (regenerate with uv run python scripts/generate_catalog.py, check with --check) and the loader in __init__.py.
  • gitea/ (done): executor.py (HTTP call, result envelope, redaction, size limits, CREDENTIAL_ISSUING_OPERATIONS), encoding.py (path, query, body and upload building, SLASH_ROUTES), pagination.py, redaction.py.
  • setup_cmd.py, setup_snippets.py, setup_template.py (done): the setup command, its client snippets and the built-in .env template.
  • server.py (done): 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. transports.py: stdio runner and streamable HTTP app.
  • auth.py and oauth/ (done): static bearer verifier and the OAuth provider with encrypted token store.
  • 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: write-ahead audit (a tool_call_started entry before the call runs, the call is refused when it cannot be written, a tool_invocation entry with the outcome after it; count calls by tool_invocation); 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 gate 90%. 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.