Rebuilds AegisGitea-MCP as a complete, full-access MCP bridge to the Gitea API. Breaking change: all tool names, the HTTP adapter, the YAML policy and API keys change (version 1.0.0.dev0). Design decisions are in docs/adr/0001-0006, the audit and chunk plan in docs/audit.md.
What changes
538 tools: one per operation of the pinned Gitea 28.1.0 swagger (spec/gitea-swagger.v1.json), generated into catalog/tools.json (gitea_<domain>_<action>), plus gitea_api_request for endpoints newer than the spec. Everything is enabled by default, including admin, secrets, deletes and Sudo. READ_ONLY, TOOLSETS and DISABLED_TOOLS restrict exposure.
Official MCP Python SDK v2 (low-level Server, since FastMCP/MCPServer cannot take raw JSON schemas): stdio and streamable HTTP from one codebase, /health readiness endpoint.
Auth: admin-token mode (stdio, static bearer on HTTP, constant-time compare) and per-user OAuth (our server is the OAuth AS for MCP clients with dynamic client registration and a consent page, and a Gitea OAuth client upstream; calls run with the user's own Gitea token, identity in every result; encrypted token store). A non-loopback bind refuses to start without auth.
Executor: path encoding verified against the live instance, pagination metadata, binary/text handling, sanitized error mapping, exact secret redaction, untrusted-content labelling on every result.
aegis-gitea-mcp setup: verifies reachability, admin rights and token scopes, registers the OAuth app, writes .env (0600) without echoing secrets, prints client configs for Claude, Claude Code, Mistral Le Chat and local tools plus a compose and reverse-proxy snippet.
Removed: FastAPI adapter, hand-rolled JSON-RPC, YAML policy engine, resource-type gate, dead API keys, automation and metrics.
Verification
Unit: 1553 passed, coverage 98% (gate raised to 90%).
Live integration against a disposable digest-pinned Gitea 28.1.0: scenario tests (org, repo, issue, PR, review, merge, release, secrets, variables, webhooks, admin, Sudo, packages, a real Actions run via act_runner), a sweep calling all 538 tools against a pinned per-tool baseline (521 with a 2xx; the rest documented in docs/integration-coverage.md), per-user mode, the setup command.
MCP smoke tests over stdio, streamable HTTP and the built image with real SDK clients.
Security phase: threat model in docs/security.md, 19 review findings fixed or waived with reasons (docs/security-findings.md), dedicated tests/security suite; pip-audit, ruff S, gitleaks and Trivy clean.
CI: 8 jobs (gitleaks, lint, test-unit, test-integration, test-mcp, build-and-push, docs-check, security), green on the feature branch.
Known limitations and owner follow-ups
gitea_user_create_token, gitea_user_get_tokens, gitea_user_delete_access_token require basic auth with a password (Gitea rule) and return 401 through this server.
The tool list is large (538); for clients that struggle, use TOOLSETS.
Reserve the aegis-gitea-mcp name on PyPI; docs install from the Gitea package index.
Enable "require approval for workflows from forks" in the repository settings (docs/deployment.md).
Rebuilds AegisGitea-MCP as a complete, full-access MCP bridge to the Gitea API. Breaking change: all tool names, the HTTP adapter, the YAML policy and API keys change (version 1.0.0.dev0). Design decisions are in docs/adr/0001-0006, the audit and chunk plan in docs/audit.md.
## What changes
- 538 tools: one per operation of the pinned Gitea 28.1.0 swagger (`spec/gitea-swagger.v1.json`), generated into `catalog/tools.json` (`gitea_<domain>_<action>`), plus `gitea_api_request` for endpoints newer than the spec. Everything is enabled by default, including admin, secrets, deletes and Sudo. `READ_ONLY`, `TOOLSETS` and `DISABLED_TOOLS` restrict exposure.
- Official MCP Python SDK v2 (low-level `Server`, since FastMCP/`MCPServer` cannot take raw JSON schemas): stdio and streamable HTTP from one codebase, `/health` readiness endpoint.
- Auth: admin-token mode (stdio, static bearer on HTTP, constant-time compare) and per-user OAuth (our server is the OAuth AS for MCP clients with dynamic client registration and a consent page, and a Gitea OAuth client upstream; calls run with the user's own Gitea token, identity in every result; encrypted token store). A non-loopback bind refuses to start without auth.
- Executor: path encoding verified against the live instance, pagination metadata, binary/text handling, sanitized error mapping, exact secret redaction, untrusted-content labelling on every result.
- `aegis-gitea-mcp setup`: verifies reachability, admin rights and token scopes, registers the OAuth app, writes `.env` (0600) without echoing secrets, prints client configs for Claude, Claude Code, Mistral Le Chat and local tools plus a compose and reverse-proxy snippet.
- Removed: FastAPI adapter, hand-rolled JSON-RPC, YAML policy engine, resource-type gate, dead API keys, automation and metrics.
## Verification
- Unit: 1553 passed, coverage 98% (gate raised to 90%).
- Live integration against a disposable digest-pinned Gitea 28.1.0: scenario tests (org, repo, issue, PR, review, merge, release, secrets, variables, webhooks, admin, Sudo, packages, a real Actions run via act_runner), a sweep calling all 538 tools against a pinned per-tool baseline (521 with a 2xx; the rest documented in docs/integration-coverage.md), per-user mode, the setup command.
- MCP smoke tests over stdio, streamable HTTP and the built image with real SDK clients.
- Security phase: threat model in docs/security.md, 19 review findings fixed or waived with reasons (docs/security-findings.md), dedicated `tests/security` suite; pip-audit, ruff S, gitleaks and Trivy clean.
- CI: 8 jobs (gitleaks, lint, test-unit, test-integration, test-mcp, build-and-push, docs-check, security), green on the feature branch.
## Known limitations and owner follow-ups
- `gitea_user_create_token`, `gitea_user_get_tokens`, `gitea_user_delete_access_token` require basic auth with a password (Gitea rule) and return 401 through this server.
- The tool list is large (538); for clients that struggle, use `TOOLSETS`.
- Reserve the `aegis-gitea-mcp` name on PyPI; docs install from the Gitea package index.
- Enable "require approval for workflows from forks" in the repository settings (docs/deployment.md).
Inventory and gap analysis of the 0.2.0 codebase against the full Gitea API
and the official gitea-mcp toolset, the B1..B11 + security chunk plan, ADRs
0001-0006 for the full-access rebuild, SDK/OAuth research notes, and the
instance swagger pinned for tool generation. The audit-logging doc moves to
docs/audit-logging.md.
Removes the FastAPI server, hand-rolled MCP protocol, tool registry and
tools, YAML policy, resource-type authz, API-key and OAuth proxy modules,
automation, observability, cache, the old Gitea client and stdio adapter,
the key-management scripts and their tests (ADR 0006).
Adds the pydantic-settings Settings with validated GITEA_URL, OAuth scopes
and toolsets, the argparse CLI skeleton, catalog.model, identity and
GiteaAPIError. Test fixtures now isolate env, cwd and state paths in tmp.
Verifies the Gitea instance, token and scopes, registers or reuses the aegis-gitea-mcp OAuth app, generates missing secrets and writes a 0600 env file from the template. Prints client configs and deployment snippets without any secret value.
Adds scripts/generate_catalog.py (with --check), the naming rules, schema conversion, the committed tools.json for all 537 operations and the load_catalog() loader.
Replace lint/test/docker workflows with ci.yml (gitleaks, lint, test-unit, test-integration, test-mcp, build-and-push, docs-check, security). Rebuild the Dockerfile as a digest-pinned multi-stage uv build running as uid 1000, add .dockerignore, a hardened compose file and a gitleaks config. The integration harness now falls back to /etc/hostname and /proc/self to find its own container.
Slash safety is decided per (path template, parameter) so single-segment
routes such as releases/tags/{tag} and editorconfig/{filepath} keep %2F.
Adds authorization_header, remote_password, mirror_password, mirror_token and
the AWS key fields to the redacted request fields. Deeply nested JSON from
Gitea is treated as text instead of raising RecursionError, and base64
uploads are capped before decoding.
Require consent before sending the token over plain http to a non-loopback
host, strip control characters from server-supplied text, restrict env values
and tokens to printable ASCII, validate the URLs like other env values and
fsync the directory after the atomic replace.
Low-level SDK server built from the catalog plus gitea_api_request, with TOOLSETS/READ_ONLY/DISABLED_TOOLS filters, schema validation without value echo, one audit event per call, static bearer verifier, identity resolver hook, /health, DNS-rebinding protection, rate limiting and fail-closed startup checks.
The audit logger is opened at startup (exit 2 on failure) and obtained before
every call, which is refused with audit_unavailable otherwise. Audit records only
take validated path and sudo values and use the request id as correlation id. The
rate limiter evicts idle keys, caps tracked keys, audits the first excess per key
and window and uses the settings passed to the app. FORWARDED_ALLOW_IPS is now a
setting, and startup warns about a missing host allow-list and unavailable OAuth.
Authorization server for MCP clients (SDK provider with dynamic client
registration, PKCE, refresh rotation and revocation) that signs users in
through Gitea and runs tool calls with the user's own Gitea token.
- consent page with CSRF protection and a browser-binding cookie on the
Gitea callback (confused-deputy protection for the shared Gitea app)
- encrypted token store (Fernet, HKDF-derived key, 0600, atomic writes,
symlink and corrupt-file refusal); our tokens stored only as hashes
- tokens bound to the MCP endpoint resource and the gitea scope; the
static bearer keeps working next to OAuth
- code replay and refresh token reuse revoke the grant
- automatic upstream refresh with per-session locking; invalid_grant
ends the session and never falls back to the admin token
- OAuth-only deployments (no GITEA_TOKEN, no bearer) may start
- rate limit on the OAuth endpoints, query strings dropped from access logs
Identity gains a redact field that the executor honours, so the OAuth
resolver no longer needs an executor subclass overriding private
internals. Opaque agm_ tokens issued by our authorization server are now
masked by the secret patterns wherever they appear in content.
The --env option swallowed the server name, so the stdio command is reordered. uvx now always
names the project registry because the package name is not registered on public PyPI.
README gains a Known limitations section, the READ_ONLY wording is corrected, packaging.md
warns about --extra-index-url and the Le Chat admin requirement is mentioned.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Rebuilds AegisGitea-MCP as a complete, full-access MCP bridge to the Gitea API. Breaking change: all tool names, the HTTP adapter, the YAML policy and API keys change (version 1.0.0.dev0). Design decisions are in docs/adr/0001-0006, the audit and chunk plan in docs/audit.md.
What changes
spec/gitea-swagger.v1.json), generated intocatalog/tools.json(gitea_<domain>_<action>), plusgitea_api_requestfor endpoints newer than the spec. Everything is enabled by default, including admin, secrets, deletes and Sudo.READ_ONLY,TOOLSETSandDISABLED_TOOLSrestrict exposure.Server, since FastMCP/MCPServercannot take raw JSON schemas): stdio and streamable HTTP from one codebase,/healthreadiness endpoint.aegis-gitea-mcp setup: verifies reachability, admin rights and token scopes, registers the OAuth app, writes.env(0600) without echoing secrets, prints client configs for Claude, Claude Code, Mistral Le Chat and local tools plus a compose and reverse-proxy snippet.Verification
tests/securitysuite; pip-audit, ruff S, gitleaks and Trivy clean.Known limitations and owner follow-ups
gitea_user_create_token,gitea_user_get_tokens,gitea_user_delete_access_tokenrequire basic auth with a password (Gitea rule) and return 401 through this server.TOOLSETS.aegis-gitea-mcpname on PyPI; docs install from the Gitea package index.Slash safety is decided per (path template, parameter) so single-segment routes such as releases/tags/{tag} and editorconfig/{filepath} keep %2F. Adds authorization_header, remote_password, mirror_password, mirror_token and the AWS key fields to the redacted request fields. Deeply nested JSON from Gitea is treated as text instead of raising RecursionError, and base64 uploads are capped before decoding.