code_search called a REST endpoint that doesn't exist — every call 404'd #55

Closed
opened 2026-07-07 09:31:39 +00:00 by mathias · 0 comments
Owner

Problem

code_search's underlying gitea.Client.SearchCode called GET /api/v1/repos/{owner}/{repo}/search?type=code — an endpoint that does not exist on this Gitea instance. Confirmed against the live swagger spec (https://git.d-ma.be/swagger.v1.json): only /repos/search, /repos/issues/search, /topics/search etc are real paths. Every call to code_search returned a raw 404 page not found.

Gitea's own web UI code search (/{owner}/{repo}/search?q=...&type=code) works, but falls back to server-side git grep because no Repository Indexer is enabled here — and that fallback is an HTML-only route, never a JSON API. So no REST endpoint ever existed to call correctly; the original implementation was built against a path that was never real.

Why it went undetected: the existing test suite mocked the fantasy endpoint directly — asserting the request hit .../search?type=code and handing back a canned JSON envelope. Tests passed; the tool never worked against the real server.

Fix (v0.11.0, commit a16c5b0)

Replaced the REST call with a client-side implementation using primitives already proven against the live server this session (GetRepo, GetTree, GetFileContents): resolve default branch, walk the tree, substring-match q (case-insensitive, literal) against fetched file contents. Bounded by a 2000-file scan cap, a 512KB file-size cap (checked via the tree listing before any fetch), a binary-extension denylist, and a null-byte content check for anything the denylist misses.

Verified two ways:

  1. Full rewritten test suite (client + tool layer) against fakes serving the REAL endpoints, not the fantasy one.
  2. Manually replicated the algorithm against production data (mathias/gitea-mcp itself): GetRepo → resolved main; GetTree → 157 real blobs; GetFileContents on a known file → real content, occurrence count matched exactly what the new scoring logic would compute. This is the check that matters most given the root cause was tests validating a mock instead of reality — didn't want to repeat that mistake verifying the fix.

Tool layer (internal/tools/code_search.go) unchanged — SearchCode's signature and []CodeSearchHit contract are identical, so the fix is isolated to the client layer.

Known limitation (by design, not a bug)

Pagination re-scans the full tree on every call (no server-side index to page through incrementally) — acceptable for the repo sizes this targets. A real indexer (bleve/elasticsearch) enabled server-side would be the long-term fix; that's an infra decision, not something gitea-mcp controls.

task check green (exit 0, 0 FAIL, 0 lint issues) throughout.

## Problem `code_search`'s underlying `gitea.Client.SearchCode` called `GET /api/v1/repos/{owner}/{repo}/search?type=code` — an endpoint that does not exist on this Gitea instance. Confirmed against the live swagger spec (`https://git.d-ma.be/swagger.v1.json`): only `/repos/search`, `/repos/issues/search`, `/topics/search` etc are real paths. Every call to `code_search` returned a raw `404 page not found`. Gitea's own web UI code search (`/{owner}/{repo}/search?q=...&type=code`) works, but falls back to server-side `git grep` because no Repository Indexer is enabled here — and that fallback is an HTML-only route, never a JSON API. So no REST endpoint ever existed to call correctly; the original implementation was built against a path that was never real. **Why it went undetected:** the existing test suite mocked the fantasy endpoint directly — asserting the request hit `.../search?type=code` and handing back a canned JSON envelope. Tests passed; the tool never worked against the real server. ## Fix (v0.11.0, commit a16c5b0) Replaced the REST call with a client-side implementation using primitives already proven against the live server this session (`GetRepo`, `GetTree`, `GetFileContents`): resolve default branch, walk the tree, substring-match `q` (case-insensitive, literal) against fetched file contents. Bounded by a 2000-file scan cap, a 512KB file-size cap (checked via the tree listing before any fetch), a binary-extension denylist, and a null-byte content check for anything the denylist misses. Verified two ways: 1. Full rewritten test suite (client + tool layer) against fakes serving the REAL endpoints, not the fantasy one. 2. **Manually replicated the algorithm against production data** (`mathias/gitea-mcp` itself): `GetRepo` → resolved `main`; `GetTree` → 157 real blobs; `GetFileContents` on a known file → real content, occurrence count matched exactly what the new scoring logic would compute. This is the check that matters most given the root cause was tests validating a mock instead of reality — didn't want to repeat that mistake verifying the fix. Tool layer (`internal/tools/code_search.go`) unchanged — `SearchCode`'s signature and `[]CodeSearchHit` contract are identical, so the fix is isolated to the client layer. ## Known limitation (by design, not a bug) Pagination re-scans the full tree on every call (no server-side index to page through incrementally) — acceptable for the repo sizes this targets. A real indexer (bleve/elasticsearch) enabled server-side would be the long-term fix; that's an infra decision, not something gitea-mcp controls. `task check` green (exit 0, 0 FAIL, 0 lint issues) throughout.
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mathias/gitea-mcp#55