[SCAL-323294] changes for search_objects and added terminologies - #178
[SCAL-323294] changes for search_objects and added terminologies#178saharsh-ts wants to merge 8 commits into
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces a new search_objects tool to the MCP server, allowing users to search for answers, liveboards, worksheets, and other metadata in ThoughtSpot. The implementation includes GraphQL query definitions, terminology mapping for synonyms, client-side post-filtering, parallel multi-term search merging, and comprehensive unit tests. During the review, a critical issue was identified in the Ping tool handler where the asynchronous validateConnection() method is not awaited, which would cause the connection check to always evaluate as truthy.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
|
@copilot please review |
Reviewed the changes. All 676 tests pass and lint is clean.
No issues to flag. LGTM. |
| query: z | ||
| .union([z.string().min(1), z.array(z.string().min(1)).nonempty()]) | ||
| .describe( | ||
| "The search term(s) to find objects for, matched against object names or descriptions. Pass a single string for one search, or an array of distinct terms when the user's request covers several separate things. Each term is searched in parallel and the results are merged and de-duplicated. Note: pagination via `cursor` only applies to a single-term search.", |
There was a problem hiding this comment.
Is there a prominent need to support multiple disjoint search terms? I feel a single search term will be the 95% case and would prefer simplifying the contract to keep it simple for agent understanding. Especially since some features (eg cursor) won't work in that mode, it can lead to more problems.
There was a problem hiding this comment.
supporting only single search term/string (can be multi-word, e.g. "sales by region")
| .describe( | ||
| "Restrict results to these object types. Accepts friendly and legacy names: 'liveboard' (aliases 'pinboard', 'dashboard'), 'answer', and 'worksheet' (aliases 'logical table', 'data model', 'data source'). Omit to search all types.", | ||
| ), | ||
| owner: z |
There was a problem hiding this comment.
Should we call the field author_name instead of owner
| .string() | ||
| .optional() | ||
| .describe( | ||
| "Restrict results to objects authored by this user, matched against the author's display name (case-insensitive).", |
There was a problem hiding this comment.
Nit: prefer "authored by the provided user" rather than "authored by this user" to avoid confusing with the end user themself
| }); | ||
|
|
||
| const SearchObjectHeaderSchema = z.object({ | ||
| id: z |
There was a problem hiding this comment.
Let's call it object_id for consistency
| .describe( | ||
| "Set only when this result is a specific visualization on a Liveboard: `id` is the Liveboard and this is the visualization. Pass it to fetch_data as `visualization_ids` to fetch just this viz.", | ||
| ), | ||
| name: z.string().describe("The display name/title of the object."), |
There was a problem hiding this comment.
Let's call it title for consistency with other schemas
|
|
||
| // Standard headers for ThoughtSpot HTTP calls. `acceptLanguage` is opt-in: the | ||
| // gRPC/Eureka endpoints 500 without it, but the legacy calls never sent it. | ||
| export function buildTsHeaders( |
There was a problem hiding this comment.
This is duplicate, use buildHeaders existing util and extend if needed
| export const TERMINOLOGY: TerminologyEntry[] = [ | ||
| { | ||
| canonical: "Liveboard", | ||
| synonyms: ["pinboard", "dashboard", "board"], |
There was a problem hiding this comment.
Why is canonical capitalized but synonyms are not? Should we make it consistent
There was a problem hiding this comment.
updated in object-types.ts
22b6428 to
959eeed
Compare
| }); | ||
|
|
||
| // no_results and error are returned as structured content (not a | ||
| // protocol error) so the model gets the typed envelope to render. |
There was a problem hiding this comment.
What does "typed envelope to render" mean? This comment is a bit unclear to me.
There was a problem hiding this comment.
simplified it. it was basically to say that no_results/error are returned as normal structured content (not an MCP protocol error) so the client still gets the structured response.
| description: z | ||
| .string() | ||
| .nullable() | ||
| .describe("The description of the object, or null when it has none."), |
There was a problem hiding this comment.
Prefer to make this optional and omit if not present, rather than nullable. This is how we handle other fields in other places.
There was a problem hiding this comment.
description is optional now, omitted when absent.
| .string() | ||
| .nullable() | ||
| .describe( | ||
| "ISO-8601 timestamp of the last modification (e.g. 2026-05-15T14:30:00Z), or null when unavailable. Render as a plain date.", |
There was a problem hiding this comment.
same, last_modified optional/omitted.
| verified: z.boolean().describe("Whether the object is marked as verified."), | ||
| frame_url: z | ||
| .string() | ||
| .describe("Deep link to open the object in the ThoughtSpot UI."), |
There was a problem hiding this comment.
Got it, prefer to call this field something like external_link
| .string() | ||
| .optional() | ||
| .describe( | ||
| "Present on no_results: human-readable 'nothing matched' note; safe to relay to the user.", |
There was a problem hiding this comment.
Why do we need this? The host agent can display a reasonable no results message themselves.
There was a problem hiding this comment.
dropped the no-results message; the host agent renders its own.
| request_id: z | ||
| .string() | ||
| .describe( | ||
| "Correlation id sent on the upstream call as x-request-id; trace this in ThoughtSpot's server logs.", |
There was a problem hiding this comment.
Let's remove it, we want to minimize the tool schema surface area as much as possible
| } from "./search-objects-types"; | ||
|
|
||
| // Build a deep link to the object in the ThoughtSpot UI from its result type. | ||
| function buildFrameUrl( |
There was a problem hiding this comment.
We have inconsistent naming for utils in this file, eg "buildFrameUrl", "toEpochMs", "deriveQuery". Let's use a conistent verb if possible.
There was a problem hiding this comment.
consistent to* naming, moved to a mapper module.
| facetSelections.push({ facetType: "IS_VERIFIED", facetValue: ["true"] }); | ||
| } | ||
|
|
||
| const endpoint = "/prism/?op=GetEurekaResults"; |
| ); | ||
|
|
||
| if (owner) { | ||
| const needle = owner.toLowerCase(); |
There was a problem hiding this comment.
Renamed - it's the lower-cased substring for the post-filter match; now ownerLower/tagLower.
No description provided.