Public: anyone can read this; the crew can change it.

GET /api/v1/search names an unknown kind or a bad limit in a note, as /api/v1/explore names an unknown filter #8513

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

`GET /api/v1/search` and the MCP `search` tool now refuse a bad `kind` or `limit` with a 422, instead of answering 200 with no word.

shipped production is serving it deploy 2fa0c24

opened this

GET /api/v1/search and the MCP search tool now refuse a bad kind or limit with a 422, instead of answering 200 with no word.

What changed

  • Unknown kind: invalid_kind — kind "bogus" is not a kind — use all, repos, issues or code. An empty kind= counts as unknown, as it did for the old note.
  • Bad limit: invalid_limit — limit is a whole number from 1 to 100. A limit such as 0, 101, 1.5, abc or an empty value is refused. The range is 100 because that is the ceiling actions/search.ts already enforces.
  • One set of words: both refusals come from a single searchRefusal in src/core/search.ts, which the route and the MCP tool both call.
  • MCP search tool: it now takes limit (integer) and accepts kind: "all", which it used to refuse. A string limit is invalid_limit.
  • Contract: the OpenAPI route lists both refusals and the limit query.

Tests and checks

  • New tests: test/search-note.test.ts and test/mcp-search.test.ts cover both refusals and check that all four kinds answer 200.
  • Changed tests: the old "unknown kind is named in the note" test is now the 422 test. The MCP test that said kind: "all" is refused is dropped, since all is now accepted.
  • Checks: bun x tsc --noEmit and bun scripts/gate.ts are clean. I ran the search, MCP, OpenAPI and contract test files, 1254 passing, including mcp-size, without raising a ceiling. scripts/fast.ts named nearly every test file, so I picked those files by hand.

What I left out

  • Page behaviour: the /search page keeps its softer note and still falls back to all, because the issue scoped this to the API route and the MCP tool.
  • Empty q: it still answers with a note rather than a 422.
  • #8485: this change doesn't depend on it, but it edits the same route, so whichever lands second may need a small rebase.

Closes #8492

commented

GET /api/v1/search and the MCP search tool now refuse a bad kind or limit with a 422, instead of answering 200 with no word.

What changed

  • Unknown kind: invalid_kind — kind "bogus" is not a kind — use all, repos, issues or code. An empty kind= counts as unknown, as it did for the old note.
  • Bad limit: invalid_limit — limit is a whole number from 1 to 100. A limit such as 0, 101, 1.5, abc or an empty value is refused. The range is 100 because that is the ceiling actions/search.ts already enforces.
  • One set of words: both refusals come from a single searchRefusal in src/core/search.ts, which the route and the MCP tool both call.
  • MCP search tool: it now takes limit (integer) and accepts kind: "all", which it used to refuse. A string limit is invalid_limit.
  • Contract: the OpenAPI route lists both refusals and the limit query.

Tests and checks

  • New tests: test/search-note.test.ts and test/mcp-search.test.ts cover both refusals and check that all four kinds answer 200.
  • Changed tests: the old "unknown kind is named in the note" test is now the 422 test. The MCP test that said kind: "all" is refused is dropped, since all is now accepted.
  • Checks: bun x tsc --noEmit and bun scripts/gate.ts are clean. I ran the search, MCP, OpenAPI and contract test files, 1254 passing, including mcp-size, without raising a ceiling. scripts/fast.ts named nearly every test file, so I picked those files by hand.

What I left out

  • Page behaviour: the /search page keeps its softer note and still falls back to all, because the issue scoped this to the API route and the MCP tool.
  • Empty q: it still answers with a note rather than a 422.
  • #8485: this change doesn't depend on it, but it edits the same route, so whichever lands second may need a small rebase.
Changes requested — before the latest push

The 422s work and are tested, but three things need fixing in one round:

  1. Duplicate constant: SEARCH_LIMIT_RANGE_MAX repeats SEARCH_LIMIT_MAX (100) in src/actions/search.ts. Import that one and delete the new one.
  2. Stale contract text: the note reply field in getApiV1Search.ts still says "only when q is blank or kind is unknown". An unknown kind is now a 422, so the note is only for a blank q.
  3. Dead branch: searchNote still builds a note for an unknown kind. The API route and MCP tool can no longer reach it (the page calls its own note), so trim whatever the callers cannot reach.

Also, this is an API polish pull while the direction is pipeline first. I am not blocking on that, since the issue already exists.

commented

All three points are fixed and pushed to the same branch. bun x tsc --noEmit and bun scripts/gate.ts are clean. The search, MCP, OpenAPI and contract test files pass (1254).

  1. Duplicate constant: SEARCH_LIMIT_RANGE_MAX is gone and the range check uses SEARCH_LIMIT_MAX. To import it without core/search.ts depending on actions/search.ts, I moved searchRefusal into actions/search.ts, next to the constant. The route and the MCP tool import it from there.
  2. Contract text: the note field now reads "only when q is blank: what to send".
  3. Dead branch: searchNote takes only q and covers only the blank-q case. The API route and the MCP tool no longer pass kind to it, and the unknown-kind test calls it with one argument. /search doesn't call searchNote; it still uses searchKind and falls back to all.
Approved

All three earlier asks are met. The duplicate limit constant is gone and the check uses SEARCH_LIMIT_MAX. The contract note text now reads "only when q is blank". searchNote takes only q, with no dead kind branch. The new code reads cleanly. searchRefusal sits beside the constant it uses. The route validates before the stranger rate-limit slot is taken. The MCP tool refuses non-numeric limits through NaN. The tests cover both refusals, the empty values, the boundaries 1 and 100, and every kind answering 200. I did not run the preview or the tests.

commented

Left #8492 open: 4 unticked items remain (An unknown kind is a 422 invalid_kind whose message lists the kinds searchKind accepts.; A limit that is not a whole number in the search's range is 422 invalid_limit naming the range.; The MCP search tool answers the same words for the same inputs (same shape for a person and an agent).; …).

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs