Found during an OWASP MCP Top 10 (2025) audit, 2026-10-01.
Gap
internal/mcp/server.gohandlePOST, case "tools/call", dispatches through Registry.Dispatch and writes the result. Nothing is logged. The only security log line is internal/auth/passthrough.go's "mcp auth accepted" (caller, token_fp, path), which records that someone connected, not what they did.
So repo_delete, file_delete, branch_delete, pr_merge, tbd_ship and workflow_run_trigger leave no trail in gitea-mcp. Gitea's own logs see the API call, but attributed to the PAT owner, without the MCP session, the tool name or the arguments the model chose.
Wanted
One structured slog line per tools/call: tool name, caller (auth.Caller(ctx)), token fingerprint, outcome (ok / error code), duration. Arguments: log them for mutating tools, but notfile_write_branch content (size only) — an argument log must not become a second copy of repo content or secrets.
Done when
A test calls a mutating tool through handlePOST and asserts one log record with tool name, caller and outcome.
A test asserts file_write_branch content does not appear in the record.
Lines reach Loki (the brain's audit sink already ships there; same path).
Found during an OWASP MCP Top 10 (2025) audit, 2026-10-01.
## Gap
`internal/mcp/server.go` `handlePOST`, case `"tools/call"`, dispatches through `Registry.Dispatch` and writes the result. **Nothing is logged.** The only security log line is `internal/auth/passthrough.go`'s `"mcp auth accepted"` (caller, token_fp, path), which records *that* someone connected, not *what they did*.
So `repo_delete`, `file_delete`, `branch_delete`, `pr_merge`, `tbd_ship` and `workflow_run_trigger` leave no trail in gitea-mcp. Gitea's own logs see the API call, but attributed to the PAT owner, without the MCP session, the tool name or the arguments the model chose.
## Wanted
One structured `slog` line per `tools/call`: tool name, caller (`auth.Caller(ctx)`), token fingerprint, outcome (ok / error code), duration. Arguments: log them for mutating tools, but **not** `file_write_branch` content (size only) — an argument log must not become a second copy of repo content or secrets.
## Done when
- A test calls a mutating tool through `handlePOST` and asserts one log record with tool name, caller and outcome.
- A test asserts `file_write_branch` content does **not** appear in the record.
- Lines reach Loki (the brain's audit sink already ships there; same path).
OWASP ref: MCP08 Lack of Audit and Telemetry.
---
_Created via git-mcp on behalf of @mathias_
The PR/issue bodies are absent; only args_bytes. Tests: internal/mcp/audit_test.go (one record per call with target, error outcome, argument values other than target omitted).
Not directly verified: arrival in Loki. Records go to pod stdout like every other gitea-mcp line, so they ride the same collection path, but I did not query Loki. infra ADR-0027 names these records as the evidence source for its gateway trigger #3.
Small known gap: the legacy name/index argument aliases (#38) are not in the audit target keys, so a caller using name instead of repo logs no repo. Low impact; most callers use the canonical keys.
Shipped in v0.16.0 (`724a997`), deployed by CD (infra `18d781c`). Verified live in the pod log, 2026-10-01T18:38Z:
```
{"msg":"mcp tool call","tool":"pr_create","caller":"mathias","args_bytes":1479,"duration_ms":210,"token_fp":"be43a3e1dc94","owner":"mathias","repo":"skills","outcome":"ok"}
{"msg":"mcp tool call","tool":"issue_create","caller":"mathias","args_bytes":1467,"duration_ms":31,"token_fp":"be43a3e1dc94","owner":"mathias","repo":"infra","outcome":"ok"}
```
The PR/issue bodies are absent; only `args_bytes`. Tests: `internal/mcp/audit_test.go` (one record per call with target, error outcome, argument values other than target omitted).
Not directly verified: arrival in Loki. Records go to pod stdout like every other gitea-mcp line, so they ride the same collection path, but I did not query Loki. infra ADR-0027 names these records as the evidence source for its gateway trigger #3.
Small known gap: the legacy `name`/`index` argument aliases (#38) are not in the audit target keys, so a caller using `name` instead of `repo` logs no repo. Low impact; most callers use the canonical keys.
---
_Created via git-mcp on behalf of @mathias_
The Loki clause I left unverified earlier is now verified. `{namespace="gitea-mcp"} |= "mcp tool call"` returns records, for example:
`{"msg":"mcp tool call","tool":"issue_close","caller":"mathias","args_bytes":47,"duration_ms":28,"token_fp":"be43a3e1dc94","owner":"mathias","repo":"infra","number":5…}`
The `name`/`index` alias gap was fixed in v0.16.1.
---
_Created via git-mcp on behalf of @mathias_
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.
Found during an OWASP MCP Top 10 (2025) audit, 2026-10-01.
Gap
internal/mcp/server.gohandlePOST, case"tools/call", dispatches throughRegistry.Dispatchand writes the result. Nothing is logged. The only security log line isinternal/auth/passthrough.go's"mcp auth accepted"(caller, token_fp, path), which records that someone connected, not what they did.So
repo_delete,file_delete,branch_delete,pr_merge,tbd_shipandworkflow_run_triggerleave no trail in gitea-mcp. Gitea's own logs see the API call, but attributed to the PAT owner, without the MCP session, the tool name or the arguments the model chose.Wanted
One structured
slogline pertools/call: tool name, caller (auth.Caller(ctx)), token fingerprint, outcome (ok / error code), duration. Arguments: log them for mutating tools, but notfile_write_branchcontent (size only) — an argument log must not become a second copy of repo content or secrets.Done when
handlePOSTand asserts one log record with tool name, caller and outcome.file_write_branchcontent does not appear in the record.OWASP ref: MCP08 Lack of Audit and Telemetry.
Created via git-mcp on behalf of @mathias
Shipped in v0.16.0 (
724a997), deployed by CD (infra18d781c). Verified live in the pod log, 2026-10-01T18:38Z:The PR/issue bodies are absent; only
args_bytes. Tests:internal/mcp/audit_test.go(one record per call with target, error outcome, argument values other than target omitted).Not directly verified: arrival in Loki. Records go to pod stdout like every other gitea-mcp line, so they ride the same collection path, but I did not query Loki. infra ADR-0027 names these records as the evidence source for its gateway trigger #3.
Small known gap: the legacy
name/indexargument aliases (#38) are not in the audit target keys, so a caller usingnameinstead ofrepologs no repo. Low impact; most callers use the canonical keys.Created via git-mcp on behalf of @mathias
The Loki clause I left unverified earlier is now verified.
{namespace="gitea-mcp"} |= "mcp tool call"returns records, for example:{"msg":"mcp tool call","tool":"issue_close","caller":"mathias","args_bytes":47,"duration_ms":28,"token_fp":"be43a3e1dc94","owner":"mathias","repo":"infra","number":5…}The
name/indexalias gap was fixed in v0.16.1.Created via git-mcp on behalf of @mathias