Skip to content

[SCAL-323294] changes for search_objects and added terminologies - #178

Open
saharsh-ts wants to merge 8 commits into
mainfrom
SCAL-323294_search-objects
Open

[SCAL-323294] changes for search_objects and added terminologies#178
saharsh-ts wants to merge 8 commits into
mainfrom
SCAL-323294_search-objects

Conversation

@saharsh-ts

Copy link
Copy Markdown
Collaborator

No description provided.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread src/servers/mcp-server.ts Outdated
@mouryabalabhadra

Copy link
Copy Markdown
Collaborator

@copilot please review

Copilot AI commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

@copilot please review

Reviewed the changes. All 676 tests pass and lint is clean.

src/thoughtspot/terminology.ts

  • Single-source-of-truth glossary with derived OBJECT_TYPE_FACET_MAP is clean and well-tested (100% coverage).
  • resolveObjectTypeFacet correctly trims and lowercases before lookup and falls back gracefully for unknown types.

src/thoughtspot/grpc/search_objects/

  • GraphQL query (search-objects-query.ts) is trimmed to only the fields the mapper reads.
  • buildFrameUrl correctly handles PINBOARD_VIZ_RESULT by routing parentId (Liveboard) and id (viz) into the right URL segments.
  • toEpochMs normalization (seconds → ms when value < 1e12) is a sensible heuristic for the Eureka quirk.
  • Post-filter pagination loop caps at MAX_PAGES = 20 to prevent runaway upstream calls; the warn log on cap is helpful.
  • mergeTermResults round-robin interleave with per-id confidence dedup is correct (Map preserves first-seen order on set of an existing key, so position is stable).

src/servers/tool-definitions.ts / mcp-server.ts

  • SearchObjectsInputSchema validation (union of z.string().min(1) and z.array(z.string().min(1)).nonempty()) guards against empty queries before they reach the backend.
  • Error surfacing in callSearchObjects correctly propagates the upstream message so failures are actionable.

No issues to flag. LGTM.

Comment thread src/servers/tool-definitions.ts Outdated
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.",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@saharsh-ts saharsh-ts Jul 22, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

supporting only single search term/string (can be multi-word, e.g. "sales by region")

Comment thread src/servers/tool-definitions.ts Outdated
.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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we call the field author_name instead of owner

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment thread src/servers/tool-definitions.ts Outdated
.string()
.optional()
.describe(
"Restrict results to objects authored by this user, matched against the author's display name (case-insensitive).",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: prefer "authored by the provided user" rather than "authored by this user" to avoid confusing with the end user themself

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment thread src/servers/tool-definitions.ts Outdated
});

const SearchObjectHeaderSchema = z.object({
id: z

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's call it object_id for consistency

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment thread src/servers/tool-definitions.ts Outdated
.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."),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's call it title for consistency with other schemas

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment thread src/thoughtspot/search-objects/search-objects-query.ts
Comment thread src/thoughtspot/search-objects/search-objects.ts Outdated
Comment thread src/thoughtspot/grpc/grpc-utils.ts Outdated

// 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(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is duplicate, use buildHeaders existing util and extend if needed

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment thread src/thoughtspot/terminology.ts Outdated
export const TERMINOLOGY: TerminologyEntry[] = [
{
canonical: "Liveboard",
synonyms: ["pinboard", "dashboard", "board"],

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is canonical capitalized but synonyms are not? Should we make it consistent

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated in object-types.ts

Comment thread src/thoughtspot/thoughtspot-service.ts
@saharsh-ts
saharsh-ts force-pushed the SCAL-323294_search-objects branch from 22b6428 to 959eeed Compare July 20, 2026 09:47
Comment thread src/servers/mcp-server.ts Outdated
});

// no_results and error are returned as structured content (not a
// protocol error) so the model gets the typed envelope to render.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does "typed envelope to render" mean? This comment is a bit unclear to me.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread src/servers/tool-definitions.ts Outdated
description: z
.string()
.nullable()
.describe("The description of the object, or null when it has none."),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer to make this optional and omit if not present, rather than nullable. This is how we handle other fields in other places.

@saharsh-ts saharsh-ts Jul 24, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

description is optional now, omitted when absent.

Comment thread src/servers/tool-definitions.ts Outdated
.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.",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same, last_modified optional/omitted.

Comment thread src/servers/tool-definitions.ts Outdated
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."),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it, prefer to call this field something like external_link

Comment thread src/servers/tool-definitions.ts Outdated
.string()
.optional()
.describe(
"Present on no_results: human-readable 'nothing matched' note; safe to relay to the user.",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need this? The host agent can display a reasonable no results message themselves.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dropped the no-results message; the host agent renders its own.

Comment thread src/servers/tool-definitions.ts Outdated
request_id: z
.string()
.describe(
"Correlation id sent on the upstream call as x-request-id; trace this in ThoughtSpot's server logs.",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have inconsistent naming for utils in this file, eg "buildFrameUrl", "toEpochMs", "deriveQuery". Let's use a conistent verb if possible.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consistent to* naming, moved to a mapper module.

facetSelections.push({ facetType: "IS_VERIFIED", facetValue: ["true"] });
}

const endpoint = "/prism/?op=GetEurekaResults";

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inline this

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

);

if (owner) {
const needle = owner.toLowerCase();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does needle mean?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed - it's the lower-cased substring for the post-filter match; now ownerLower/tagLower.

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.

4 participants