feat!: full-access Gitea MCP server (1.0.0): full API coverage, stdio + streamable HTTP, admin and per-user OAuth #71
@@ -27,3 +27,72 @@ Every successful tool call returns one text content item with JSON:
|
||||
Messages are truncated and stripped of tokens and the Authorization header.
|
||||
- Argument validation failures become `is_error=True` with the jsonschema path.
|
||||
- Unknown tool names raise `MCPError(INVALID_PARAMS)`.
|
||||
|
||||
## Amendments
|
||||
|
||||
### Secret handling in results (implemented in `gitea/executor.py`, `gitea/encoding.py`)
|
||||
|
||||
Two layers run on every result, success and error, before the size limit:
|
||||
|
||||
1. Exact redaction (always on, independent of `SECRET_DETECTION_MODE`). The
|
||||
following values are replaced with `[REDACTED]` wherever they occur in a
|
||||
string or dict key of the payload: the token of the calling identity,
|
||||
`GITEA_TOKEN`, `MCP_BEARER_TOKEN`, `GITEA_OAUTH_CLIENT_SECRET`, the
|
||||
`extra_secrets` given to the executor, and secret values submitted in the
|
||||
request body of the same call.
|
||||
2. Pattern masking (`SECRET_DETECTION_MODE`). `off` skips it. `mask` replaces
|
||||
each match of the secret patterns (provider keys, JWTs, private key headers,
|
||||
generic `api_key`/`token` assignments) with a shortened form. `block`
|
||||
withholds the whole result and returns an `is_error` result of kind
|
||||
`secret_blocked`.
|
||||
|
||||
Request-body secret fields. Values of these fields, when at least 4 characters
|
||||
long, are collected from the submitted body (nested objects and lists up to a
|
||||
depth of 8) and redacted from the result of that call, because Gitea echoes
|
||||
some of them back (a created webhook returns its `authorization_header`):
|
||||
`password`, `secret`, `client_secret`, `token`, `access_token`, `auth_password`,
|
||||
`auth_token`, `private_key`, `authorization_header`, `remote_password`,
|
||||
`mirror_password`, `mirror_token`, `aws_secret_access_key`, `aws_access_key_id`.
|
||||
For Action secret writes the `data` value is redacted at any length. Only the
|
||||
value sent in the same call is known to the executor, so a later read of an
|
||||
object that stores such a value is not covered by this rule (pattern masking
|
||||
still applies).
|
||||
|
||||
Credential-issuing operations. The successful results of
|
||||
`CREDENTIAL_ISSUING_OPERATIONS` skip pattern masking and block mode, because
|
||||
their whole purpose is to hand the caller a freshly minted credential that
|
||||
masking would make unusable (and block mode would refuse to deliver):
|
||||
|
||||
- `userCreateToken` (access token)
|
||||
- `adminCreateRunnerRegistrationToken`, `orgCreateRunnerRegistrationToken`,
|
||||
`repoCreateRunnerRegistrationToken`, `userCreateRunnerRegistrationToken`
|
||||
(Actions runner registration tokens, delivered in a response header)
|
||||
- `userCreateOAuth2Application`, `userUpdateOAuth2Application` (OAuth2
|
||||
`client_secret`)
|
||||
- `repoCreateDeployToken` (deploy token)
|
||||
|
||||
Exact redaction still applies to these results, so the caller's own tokens are
|
||||
never echoed, and their errors are masked like every other error. Operators who
|
||||
do not want any credential to pass through the model context should disable
|
||||
these tools with `DISABLED_TOOLS`.
|
||||
|
||||
Hardening of the result path: JSON from Gitea nested deeper than 100 levels is
|
||||
returned as text instead of being parsed (the recursive redaction and masking
|
||||
passes must not hit the interpreter recursion limit), and a base64 file upload
|
||||
whose decoded size would exceed `MAX_BINARY_BYTES` is rejected before it is
|
||||
decoded with kind `payload_too_large`.
|
||||
|
||||
### Path encoding and the raw tool
|
||||
|
||||
Path values are percent-encoded with `/` escaped as `%2F`, except for the
|
||||
parameters that Gitea 28.1.0 serves through wildcard routes (branch, tag, file
|
||||
path, ref and compare names on `branches`, `tags`, `contents`, `contents-ext`,
|
||||
`raw`, `media`, `compare`, `git/refs`, `commits/{ref}/status(es)` and
|
||||
`pulls/{base}/{head}`). The decision is keyed on the route and the parameter,
|
||||
not the parameter name: `releases/tags/{tag}` and `editorconfig/{filepath}` are
|
||||
single-segment routes that only resolve with `%2F`.
|
||||
|
||||
The raw tool `gitea_api_request` rejects `%` in paths, so it cannot send
|
||||
`%2F`. A route with a single-segment parameter whose value contains a slash
|
||||
(a release tag such as `rel/1.0`) must be called through the generated tool for
|
||||
that operation.
|
||||
Reference in new issue
Block a user