feat(search): replace sort clause maps with a typed SortClause record - #164
feat(search): replace sort clause maps with a typed SortClause record#164adityamparikh wants to merge 1 commit into
Conversation
The search tool's sortClauses parameter was List<Map<String,String>> keyed by magic "item"/"order" strings that the generated JSON schema cannot express, so MCP clients had to guess the keys. A record makes the components named schema properties and the contract self-documenting. The record also normalizes input the way LLM clients actually send it: order is case-insensitive and defaults to asc when omitted; an unknown order or missing field fails with a message that names the offending value so clients can self-correct on retry. Registered in SolrNativeHints alongside the response records: tool parameter records are bound reflectively by Jackson and need the same native-image treatment. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: adityamparikh <aditya.m.parikh@gmail.com>
7fad8e3 to
8746845
Compare
|
Rebased onto 1. 2. Reconciled with #176's map validation. 3. Updated two test call sites that did not exist when this PR was written. 4. Verified: |
The note quoted the sort clause shape as {"item": ..., "order": ...}. apache#164
replaces that map with a typed SortClause record keyed on "field", so the
example would have become wrong the moment that PR merged — and this tutorial
already carries one merge-order dependency.
The underlying caveat holds either way: undeclared arguments are dropped rather
than rejected, so a misnamed one reads as a query that quietly ignored part of
the request. Keeps that, drops the key names, and points at the client's tool
inspector as the durable way to check.
Signed-off-by: Aditya Parikh <aditya.m.parikh@gmail.com>
Motivation
The
searchtool'ssortClausesparameter isList<Map<String, String>>with magic"item"/"order"keys. A JSON schema cannot express required keys of a map, so the tool schema MCP clients receive says nothing about what to send — LLM clients guess (field/directionis a common wrong guess) and sorting silently doesn't apply (map.get(...)returns null).A typed record turns the keys into named schema properties: the contract becomes machine-checkable instead of guessable.
Changes
SortClause(field, order)record in thesearchpackage;sortClausesis nowList<SortClause>.orderis case-insensitive (DESCworks) and defaults toascwhen omitted; an unrecognized order or missing field throwsIllegalArgumentExceptionnaming the offending value, so the client can self-correct on the next call.SORT_ITEM/SORT_ORDERconstants.SolrNativeHints(tool parameter records are deserialized reflectively by Jackson, same as response records; list renamedMCP_RESPONSE_RECORDS→MCP_TOOL_RECORDS).SortClauseTestcovering conversion, case normalization, default order, and both rejection paths; existing unit + integration tests updated to the record.Note: this changes the tool's parameter schema. Map-shaped calls from existing clients would stop matching — but they only "worked" if the client guessed both magic keys correctly, which the schema never told them.
./gradlew buildpasses (unit + Testcontainers integration tests).🤖 Generated with Claude Code