Skip to content

fix(mcp): keep zero-result lookups out of isError - #1

Merged
fahreddinozcan merged 1 commit into
AlexRixten:fix/query-docs-iserrorfrom
upstash:fix/iserror-empty-results
Aug 6, 2026
Merged

fix(mcp): keep zero-result lookups out of isError#1
fahreddinozcan merged 1 commit into
AlexRixten:fix/query-docs-iserrorfrom
upstash:fix/iserror-empty-results

Conversation

@fahreddinozcan

@fahreddinozcan fahreddinozcan commented Aug 6, 2026

Copy link
Copy Markdown

Stops resolve-library-id and query-docs from flagging "nothing matched" as a tool execution error, which upstash#2989 currently does despite its changeset promising otherwise.

  • The API reports zero results as a 404 carrying no_libraries_found / no_relevant_snippets. parseErrorResponse discarded that code and kept only the message, so every 404 became isError: true.
  • parseErrorResponse now returns { message, isError }. The status fallback moves to a helper so its branches stay byte-identical.
  • SearchResponse gains isError, so resolve-library-id stops inferring failure from the presence of display text.
  • Benign outcomes are no longer logged to stderr as errors.
  • Adds coverage for both empty-result codes, library_not_found on the same 404 status, and searchLibraries, which had no tests.

library_not_found stays an error. Error text is unchanged everywhere, so the model still sees "Try a different search term".

Why

The spec does not address empty result sets. It says isError is for tool calls that failed, and also that it exists so the LLM can self-correct, which cuts both ways. The tiebreaker is what clients do: the v2 SDK's own callTool example returns early on isError without showing content. Leaving the flag unset is safe under every host because the message text already tells the model to retry, whereas setting it risks swallowing that guidance on the most common benign outcome.

Verified

typecheck, lint, build, test (54/54). Live against the real Context7 API over stdio:

case before after
zero search matches true unset
query matched no snippets true unset
bad library id true true
valid search / valid docs unset unset
unreachable API, all calls true true

The API reports "nothing matched" as a 404 carrying a machine-readable
code (no_libraries_found, no_relevant_snippets), so status alone cannot
separate it from a real failure. parseErrorResponse discarded that code
and returned only the message, so every 404 became isError: true.

That contradicted the changeset ("an empty search result set is still a
success"). It also works against both tools' instructions: their
messages ask the model to refine its query, while isError tells the
client the call failed, and the SDK's own callTool example returns early
on isError without showing the content.

parseErrorResponse now returns { message, isError }; the status fallback
moves to a helper so its branches stay unchanged. Benign outcomes are no
longer logged to stderr as errors.

Tests cover both empty-result codes, library_not_found sharing the same
404 status, and searchLibraries, which had none.
@fahreddinozcan
fahreddinozcan force-pushed the fix/iserror-empty-results branch from 4c8efbd to 4dc8477 Compare August 6, 2026 08:06
@fahreddinozcan
fahreddinozcan merged commit 4dc8477 into AlexRixten:fix/query-docs-iserror Aug 6, 2026
@fahreddinozcan
fahreddinozcan deleted the fix/iserror-empty-results branch August 6, 2026 08:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant