fix: search_issues and list_issues return full issue bodies — a normal query blows the consumer's context window #8
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
search_issues— and, by the same code path,list_issues— return the complete Forgejo issue object for every hit, including the fullbodyand the entireuserobject. On a repo with long ticket bodies this makes the tool unusable: payload size scales withbody length × hit count, so even a 10-hit query can exceed an MCP client's tool-result limit. Narrowing the query barely helps.Impact / evidence
Hit during a
/forgejo-ticketduplicate check againsttrtmn/churchsms:search_issues {"query": "auth provider", "state": "all", "limit": 30}search_issues {"query": "OIDC identity provider", "limit": 10}(retried narrower)Both exceeded the client's tool-result limit, so the harness persisted them to a file instead of returning them inline — nothing usable reached context, and the search had to be redone by another route (reading the persisted file and projecting it down with
python3).That defeats the tool's own stated purpose — "find all open issues mentioning authentication" — on the repo where cross-repo search is most needed.
Root cause
src/tools/issues.ts:74-79—search_issuescallsGET /repos/issues/search, then hands the raw response straight torespond()(src/tools/issues.ts:13-14):There is no field projection at all. Forgejo's search endpoint returns full issue objects (
body,user,repository, …), so response size is driven entirely by upstream body length times hit count.list_issues(src/tools/issues.ts:45-52) goes through the identicalrespond()pass-through and has the same problem — it is simply reached less often. The fix belongs in the shared path, not only insearch_issues.Fix options (best first)
get_issue. Project list/search payloads down tonumber,title,state,labels,updated_at,html_url, and reservebodyfor a single-issue fetch. This is the standard MCP convention for list tools, and it removes the failure mode rather than bounding it.full_bodyflag (default: summary) on both tools.bodyto N chars inrespond()with a marker. Cheapest option, but still scales with hit count.Whichever is chosen, add a test asserting the projected payload for a long-bodied issue stays small.
Operational notes
dist/must be rebuilt.mainisdist/index.js(thetscoutput), and severalsrc/files are newer than their compiled counterparts. Editingsrc/alone changes nothing for a running server — it needsnpm run buildplus an MCP reconnect.mainis still the original plain-JS server (f36e6fb,src/tools/issues.js). The TypeScript rewrite currently being run is onfeat/typescript-migration, which is not merged tomain. Fixing the TS source means either merging that migration first or patching both trees.Workaround until this lands
Use
list_issuesfor title and duplicate scans (it is also the more precise tool for a single known repo — as its own description already says). Ifsearch_issuesis genuinely required, keeplimitlow and project the client-persisted output file down tonumber/state/titlebefore any of it enters context.