Files
AegisGitea-MCP/docs/raw-api.md
T
Latte 8c84d76bd5 docs(raw-api): document gitea_request, env vars and policy examples
Adds docs/raw-api.md (two-layer policy, sensitive denylist, env vars, write-mode
warning), links it from index and api-reference, documents RAW_API_ENABLED /
RAW_API_ALLOW_SENSITIVE in .env.example, and adds commented virtual-tool-name
deny examples to policy.yaml.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 12:26:57 +02:00

5.1 KiB

Raw API Dispatch (gitea_request)

gitea_request is a generic escape hatch that can call any Gitea REST endpoint by method and path. It exists for the long tail of the Gitea API that the curated, typed tools do not cover (merging PRs, reviews, writing files, webhooks, branch/tag protections, collaborators, Actions/CI, packages, notifications, and so on).

Prefer the dedicated tools whenever one exists. Use gitea_request only for endpoints they do not cover. It is subject to policy, write-mode, and the sensitive-path denylist described below.

Arguments

Field Type Notes
method enum GET, HEAD, POST, PUT, PATCH, DELETE (case-insensitive). Any other method is rejected before any network call.
path string Gitea REST path. The /api/v1 prefix is optional. A full URL may be supplied — the host and query string are stripped.
query object Optional query-string parameters.
body object Optional JSON request body. Never logged.

The response is returned in a stable envelope:

{
  "method": "GET",
  "path": "/api/v1/repos/acme/app/pulls/1",
  "write": false,
  "repository": "acme/app",
  "data": { "...": "..." }
}

List responses add count and omitted; oversized objects are returned as a truncated JSON string with "truncated": true. All responses are bounded by MAX_TOOL_RESPONSE_ITEMS / MAX_TOOL_RESPONSE_CHARS.

Two-layer authorization

A single tool surface would normally collapse the granularity of policy.yaml. To preserve it, every call is authorized twice:

  1. Central gate (server.py). The registered gitea_request tool name is allowed/denied like any other tool. In service-PAT mode the central gate also parses the target repository from the path and verifies that the signed-in user has permission on that repository before the service PAT is used.
  2. Handler gate (raw_tools.py). The handler derives a coarse virtual tool name of the form gitea_request:<METHOD>:<top-path-segment> (for example gitea_request:GET:repos or gitea_request:DELETE:repos) and runs it back through the policy engine with the parsed repository, target path, and a is_write flag (true for any method other than GET/HEAD). This reuses the existing write-mode + write-whitelist enforcement and lets policy.yaml allow or deny raw dispatch per method and per top-level path segment.

Because the policy engine matches tool names by exact set membership (only paths use globbing), the virtual name is deliberately coarse and stable.

Example: lock raw dispatch to reads

tools:
  deny:
    - gitea_request:POST:repos
    - gitea_request:PUT:repos
    - gitea_request:PATCH:repos
    - gitea_request:DELETE:repos

Sensitive-path denylist

Independently of policy.yaml, the handler blocks endpoints that touch an admin or credential surface for every method, including GET (a GET on these already leaks credentials or privileged configuration):

  • /admin
  • *tokens*
  • *secrets*
  • *hooks*
  • *keys* (and *gpg_keys*)
  • applications/oauth2
  • actions/runners/registration-token

This denylist lives in the handler and cannot be re-opened from policy.yaml. It is overridden only by setting RAW_API_ALLOW_SENSITIVE=true.

Configuration

Variable Default Notes
RAW_API_ENABLED true Killswitch. When false, gitea_request refuses every dispatch with a 403.
RAW_API_ALLOW_SENSITIVE false When true, the admin/credential denylist is bypassed. Leave false unless you fully understand the exposure.

Security warning

With WRITE_MODE=true, the write whitelist is the only brake on POST/PUT/PATCH/DELETE across the entire Gitea API surface reachable by gitea_request. Any write method against a whitelisted repository will be attempted. Keep the whitelist tight, prefer denying the write virtual tool names in policy.yaml, and keep RAW_API_ALLOW_SENSITIVE=false.

Behavioral notes and edge cases

  • Full URL supplied instead of a path: only the path is used; the host and query string are discarded (query carries query parameters).
  • Path traversal (..): rejected during argument validation (400).
  • Unknown / non-HTTP method: rejected during argument validation, before any network call.
  • Cross-repo endpoints such as /repos/search and /repos/issues/search are intentionally not treated as repository-scoped, so repository is null for them.
  • Non-repository writes such as POST /user/repos or POST /orgs are denied with "write operation requires a repository target". This is the secure default — the per-user permission model is repository-scoped, so there is no repository against which to verify the write. This behavior is intentional and is not worked around.
  • Service-PAT mode: non-repository endpoints (for example GET /user/orgs) are denied by the central gate because per-user permission can only be verified against a repository target. Use the dedicated tools for those, or run in OAuth-only mode.