Skip to content

Commit 666a75d

Browse files
committed
feat(tools): Improve tool robustness and developer experience
This commit introduces several key improvements to the Gemini Cloud Assist tools to make them more reliable and easier to use. The primary changes include: - **Enhanced Tool Definitions:** The documentation for all troubleshooting tools has been significantly updated to provide clear, strict instructions for use. This includes mandating fully-formed GCP resource URIs and absolute UTC timestamps, which will reduce errors and improve the reliability of tool calls. - **Client-Side Validation:** The API client now proactively checks for authentication and licensing before attempting to make any API calls. This provides clearer, more immediate feedback if credentials are not set up correctly. - **Cleaner Initial Output:** The `create_investigation` tool now returns a more concise summary by default, with the option to include detailed observations and hypotheses when needed. Change-Id: I1e7acff515e557c184785173f48a695b6a17cd1a
1 parent b7dba51 commit 666a75d

File tree

5 files changed

+118
-146
lines changed

5 files changed

+118
-146
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ archive/*
33
personal-docs/*
44
node_modules/*
55
troubleshooting/samples/*
6-
troubleshooting/api/api.integration.test.js
6+
troubleshooting/api/api.integration.test.js
7+
scripts/*

tools.js

Lines changed: 59 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export const registerTools = (server) => {
2424
server.tool(
2525
"fetch_investigation",
2626
`/**
27-
* Fetches Gemini Cloud Assist troubleshooting investigations.
27+
* Fetches Gemini Cloud Assist Investigations.
2828
*
2929
* This function serves as a versatile entry point for retrieving investigation data.
3030
* It can operate in two modes:
@@ -33,13 +33,11 @@ export const registerTools = (server) => {
3333
* troubleshooting investigations associated with that project. It can be
3434
* optionally filtered by title using the filter_expression parameter.
3535
*
36-
* 2. **Get Mode:** If an investigationId is provided, it will fetch the
36+
* 2. **Get Mode:** If an investigationId is also provided, it will fetch the
3737
* detailed report for that specific investigation. If a revisionId is
3838
* also provided, it fetches that particular revision; otherwise, it
3939
* retrieves the latest version.
4040
*
41-
* The function handles constructing the correct API request based on the
42-
* presence of investigationId and revisionId.
4341
*/`,
4442
{
4543
projectId: z.string().describe("The Google Cloud Project ID."),
@@ -67,22 +65,10 @@ export const registerTools = (server) => {
6765
}]
6866
};
6967
} catch (error) {
70-
if (error.name === 'ApiError') {
71-
return {
72-
content: [{
73-
type: 'tool-error',
74-
code: error.code,
75-
message: error.message,
76-
details: error.details
77-
}]
78-
};
79-
}
80-
// Fallback for generic errors
8168
return {
8269
content: [{
83-
type: 'tool-error',
84-
code: 'UNEXPECTED_ERROR',
85-
message: error.message
70+
type: 'text',
71+
text: error.message
8672
}]
8773
};
8874
}
@@ -95,49 +81,37 @@ export const registerTools = (server) => {
9581
server.tool(
9682
'create_investigation',
9783
`/**
98-
* Creates a new Gemini Cloud Assist Investigation.
84+
* Creates a new Gemini Cloud Assist Investigation. This tool is the primary entry point for initiating any new troubleshooting analysis.
85+
*
86+
* Prerequisites:
87+
* Argument Resolution: Before invoking this tool, you **MUST** resolve all user-provided information into the specific formats required by the arguments.
9988
*
100-
* This is the first and mandatory step.
89+
* Resource URI Mandate: The 'relevant_resources' parameter requires a list of full Google Cloud Platform (GCP) resource URIs.
90+
* - **Format:** Each URI **MUST** strictly adhere to the format: //<service>.googleapis.com/<resource-path>.
91+
* - **Validation:** The tool will fail if the provided strings are not well-formed URIs in this exact format.
92+
* - **Resolution:** You are responsible for converting any partial, ambiguous, or incomplete resource names (e.g., "my GKE cluster", "the default nodepool", or "project/zone/resource_type/resource_name") into their full URI representation. Utilize available tools like 'gcloud', 'kubectl', or your internal knowledge base to discover the complete and accurate resource URIs.
93+
* - **GCP Resource URI Reference**: https://cloud.google.com/asset-inventory/docs/asset-names
94+
*
95+
* Example of a correct GCP Resource URI:
96+
* - //compute.googleapis.com/projects/my-gcp-project/zones/us-central1-a/instances/my-vm-instance
10197
*
102-
* This tool is the primary entry point for starting any new troubleshooting
103-
* analysis.
98+
* Additional Argument Formatting:
99+
* - **Timestamp ('start_time'):** Convert all relative time expressions (e.g., "30 minutes ago", "yesterday at 5pm") into the absolute 'YYYY-MM-DDTHH:mm:ssZ' UTC format. The 'Shell' tool with the 'date' command can be used for this conversion.
100+
* - **Project ID ('project_id'):** If a project is not explicitly mentioned by the user, you must determine the correct one from the context of the conversation or by using the command 'gcloud config get-value project'.
104101
*
105-
* **Prerequisites: Argument Resolution**
106-
* Before calling this tool, you **MUST** resolve all user-provided information
107-
* into the required, specific formats using your available tools.
108-
* - **Resource URIs ('relevant_resources')**: Convert vague names (e.g., "my
109-
* GKE cluster", "the default nodepool") into full resource URIs. Use
110-
* 'gcloud', 'kubectl', or 'Memorybank' to find the exact paths.
111-
* - **Timestamp ('start_time')**: Convert relative times (e.g., "30 minutes
112-
* ago", "yesterday at 5pm") into the absolute 'YYYY-MM-DDTHH:mm:ssZ'
113-
* (UTC) format. Use the 'Shell' tool (e.g., with the 'date' command).
114-
* - **Project ID ('project_id')**: If the user doesn't specify a project,
115-
* determine the correct one from context or by using 'gcloud config get-value
116-
* project'.
117-
* **If you cannot resolve any of this information, you MUST ask the user for
118-
* clarification before proceeding.**
102+
* **Crucial:** If you are unable to resolve any of this information into the required formats, you **MUST** seek clarification from the user before proceeding to call this tool.
119103
*
120-
* @param {string} projectId [REQUIRED] The fully-resolved Google Cloud Project ID.
121-
* @param {string} title [REQUIRED] A human-readable title. You MUST prefix the title with "[Gemini CLI]".
122-
* @param {string} issue_description [REQUIRED] A detailed description of the issue.
123-
* @param {Array<string>} relevant_resources [REQUIRED] A list of fully-resolved resource URIs.
124-
* @param {string} start_time [REQUIRED] The investigation start time, formatted as 'YYYY-MM-DDTHH:mm:ssZ' (UTC).
125-
* @returns {string} A summary of the new investigation, structured with markdown. You
126-
* **MUST**
127-
* parse this output to find the '**Investigation Path**' and
128-
* '**Revision Path**'
129-
* fields. The final segment of the 'Investigation Path' is the
130-
* 'investigation_id',
131-
* and the final segment of the 'Revision Path' is the 'revision_id'.
132-
* These
133-
* are required for subsequent tool calls.
104+
* @returns {string} A summary of the new investigation, structured with Markdown.
105+
* You **MUST** parse this output to find the '**Investigation Path**' and '**Revision Path**'
106+
* fields. The final segment of the 'Investigation Path' is the 'investigation_id' and the
107+
* final segment of the 'Revision Path' is the 'revision_id'. These are required for subsequent tool calls.
134108
*/`,
135109
{
136-
projectId: z.string().describe('The Google Cloud Project ID.'),
137-
title: z.string().describe('The title of the investigation.'),
138-
issue_description: z.string().describe('A description of the issue.'),
139-
relevant_resources: z.array(z.string()).describe('A list of relevant resources.'),
140-
start_time: z.string().describe('The start time of the issue in RFC3339 UTC "Zulu" format.')
110+
projectId: z.string().describe("The Google Cloud Project ID."),
111+
title: z.string().describe("A human-readable title. You MUST prefix the title with \"[Gemini CLI]\""),
112+
issue_description: z.string().describe("A detailed comprehensive description of the issue including relevant tool outputs."),
113+
relevant_resources: z.array(z.string()).describe("A list of fully-resolved GCP resource URIs, each starting with '//<service>.googleapis.com/...'. For example: '//compute.googleapis.com/projects/my-project/zones/us-central1-a/instances/my-instance-name'."),
114+
start_time: z.string().describe("The investigation start time, formatted as 'YYYY-MM-DDTHH:mm:ssZ' (UTC).")
141115
},
142116
async ({
143117
projectId,
@@ -158,7 +132,7 @@ export const registerTools = (server) => {
158132
try {
159133
const result = await client.createInvestigation(projectId, investigation);
160134
const viewer = new InvestigationViewer(result);
161-
const formattedOutput = viewer.render();
135+
const formattedOutput = viewer.render({ showObservationsAndHypotheses: false });
162136

163137
return {
164138
content: [{
@@ -167,22 +141,10 @@ export const registerTools = (server) => {
167141
}]
168142
};
169143
} catch (error) {
170-
if (error.name === 'ApiError') {
171-
return {
172-
content: [{
173-
type: 'tool-error',
174-
code: error.code,
175-
message: error.message,
176-
details: error.details
177-
}]
178-
};
179-
}
180-
// Fallback for generic errors
181144
return {
182145
content: [{
183-
type: 'tool-error',
184-
code: 'UNEXPECTED_ERROR',
185-
message: error.message
146+
type: 'text',
147+
text: error.message
186148
}]
187149
};
188150
}
@@ -203,9 +165,6 @@ export const registerTools = (server) => {
203165
* detailed report. There is no need to call any other tool after this to get the
204166
* results.
205167
*
206-
* @param {string} projectId [REQUIRED] The GCP Project ID where the investigation resides.
207-
* @param {string} investigationId [REQUIRED] The ID of the investigation to run.
208-
* @param {string} revisionId [REQUIRED] The specific revision ID to run.
209168
* @returns {string} A detailed troubleshooting report in a structured string format. The
210169
* report is organized with '##' headers for sections like 'Issue',
211170
* 'Hypotheses', 'Relevant Observations', and 'Remediation'.
@@ -214,9 +173,9 @@ export const registerTools = (server) => {
214173
* a clear summary of the findings (or lack thereof) to the user.
215174
*/`,
216175
{
217-
projectId: z.string().describe('The Google Cloud Project ID.'),
218-
investigationId: z.string().describe('The ID of the investigation to run.'),
219-
revisionId: z.string().describe('The revision ID of the investigation to run.'),
176+
projectId: z.string().describe("The GCP Project ID where the investigation resides."),
177+
investigationId: z.string().describe("The ID of the investigation to run."),
178+
revisionId: z.string().describe("The specific revision ID to run."),
220179
},
221180
async ({
222181
projectId,
@@ -243,28 +202,13 @@ export const registerTools = (server) => {
243202
}]
244203
};
245204
} catch (error) {
246-
if (error.name === 'ApiError') {
247-
return {
248-
content: [{
249-
type: 'tool-error',
250-
code: error.code,
251-
message: error.message,
252-
details: error.details
253-
}]
254-
};
255-
}
256-
// Fallback for generic errors
257205
return {
258206
content: [{
259-
type: 'tool-error',
260-
code: 'UNEXPECTED_ERROR',
261-
message: error.message
207+
type: 'text',
208+
text: error.message
262209
}]
263210
};
264211
}
265-
},
266-
{
267-
'mcp:tool-class': 'idempotent'
268212
}
269213
);
270214

@@ -273,34 +217,32 @@ export const registerTools = (server) => {
273217
`/**
274218
* Adds a new user observation to an existing investigation.
275219
*
276-
* **Prerequisites: Argument Resolution**
277-
* Before calling this tool, you **MUST** resolve any new resource names
278-
* mentioned in the user's observation.
279-
* - **Resource URIs ('relevant_resources')**: If the user's 'observation'
280-
* mentions new resources, convert their vague names into full resource URIs
281-
* using 'gcloud', 'kubectl', or 'Memorybank'. If no new resources are
282-
* mentioned, provide an empty list '[]'.
283-
* **If you cannot resolve a resource name, you MUST ask the user for
284-
* clarification before proceeding.**
220+
* Prerequisites:
221+
* Argument Resolution: Before invoking this tool, you **MUST** resolve any new resource names mentioned in the user's observation into the specific formats required by the arguments.
222+
*
223+
* Resource URI Mandate: If the user's 'observation' mentions new resources, the 'relevant_resources' parameter requires a list of full Google Cloud Platform (GCP) resource URIs. If no new resources are mentioned, provide an empty list '[]'.
224+
* - **Format:** Each URI **MUST** strictly adhere to the format: //<service>.googleapis.com/<resource-path>.
225+
* - **Validation:** The tool will fail if any provided strings are not well-formed URIs in this exact format.
226+
* - **Resolution:** You are responsible for converting any partial, ambiguous, or incomplete resource names into their full URI representation. Utilize available tools like 'gcloud', 'kubectl', or your internal knowledge base to discover the complete and accurate resource URIs.
227+
* - **GCP Resource URI Reference**: https://cloud.google.com/asset-inventory/docs/asset-names
228+
*
229+
* Example of a correct GCP Resource URI:
230+
* - //compute.googleapis.com/projects/my-gcp-project/zones/us-central1-a/instances/my-vm-instance
285231
*
286-
* **Workflow:** After adding an observation, you **MUST** call
287-
* 'run_investigation'
288-
* on the new revision to re-analyze with the added context.
232+
* **Crucial:** If you cannot resolve a new resource name into the required format, you **MUST** seek clarification from the user before proceeding to call this tool.
289233
*
290-
* @param {string} projectId [REQUIRED] The GCP Project ID where the investigation resides.
291-
* @param {string} investigationId [REQUIRED] The ID of the investigation.
292-
* @param {string} observation [REQUIRED] The new information or question from the user.
293-
* @param {Array<string>} relevant_resources [REQUIRED] A list of fully-resolved resource URIs for any new resources mentioned in the observation.
294-
* @returns {string} A string summary of the updated investigation, structured with markdown.
234+
* Workflow: After adding an observation, you **MUST** call 'run_investigation' on the new revision to re-analyze with the added context.
235+
*
236+
* @returns {string} A string summary of the updated investigation, structured with Markdown.
295237
* You **MUST** parse this output to find the '**Revision Path**' field.
296238
* The final segment of this path is the new 'revision_id' that you must
297239
* use for the subsequent 'run_investigation' call.
298240
*/`,
299241
{
300-
projectId: z.string().describe('The Google Cloud Project ID.'),
301-
investigationId: z.string().describe('The ID of the investigation.'),
302-
observation: z.string().describe('The new information or question from the user.'),
303-
relevant_resources: z.array(z.string()).describe('A list of fully-resolved resource URIs for any new resources mentioned in the observation.'),
242+
projectId: z.string().describe("The GCP Project ID where the investigation resides."),
243+
investigationId: z.string().describe("The ID of the investigation."),
244+
observation: z.string().describe("The new information or question from the user."),
245+
relevant_resources: z.array(z.string()).describe("A list of fully-resolved GCP resource URIs for any new resources mentioned in the observation, each starting with '//<service>.googleapis.com/...'. Provide an empty list if no new resources are mentioned."),
304246
},
305247
async ({ projectId, investigationId, observation, relevant_resources }) => {
306248
const client = new GeminiCloudAssistClient();
@@ -320,26 +262,13 @@ export const registerTools = (server) => {
320262
}]
321263
};
322264
} catch (error) {
323-
if (error.name === 'ApiError') {
324-
return {
325-
content: [{
326-
type: 'tool-error',
327-
code: error.code,
328-
message: error.message,
329-
details: error.details
330-
}]
331-
};
332-
}
333-
// Fallback for generic errors
334265
return {
335266
content: [{
336-
type: 'tool-error',
337-
code: 'UNEXPECTED_ERROR',
338-
message: error.message
267+
type: 'text',
268+
text: error.message
339269
}]
340270
};
341271
}
342272
}
343273
);
344-
};
345-
274+
};

0 commit comments

Comments
 (0)