[SCAL-323294] changes for fetch_data tool - #179
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces two new tools, search_objects and fetch_data, to the MCP server for searching and retrieving data from ThoughtSpot objects (Answers and Liveboards). It includes new schemas, gRPC-backed API clients, terminology mapping for synonyms, and comprehensive unit tests. The reviewer identified a critical issue in src/servers/mcp-server.ts where the asynchronous validateConnection method is not awaited in the Ping tool case, causing the check to always evaluate to true.
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.
| case ToolName.Ping: { | ||
| if (this.ctx.props.accessToken && this.ctx.props.instanceUrl) { | ||
| if (!this.getThoughtSpotService(recorder).validateConnection()) { | ||
| if (!this.getThoughtSpotService(recorder).validateConnection()) { |
There was a problem hiding this comment.
The validateConnection method is asynchronous and returns a Promise<boolean>. Since it is not awaited here, the condition !promise will always evaluate to false because any Promise object is truthy. This effectively bypasses the connection validation check. Please add await to correctly evaluate the promise result.
| if (!this.getThoughtSpotService(recorder).validateConnection()) { | |
| if (!(await this.getThoughtSpotService(recorder).validateConnection())) { |
References
- When a method returns a Promise, ensure it is awaited to prevent race conditions and potential data loss, especially when subsequent operations depend on its completion.
2b7918b to
de4930a
Compare
|
@copilot review the pr |
Reviewed the PR and addressed one issue: |
352f265 to
6454d11
Compare
|
@copilot review the pr |
6454d11 to
ddaf1fa
Compare
| ), | ||
| columns: z.array(z.string()).describe("The column names, in order."), | ||
| data_rows: z | ||
| .array(z.array(z.unknown())) |
There was a problem hiding this comment.
Is there a way to add a proper type contract
There was a problem hiding this comment.
Tightened to string | number | boolean | null datatypes.
|
|
||
| // Round numeric cells to 2 decimals: collapses FP noise and trims payload. | ||
| // Non-numbers and non-finite values pass through untouched. | ||
| function roundCell(value: unknown): unknown { |
There was a problem hiding this comment.
This seems arbitrary, what if the customer wants more precision? I feel we should return the full value for now, and not prematurely optimize this.
There was a problem hiding this comment.
Removed — cells now returned at full precision.
| // treats it as "must hold the whole viz" and 500s when it's too small. | ||
| // Start at maxRows; on that error, bump to the required count and refetch, | ||
| // then cap rows client-side below. Attempts are bounded (a full Liveboard | ||
| // can report a larger viz on each retry) to avoid a runaway loop. |
There was a problem hiding this comment.
This seems like a hacky design, we should make the API call such that it is successful on the first try. Is there no way to do that using this API?
There was a problem hiding this comment.
Reworked to one call — Answers pass record_size = max_rows,
Liveboards request the max 32-bit int.
Verified on local instance.
349b5d8 to
cb87e5c
Compare
|
@copilot review the PR |
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces the get_object_data tool to fetch tabular data for saved Answers and Liveboards, gated by a new canDownloadData privilege check. It maps the REST v2 metadata endpoints, normalizes data formats, and includes comprehensive integration and unit tests. Feedback recommends improving type safety in the catch block of callGetObjectData by verifying if the error is an instance of Error before accessing its message, and refactoring the supported object types into a shared constant to eliminate duplication between the Zod schema and the local enum.
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
rifdhan-test-app | 376bd4b | Aug 27 2026, 08:34 AM |
|
|
||
| // "Unbounded" record_size for Liveboards (they 500 if it can't hold the whole | ||
| // viz). Max 32-bit signed int — the endpoint reads it as a GraphQL Int. | ||
| export const LIVEBOARD_RECORD_SIZE = 2_147_483_647; |
There was a problem hiding this comment.
Liveboard fetches send record_size = 2,147,483,647 and apply max_rows only client-side, so the Worker downloads the entire result set of every visualization before slicing to 25 rows.
There was a problem hiding this comment.
ack. The Liveboard /data endpoint 500s when record_size is below the viz's full row count, so it can't be bounded server-side — record_size is int32-max and max_rows caps client-side.
so we fetch complete liveboard vizzes, and then use min(numViz, 25) objects to explain the liveboard.
| .optional() | ||
| .describe( | ||
| "Set only when this result is a specific visualization on a Liveboard: `object_id` is the Liveboard and this is the visualization. Pass it to fetch_data as `visualization_ids` to fetch just this viz.", | ||
| "Set only when this result is a specific visualization on a Liveboard: `object_id` is the Liveboard and this is the visualization. Pass it to get_object_data as `visualization_ids` to fetch just this viz.", |
There was a problem hiding this comment.
we are unconditionally instructing the model to call get_object_data but it sbehind privilege
| `Fetched data for ${object_id} (${result.data.length} result(s))`, | ||
| ); | ||
| } catch (error) { | ||
| // Surface the upstream message (e.g. status 401/500) so the failure is |
There was a problem hiding this comment.
this is too generic catch all error. 401 errors have the dispatch-layer 401 handler to return explicit reauthentication message
| } | ||
|
|
||
| @WithSpan("call-get-object-data") | ||
| async callGetObjectData( |
There was a problem hiding this comment.
callGetObjectData checks canDownloadData() but doesn't wait for ensureSessionInfo()
it should do similar to listTools
There was a problem hiding this comment.
added await this.ensureSessionInfo() before the gate, same as listTools
No description provided.