Every per-repo MCP tool 404s while owner-only tools work. The leaked failure URL points at https://gitea.d-ma.be/api/swagger, so the per-repo route resolver falls back to a wrong base path instead of /api/v1/repos/{owner}/{repo}/....
Affected (per-repo path)
repo_get, repo_status -> 404
dir_list -> 404
workflow_run_list, workflow_run_status -> 404
issue_create -> 405 (route present but method misrouted)
repo_get(owner=mathias, repo=tapir) -> -32000 not found: 404 page not found
dir_list(owner=mathias, repo=hyperguild, path="") -> leaks the cause: -32000 not found: {"errors":null,"message":"not found","url":"https://gitea.d-ma.be/api/swagger"}
issue_create(owner=mathias, repo=gitea-mcp) -> -32000 unexpected status 405
Same failures on tapir (private) and hyperguild (public) -> not repo- or visibility-specific.
Diagnosis
Not auth (owner-scoped calls authenticate fine) and not a missing repo (repo_list enumerates it). Per-repo handlers construct the wrong upstream API URL — /api/swagger rather than /api/v1/repos/{owner}/{repo}/contents (and /actions/... for the workflow tools). Likely a base-URL / route-template bug in the per-repo resolver.
Impact
Cannot read CI runs, file contents, or repo metadata through the MCP front door. Owner-scoped operations unaffected.
Repro
repo_get(owner=mathias, repo=tapir) -> 404
dir_list(owner=mathias, repo=hyperguild, path="") -> 404 with url:.../api/swagger
Filed via gitea API after the MCP per-repo tools failed during a tapir push/CI check (var: DMABE_GITEA_API_TOKEN via op run).
## Summary
Every **per-repo** MCP tool 404s while owner-only tools work. The leaked failure URL points at `https://gitea.d-ma.be/api/swagger`, so the per-repo route resolver falls back to a wrong base path instead of `/api/v1/repos/{owner}/{repo}/...`.
## Affected (per-repo path)
- `repo_get`, `repo_status` -> 404
- `dir_list` -> 404
- `workflow_run_list`, `workflow_run_status` -> 404
- `issue_create` -> 405 (route present but method misrouted)
## Works (owner-only param)
- `repo_list` (owner=mathias) -> returns repos incl. `mathias/tapir`
## Evidence
- `repo_get(owner=mathias, repo=tapir)` -> `-32000 not found: 404 page not found`
- `dir_list(owner=mathias, repo=hyperguild, path="")` -> leaks the cause: `-32000 not found: {"errors":null,"message":"not found","url":"https://gitea.d-ma.be/api/swagger"}`
- `issue_create(owner=mathias, repo=gitea-mcp)` -> `-32000 unexpected status 405`
- Same failures on `tapir` (private) and `hyperguild` (public) -> not repo- or visibility-specific.
## Diagnosis
Not auth (owner-scoped calls authenticate fine) and not a missing repo (`repo_list` enumerates it). Per-repo handlers construct the wrong upstream API URL — `/api/swagger` rather than `/api/v1/repos/{owner}/{repo}/contents` (and `/actions/...` for the workflow tools). Likely a base-URL / route-template bug in the per-repo resolver.
## Impact
Cannot read CI runs, file contents, or repo metadata through the MCP front door. Owner-scoped operations unaffected.
## Repro
1. `repo_get(owner=mathias, repo=tapir)` -> 404
2. `dir_list(owner=mathias, repo=hyperguild, path="")` -> 404 with `url:.../api/swagger`
Filed via gitea API after the MCP per-repo tools failed during a tapir push/CI check (var: DMABE_GITEA_API_TOKEN via op run).
Root cause — parameter-name contract mismatch, not a routing fault
Not a route resolver / base-URL bug. MCP dispatch was correct the whole time.
Every per-repo tool declares the repo identifier as name (and issue/PR index as number), but every caller — the claude.ai connector and LLMs primed on gitea's own owner/repo API — sends repo and index. The unmatched fields zero-valued the path segment, so the client built /api/v1/repos/{owner}//... and forwarded it upstream. Gitea answers an unmatched /api/v1 path with its generic 404 whose body always carries "url":".../api/swagger" — that swagger pointer is gitea boilerplate, not a misroute. repo_list "worked" only because it's the one tool requiring owner alone.
Confirmed by raw curl to the live pod (claude.ai bypassed): same tool, name arg → 200; repo arg → 404.
Fix (v0.2.8, deployed 184d5a9)
parseArgs aliases repo→name and index→number (explicit canonical wins; alias key preserved so pr_merge's real index is unaffected) — accepts the idiomatic argument names so the recurrence is gone.
The gitea client rejects any path with a mid-path empty segment (//) before the HTTP call, returning ErrValidation instead of leaking gitea's opaque swagger-404.
Round-trip dispatch test asserts every registered tool resolves and ships a parseable schema (shared RegisterAll), so a future tool can't silently fail to route — caught by CI, not by a 404 in a planning session.
Verified live
repo_get/dir_list/issue_get/issue_list with the old repo/index args → all ✅. Canonical name/number → ✅. Owner-level repo_list regression → ✅.
## Root cause — parameter-name contract mismatch, not a routing fault
Not a route resolver / base-URL bug. MCP dispatch was correct the whole time.
Every per-repo tool declares the repo identifier as **`name`** (and issue/PR index as **`number`**), but every caller — the claude.ai connector and LLMs primed on gitea's own `owner`/`repo` API — sends **`repo`** and **`index`**. The unmatched fields zero-valued the path segment, so the client built `/api/v1/repos/{owner}//...` and forwarded it upstream. Gitea answers an unmatched `/api/v1` path with its generic 404 whose body always carries `"url":".../api/swagger"` — that swagger pointer is gitea boilerplate, **not** a misroute. `repo_list` "worked" only because it's the one tool requiring `owner` alone.
Confirmed by raw curl to the live pod (claude.ai bypassed): same tool, `name` arg → 200; `repo` arg → 404.
## Fix (v0.2.8, deployed `184d5a9`)
1. `parseArgs` aliases `repo`→`name` and `index`→`number` (explicit canonical wins; alias key preserved so `pr_merge`'s real `index` is unaffected) — accepts the idiomatic argument names so the recurrence is gone.
2. The gitea client rejects any path with a mid-path empty segment (`//`) before the HTTP call, returning `ErrValidation` instead of leaking gitea's opaque swagger-404.
3. Round-trip dispatch test asserts every registered tool resolves and ships a parseable schema (shared `RegisterAll`), so a future tool can't silently fail to route — caught by CI, not by a 404 in a planning session.
## Verified live
`repo_get`/`dir_list`/`issue_get`/`issue_list` with the old `repo`/`index` args → all ✅. Canonical `name`/`number` → ✅. Owner-level `repo_list` regression → ✅.
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.
Summary
Every per-repo MCP tool 404s while owner-only tools work. The leaked failure URL points at
https://gitea.d-ma.be/api/swagger, so the per-repo route resolver falls back to a wrong base path instead of/api/v1/repos/{owner}/{repo}/....Affected (per-repo path)
repo_get,repo_status-> 404dir_list-> 404workflow_run_list,workflow_run_status-> 404issue_create-> 405 (route present but method misrouted)Works (owner-only param)
repo_list(owner=mathias) -> returns repos incl.mathias/tapirEvidence
repo_get(owner=mathias, repo=tapir)->-32000 not found: 404 page not founddir_list(owner=mathias, repo=hyperguild, path="")-> leaks the cause:-32000 not found: {"errors":null,"message":"not found","url":"https://gitea.d-ma.be/api/swagger"}issue_create(owner=mathias, repo=gitea-mcp)->-32000 unexpected status 405tapir(private) andhyperguild(public) -> not repo- or visibility-specific.Diagnosis
Not auth (owner-scoped calls authenticate fine) and not a missing repo (
repo_listenumerates it). Per-repo handlers construct the wrong upstream API URL —/api/swaggerrather than/api/v1/repos/{owner}/{repo}/contents(and/actions/...for the workflow tools). Likely a base-URL / route-template bug in the per-repo resolver.Impact
Cannot read CI runs, file contents, or repo metadata through the MCP front door. Owner-scoped operations unaffected.
Repro
repo_get(owner=mathias, repo=tapir)-> 404dir_list(owner=mathias, repo=hyperguild, path="")-> 404 withurl:.../api/swaggerFiled via gitea API after the MCP per-repo tools failed during a tapir push/CI check (var: DMABE_GITEA_API_TOKEN via op run).
Root cause — parameter-name contract mismatch, not a routing fault
Not a route resolver / base-URL bug. MCP dispatch was correct the whole time.
Every per-repo tool declares the repo identifier as
name(and issue/PR index asnumber), but every caller — the claude.ai connector and LLMs primed on gitea's ownowner/repoAPI — sendsrepoandindex. The unmatched fields zero-valued the path segment, so the client built/api/v1/repos/{owner}//...and forwarded it upstream. Gitea answers an unmatched/api/v1path with its generic 404 whose body always carries"url":".../api/swagger"— that swagger pointer is gitea boilerplate, not a misroute.repo_list"worked" only because it's the one tool requiringowneralone.Confirmed by raw curl to the live pod (claude.ai bypassed): same tool,
namearg → 200;repoarg → 404.Fix (v0.2.8, deployed
184d5a9)parseArgsaliasesrepo→nameandindex→number(explicit canonical wins; alias key preserved sopr_merge's realindexis unaffected) — accepts the idiomatic argument names so the recurrence is gone.//) before the HTTP call, returningErrValidationinstead of leaking gitea's opaque swagger-404.RegisterAll), so a future tool can't silently fail to route — caught by CI, not by a 404 in a planning session.Verified live
repo_get/dir_list/issue_get/issue_listwith the oldrepo/indexargs → all ✅. Canonicalname/number→ ✅. Owner-levelrepo_listregression → ✅.