@gcnv/gcnv-mcp-server 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/GEMINI.md +200 -0
  2. package/LICENSE +201 -0
  3. package/README.md +374 -0
  4. package/build/index.js +185 -0
  5. package/build/logger.js +19 -0
  6. package/build/registry/register-tools.js +101 -0
  7. package/build/registry/tool-registry.js +27 -0
  8. package/build/tools/active-directory-tools.js +124 -0
  9. package/build/tools/backup-policy-tools.js +140 -0
  10. package/build/tools/backup-tools.js +178 -0
  11. package/build/tools/backup-vault-tools.js +147 -0
  12. package/build/tools/handlers/active-directory-handler.js +321 -0
  13. package/build/tools/handlers/backup-handler.js +451 -0
  14. package/build/tools/handlers/backup-policy-handler.js +275 -0
  15. package/build/tools/handlers/backup-vault-handler.js +370 -0
  16. package/build/tools/handlers/kms-config-handler.js +327 -0
  17. package/build/tools/handlers/operation-handler.js +254 -0
  18. package/build/tools/handlers/quota-rule-handler.js +411 -0
  19. package/build/tools/handlers/replication-handler.js +504 -0
  20. package/build/tools/handlers/snapshot-handler.js +320 -0
  21. package/build/tools/handlers/storage-pool-handler.js +346 -0
  22. package/build/tools/handlers/volume-handler.js +353 -0
  23. package/build/tools/kms-config-tools.js +162 -0
  24. package/build/tools/operation-tools.js +64 -0
  25. package/build/tools/quota-rule-tools.js +166 -0
  26. package/build/tools/replication-tools.js +227 -0
  27. package/build/tools/snapshot-tools.js +124 -0
  28. package/build/tools/storage-pool-tools.js +215 -0
  29. package/build/tools/volume-tools.js +216 -0
  30. package/build/types/tool.js +1 -0
  31. package/build/utils/netapp-client-factory.js +53 -0
  32. package/gemini-extension.json +12 -0
  33. package/package.json +66 -0
@@ -0,0 +1,275 @@
1
+ import { NetAppClientFactory } from '../../utils/netapp-client-factory.js';
2
+ import { createBackupPolicyTool, deleteBackupPolicyTool, getBackupPolicyTool, listBackupPoliciesTool, updateBackupPolicyTool, } from '../backup-policy-tools.js';
3
+ /**
4
+ * Creates a new backup policy
5
+ */
6
+ export const createBackupPolicyHandler = async (args) => {
7
+ try {
8
+ const { projectId, location, backupPolicyId, dailyBackupLimit, weeklyBackupLimit, monthlyBackupLimit, description, enabled, labels, } = args;
9
+ const client = NetAppClientFactory.createClient();
10
+ // Format resource name
11
+ const parent = `projects/${projectId}/locations/${location}`;
12
+ // Build backup policy
13
+ const backupPolicy = {
14
+ dailyBackupLimit,
15
+ weeklyBackupLimit,
16
+ monthlyBackupLimit,
17
+ description,
18
+ enabled,
19
+ labels,
20
+ };
21
+ // Filter out undefined properties
22
+ Object.keys(backupPolicy).forEach((key) => {
23
+ if (backupPolicy[key] === undefined) {
24
+ delete backupPolicy[key];
25
+ }
26
+ });
27
+ // Call the API to create a backup policy
28
+ const [operation] = await client.createBackupPolicy({
29
+ parent,
30
+ backupPolicy,
31
+ backupPolicyId,
32
+ });
33
+ return {
34
+ content: [
35
+ {
36
+ type: 'text',
37
+ text: `Created backup policy: ${backupPolicyId}. Operation ID: ${operation.name}`,
38
+ },
39
+ ],
40
+ structuredContent: {
41
+ name: `projects/${projectId}/locations/${location}/backupPolicy/${backupPolicyId}`,
42
+ operationId: operation.name || '',
43
+ },
44
+ };
45
+ }
46
+ catch (error) {
47
+ return {
48
+ content: [
49
+ {
50
+ type: 'text',
51
+ text: `Failed to create backup policy: ${error.message}`,
52
+ },
53
+ ],
54
+ isError: true,
55
+ };
56
+ }
57
+ };
58
+ /**
59
+ * Deletes a backup policy
60
+ */
61
+ export const deleteBackupPolicyHandler = async (args) => {
62
+ try {
63
+ const { projectId, location, backupPolicyId } = args;
64
+ const client = NetAppClientFactory.createClient();
65
+ // Format resource name
66
+ const name = `projects/${projectId}/locations/${location}/backupPolicies/${backupPolicyId}`;
67
+ // Call the API to delete a backup policy
68
+ const [operation] = await client.deleteBackupPolicy({
69
+ name,
70
+ });
71
+ const result = {
72
+ success: true,
73
+ operationId: operation.name || '',
74
+ };
75
+ return {
76
+ content: [
77
+ {
78
+ type: 'text',
79
+ text: `Deleted backup policy ${backupPolicyId}. Operation ID: ${result.operationId}`,
80
+ },
81
+ ],
82
+ structuredContent: result,
83
+ };
84
+ }
85
+ catch (error) {
86
+ return {
87
+ content: [
88
+ {
89
+ type: 'text',
90
+ text: `Failed to delete backup policy: ${error.message}`,
91
+ },
92
+ ],
93
+ isError: true,
94
+ };
95
+ }
96
+ };
97
+ /**
98
+ * Gets a backup policy
99
+ */
100
+ export const getBackupPolicyHandler = async (args) => {
101
+ try {
102
+ const { projectId, location, backupPolicyId } = args;
103
+ const client = NetAppClientFactory.createClient();
104
+ // Format resource name
105
+ const name = `projects/${projectId}/locations/${location}/backupPolicies/${backupPolicyId}`;
106
+ // Call the API to get a backup policy
107
+ const [backupPolicy] = await client.getBackupPolicy({ name });
108
+ const result = {
109
+ name: backupPolicy.name || '',
110
+ backupPolicyId: backupPolicy.name?.split('/').pop() || '',
111
+ dailyBackupLimit: backupPolicy.dailyBackupLimit,
112
+ weeklyBackupLimit: backupPolicy.weeklyBackupLimit,
113
+ monthlyBackupLimit: backupPolicy.monthlyBackupLimit,
114
+ description: backupPolicy.description,
115
+ enabled: backupPolicy.enabled || false,
116
+ assignedVolumeCount: backupPolicy.assignedVolumeCount,
117
+ state: backupPolicy.state || 'UNKNOWN',
118
+ createTime: backupPolicy.createTime
119
+ ? new Date(Number(backupPolicy.createTime.seconds || 0) * 1000)
120
+ : new Date(),
121
+ labels: backupPolicy.labels,
122
+ };
123
+ return {
124
+ content: [
125
+ {
126
+ type: 'text',
127
+ text: JSON.stringify(result, null, 2),
128
+ },
129
+ ],
130
+ structuredContent: result,
131
+ };
132
+ }
133
+ catch (error) {
134
+ return {
135
+ content: [
136
+ {
137
+ type: 'text',
138
+ text: `Failed to get backup policy: ${error.message}`,
139
+ },
140
+ ],
141
+ isError: true,
142
+ };
143
+ }
144
+ };
145
+ /**
146
+ * Lists backup policies
147
+ */
148
+ export const listBackupPoliciesHandler = async (args) => {
149
+ try {
150
+ const { projectId, location, filter, pageSize, pageToken } = args;
151
+ const client = NetAppClientFactory.createClient();
152
+ // Format resource name
153
+ const parent = `projects/${projectId}/locations/${location}`;
154
+ // Call the API to list backup policies
155
+ const [response] = await client.listBackupPolicies({
156
+ parent,
157
+ filter,
158
+ pageSize,
159
+ pageToken,
160
+ });
161
+ // Handle API response
162
+ const backupPolicies = Array.isArray(response)
163
+ ? response
164
+ : response.backupPolicies || [];
165
+ const nextPageToken = typeof response === 'object' && response !== null
166
+ ? response.nextPageToken || ''
167
+ : '';
168
+ const result = {
169
+ backupPolicies: backupPolicies.map((policy) => ({
170
+ name: policy.name || '',
171
+ backupPolicyId: policy.name?.split('/').pop() || '',
172
+ dailyBackupLimit: policy.dailyBackupLimit,
173
+ weeklyBackupLimit: policy.weeklyBackupLimit,
174
+ monthlyBackupLimit: policy.monthlyBackupLimit,
175
+ description: policy.description,
176
+ enabled: policy.enabled || false,
177
+ assignedVolumeCount: policy.assignedVolumeCount,
178
+ state: policy.state || 'UNKNOWN',
179
+ createTime: policy.createTime
180
+ ? new Date(Number(policy.createTime.seconds ?? 0) * 1000)
181
+ : undefined,
182
+ labels: policy.labels,
183
+ })),
184
+ nextPageToken: nextPageToken,
185
+ };
186
+ return {
187
+ content: [
188
+ {
189
+ type: 'text',
190
+ text: JSON.stringify({ backupPolicies: backupPolicies, nextPageToken: nextPageToken }, null, 2),
191
+ },
192
+ ],
193
+ structuredContent: result,
194
+ };
195
+ }
196
+ catch (error) {
197
+ return {
198
+ content: [
199
+ {
200
+ type: 'text',
201
+ text: `Failed to list backup policies: ${error.message}`,
202
+ },
203
+ ],
204
+ isError: true,
205
+ };
206
+ }
207
+ };
208
+ /**
209
+ * Updates a backup policy
210
+ */
211
+ export const updateBackupPolicyHandler = async (args) => {
212
+ try {
213
+ const { projectId, location, backupPolicyId, dailyBackupLimit, weeklyBackupLimit, monthlyBackupLimit, description, enabled, labels, } = args;
214
+ const client = NetAppClientFactory.createClient();
215
+ // Format resource name
216
+ const name = `projects/${projectId}/locations/${location}/backupPolicies/${backupPolicyId}`;
217
+ // Build backup policy update
218
+ const backupPolicy = {
219
+ name,
220
+ dailyBackupLimit,
221
+ weeklyBackupLimit,
222
+ monthlyBackupLimit,
223
+ description,
224
+ enabled,
225
+ labels,
226
+ };
227
+ // Filter out undefined properties
228
+ Object.keys(backupPolicy).forEach((key) => {
229
+ if (backupPolicy[key] === undefined) {
230
+ delete backupPolicy[key];
231
+ }
232
+ });
233
+ // Create an update mask based on the provided fields
234
+ const updateMask = {
235
+ paths: Object.keys(backupPolicy).filter((key) => key !== 'name'),
236
+ };
237
+ // Call the API to update a backup policy
238
+ const [operation] = await client.updateBackupPolicy({
239
+ backupPolicy,
240
+ updateMask,
241
+ });
242
+ const result = {
243
+ name: operation.metadata ? operation.metadata.target || '' : '',
244
+ operationId: operation.name || '',
245
+ };
246
+ return {
247
+ content: [
248
+ {
249
+ type: 'text',
250
+ text: `Updated backup policy: ${result.name}. Operation ID: ${result.operationId}`,
251
+ },
252
+ ],
253
+ structuredContent: result,
254
+ };
255
+ }
256
+ catch (error) {
257
+ return {
258
+ content: [
259
+ {
260
+ type: 'text',
261
+ text: `Failed to update backup policy: ${error.message}`,
262
+ },
263
+ ],
264
+ isError: true,
265
+ };
266
+ }
267
+ };
268
+ // Export tool handler mappings
269
+ export const backupPolicyHandlers = {
270
+ [createBackupPolicyTool.name]: createBackupPolicyHandler,
271
+ [deleteBackupPolicyTool.name]: deleteBackupPolicyHandler,
272
+ [getBackupPolicyTool.name]: getBackupPolicyHandler,
273
+ [listBackupPoliciesTool.name]: listBackupPoliciesHandler,
274
+ [updateBackupPolicyTool.name]: updateBackupPolicyHandler,
275
+ };
@@ -0,0 +1,370 @@
1
+ import { NetAppClientFactory } from '../../utils/netapp-client-factory.js';
2
+ import { logger } from '../../logger.js';
3
+ const log = logger.child({ module: 'backup-vault-handler' });
4
+ // Helper to format backup vault data for responses
5
+ function formatBackupVaultData(backupVault) {
6
+ const result = {};
7
+ if (!backupVault)
8
+ return result;
9
+ if (backupVault.name) {
10
+ // Extract backupVaultId from name (last part after last slash)
11
+ const nameParts = backupVault.name.split('/');
12
+ result.name = backupVault.name;
13
+ result.backupVaultId = nameParts[nameParts.length - 1];
14
+ }
15
+ // Copy basic properties
16
+ if (backupVault.state)
17
+ result.state = backupVault.state;
18
+ // Format timestamps if they exist
19
+ if (backupVault.createTime) {
20
+ result.createTime = new Date(backupVault.createTime.seconds * 1000);
21
+ }
22
+ if (backupVault.updateTime) {
23
+ result.updateTime = new Date(backupVault.updateTime.seconds * 1000);
24
+ }
25
+ // Copy required properties according to the schema
26
+ if (backupVault.backupVaultType)
27
+ result.backupVaultType = backupVault.backupVaultType;
28
+ if (backupVault.sourceRegion)
29
+ result.sourceRegion = backupVault.sourceRegion;
30
+ if (backupVault.backupRegion)
31
+ result.backupRegion = backupVault.backupRegion;
32
+ if (backupVault.sourceBackupVault)
33
+ result.sourceBackupVault = backupVault.sourceBackupVault;
34
+ if (backupVault.destinationBackupVault)
35
+ result.destinationBackupVault = backupVault.destinationBackupVault;
36
+ // Copy backup retention policy if it exists
37
+ if (backupVault.backupRetentionPolicy) {
38
+ result.backupRetentionPolicy = {
39
+ backupMinimumEnforcedRetentionDays: backupVault.backupRetentionPolicy.backupMinimumEnforcedRetentionDays || 0,
40
+ dailyBackupImmutable: backupVault.backupRetentionPolicy.dailyBackupImmutable || false,
41
+ weeklyBackupImmutable: backupVault.backupRetentionPolicy.weeklyBackupImmutable || false,
42
+ monthlyBackupImmutable: backupVault.backupRetentionPolicy.monthlyBackupImmutable || false,
43
+ manualBackupImmutable: backupVault.backupRetentionPolicy.manualBackupImmutable || false,
44
+ };
45
+ }
46
+ // Copy optional properties
47
+ if (backupVault.description)
48
+ result.description = backupVault.description;
49
+ if (backupVault.labels)
50
+ result.labels = backupVault.labels;
51
+ return result;
52
+ }
53
+ // Create Backup Vault Handler
54
+ export const createBackupVaultHandler = async (args) => {
55
+ try {
56
+ const { projectId, location, backupVaultId, description, labels } = args;
57
+ // Create a new NetApp client using the factory
58
+ const netAppClient = NetAppClientFactory.createClient();
59
+ // Format the parent path for the backup vault
60
+ const parent = `projects/${projectId}/locations/${location}`;
61
+ // Create the backup vault request
62
+ const request = {
63
+ parent,
64
+ backupVaultId,
65
+ backupVault: {
66
+ description,
67
+ labels,
68
+ },
69
+ };
70
+ // Create the backup vault
71
+ const [operation] = await netAppClient.createBackupVault(request);
72
+ // Extract the operation name for tracking
73
+ const operationName = operation.name;
74
+ // Construct the backup vault name
75
+ const backupVaultName = `${parent}/backupVaults/${backupVaultId}`;
76
+ return {
77
+ content: [
78
+ {
79
+ type: 'text',
80
+ text: `Backup vault creation initiated. Operation ID: ${operationName}`,
81
+ },
82
+ ],
83
+ structuredContent: {
84
+ name: backupVaultName,
85
+ operationId: operationName,
86
+ },
87
+ };
88
+ }
89
+ catch (error) {
90
+ log.error({ err: error }, 'Error creating backup vault');
91
+ let errorMessage = `Failed to create backup vault: ${error.message}`;
92
+ // Handle specific error types and provide useful error messages
93
+ if (error.code === 6) {
94
+ // ALREADY_EXISTS
95
+ errorMessage = `Backup vault ${args.backupVaultId} already exists in project ${args.projectId}, location ${args.location}`;
96
+ }
97
+ else if (error.code === 7) {
98
+ // PERMISSION_DENIED
99
+ errorMessage = 'Permission denied. Please check your credentials and access rights.';
100
+ }
101
+ else if (error.code === 5) {
102
+ // NOT_FOUND
103
+ errorMessage = `Project ${args.projectId} or location ${args.location} not found`;
104
+ }
105
+ else if (error.code === 3) {
106
+ // INVALID_ARGUMENT
107
+ errorMessage = `Invalid argument: ${error.message}`;
108
+ }
109
+ return {
110
+ isError: true,
111
+ content: [
112
+ {
113
+ type: 'text',
114
+ text: errorMessage,
115
+ },
116
+ ],
117
+ };
118
+ }
119
+ };
120
+ // Delete Backup Vault Handler
121
+ export const deleteBackupVaultHandler = async (args) => {
122
+ try {
123
+ const { projectId, location, backupVaultId } = args;
124
+ // Create a new NetApp client using the factory
125
+ const netAppClient = NetAppClientFactory.createClient();
126
+ // Format the name for the backup vault
127
+ const name = `projects/${projectId}/locations/${location}/backupVaults/${backupVaultId}`;
128
+ // Delete the backup vault
129
+ const request = { name };
130
+ const [operation] = await netAppClient.deleteBackupVault(request);
131
+ return {
132
+ content: [
133
+ {
134
+ type: 'text',
135
+ text: `Backup vault deletion initiated. Operation ID: ${operation.name}`,
136
+ },
137
+ ],
138
+ structuredContent: {
139
+ success: true,
140
+ operationId: operation.name,
141
+ },
142
+ };
143
+ }
144
+ catch (error) {
145
+ log.error({ err: error }, 'Error deleting backup vault');
146
+ let errorMessage = `Failed to delete backup vault: ${error.message}`;
147
+ // Handle specific error types
148
+ if (error.code === 5) {
149
+ // NOT_FOUND
150
+ errorMessage = `Backup vault not found: projects/${args.projectId}/locations/${args.location}/backupVaults/${args.backupVaultId}`;
151
+ }
152
+ else if (error.code === 7) {
153
+ // PERMISSION_DENIED
154
+ errorMessage = 'Permission denied. Please check your credentials and access rights.';
155
+ }
156
+ else if (error.code === 9) {
157
+ // FAILED_PRECONDITION
158
+ errorMessage =
159
+ 'Failed precondition. The backup vault may have dependent backups that need to be deleted first.';
160
+ }
161
+ return {
162
+ isError: true,
163
+ content: [
164
+ {
165
+ type: 'text',
166
+ text: errorMessage,
167
+ },
168
+ ],
169
+ };
170
+ }
171
+ };
172
+ // Get Backup Vault Handler
173
+ export const getBackupVaultHandler = async (args) => {
174
+ try {
175
+ const { projectId, location, backupVaultId } = args;
176
+ // Create a new NetApp client using the factory
177
+ const netAppClient = NetAppClientFactory.createClient();
178
+ // Format the name for the backup vault
179
+ const name = `projects/${projectId}/locations/${location}/backupVaults/${backupVaultId}`;
180
+ // Get the backup vault
181
+ const [backupVault] = await netAppClient.getBackupVault({ name });
182
+ // Format the backup vault data
183
+ const formattedData = formatBackupVaultData(backupVault);
184
+ // Default values for required fields if they're not present in the API response
185
+ if (!formattedData.backupVaultType)
186
+ formattedData.backupVaultType = 'STANDARD';
187
+ if (!formattedData.sourceRegion)
188
+ formattedData.sourceRegion = location;
189
+ if (!formattedData.backupRegion)
190
+ formattedData.backupRegion = location;
191
+ if (!formattedData.sourceBackupVault)
192
+ formattedData.sourceBackupVault = '';
193
+ if (!formattedData.destinationBackupVault)
194
+ formattedData.destinationBackupVault = '';
195
+ return {
196
+ content: [
197
+ {
198
+ type: 'text',
199
+ text: JSON.stringify(formattedData, null, 2),
200
+ },
201
+ ],
202
+ structuredContent: formattedData,
203
+ };
204
+ }
205
+ catch (error) {
206
+ log.error({ err: error }, 'Error getting backup vault');
207
+ let errorMessage = `Failed to get backup vault: ${error.message}`;
208
+ // Handle specific error types
209
+ if (error.code === 5) {
210
+ // NOT_FOUND
211
+ errorMessage = `Backup vault not found: projects/${args.projectId}/locations/${args.location}/backupVaults/${args.backupVaultId}`;
212
+ }
213
+ else if (error.code === 7) {
214
+ // PERMISSION_DENIED
215
+ errorMessage = 'Permission denied. Please check your credentials and access rights.';
216
+ }
217
+ return {
218
+ isError: true,
219
+ content: [
220
+ {
221
+ type: 'text',
222
+ text: errorMessage,
223
+ },
224
+ ],
225
+ };
226
+ }
227
+ };
228
+ // List Backup Vaults Handler
229
+ export const listBackupVaultsHandler = async (args) => {
230
+ try {
231
+ const { projectId, location, filter, pageSize, pageToken } = args;
232
+ // Create a new NetApp client using the factory
233
+ const netAppClient = NetAppClientFactory.createClient();
234
+ // Format the parent path for listing backup vaults
235
+ const parent = `projects/${projectId}/locations/${location}`;
236
+ // List the backup vaults
237
+ const options = {
238
+ parent,
239
+ filter,
240
+ pageSize,
241
+ pageToken,
242
+ };
243
+ const [backupVaultsResponse] = await netAppClient.listBackupVaults(options);
244
+ // Process the response
245
+ let backupVaults = [];
246
+ let nextPageToken;
247
+ // Handle the response structure correctly
248
+ if (backupVaultsResponse) {
249
+ // If it's an array, map each vault
250
+ if (Array.isArray(backupVaultsResponse)) {
251
+ backupVaults = backupVaultsResponse.map((vault) => formatBackupVaultData(vault));
252
+ }
253
+ // Handle as a generic object with any fields
254
+ else {
255
+ const response = backupVaultsResponse;
256
+ // Check for different possible response structures
257
+ if (response.backupVaults && Array.isArray(response.backupVaults)) {
258
+ backupVaults = response.backupVaults.map((vault) => formatBackupVaultData(vault));
259
+ }
260
+ if (response.nextPageToken) {
261
+ nextPageToken = response.nextPageToken;
262
+ }
263
+ }
264
+ }
265
+ return {
266
+ content: [
267
+ {
268
+ type: 'text',
269
+ text: `Retrieved ${backupVaults.length} backup vault(s) from project ${projectId}, location ${location}.\n` +
270
+ JSON.stringify(backupVaults, null, 2),
271
+ },
272
+ ],
273
+ structuredContent: {
274
+ backupVaults,
275
+ nextPageToken,
276
+ },
277
+ };
278
+ }
279
+ catch (error) {
280
+ log.error({ err: error }, 'Error listing backup vaults');
281
+ let errorMessage = `Failed to list backup vaults: ${error.message}`;
282
+ // Handle specific error types
283
+ if (error.code === 5) {
284
+ // NOT_FOUND
285
+ errorMessage = `Project or location not found: projects/${args.projectId}/locations/${args.location}`;
286
+ }
287
+ else if (error.code === 7) {
288
+ // PERMISSION_DENIED
289
+ errorMessage = 'Permission denied. Please check your credentials and access rights.';
290
+ }
291
+ else if (error.code === 3) {
292
+ // INVALID_ARGUMENT
293
+ errorMessage = `Invalid argument: ${error.message}`;
294
+ }
295
+ return {
296
+ isError: true,
297
+ content: [
298
+ {
299
+ type: 'text',
300
+ text: errorMessage,
301
+ },
302
+ ],
303
+ };
304
+ }
305
+ };
306
+ // Update Backup Vault Handler
307
+ export const updateBackupVaultHandler = async (args) => {
308
+ try {
309
+ const { projectId, location, backupVaultId, description, labels } = args;
310
+ // Create a new NetApp client using the factory
311
+ const netAppClient = NetAppClientFactory.createClient();
312
+ // Format the name for the backup vault
313
+ const name = `projects/${projectId}/locations/${location}/backupVaults/${backupVaultId}`;
314
+ // Create update mask based on provided fields
315
+ const updateMask = [];
316
+ if (description !== undefined)
317
+ updateMask.push('description');
318
+ if (labels !== undefined)
319
+ updateMask.push('labels');
320
+ // Update the backup vault
321
+ const [operation] = await netAppClient.updateBackupVault({
322
+ backupVault: {
323
+ name,
324
+ description,
325
+ labels,
326
+ },
327
+ updateMask: {
328
+ paths: updateMask,
329
+ },
330
+ });
331
+ return {
332
+ content: [
333
+ {
334
+ type: 'text',
335
+ text: `Backup vault update initiated. Operation ID: ${operation.name}`,
336
+ },
337
+ ],
338
+ structuredContent: {
339
+ name,
340
+ operationId: operation.name,
341
+ },
342
+ };
343
+ }
344
+ catch (error) {
345
+ log.error({ err: error }, 'Error updating backup vault');
346
+ let errorMessage = `Failed to update backup vault: ${error.message}`;
347
+ // Handle specific error types
348
+ if (error.code === 5) {
349
+ // NOT_FOUND
350
+ errorMessage = `Backup vault not found: projects/${args.projectId}/locations/${args.location}/backupVaults/${args.backupVaultId}`;
351
+ }
352
+ else if (error.code === 7) {
353
+ // PERMISSION_DENIED
354
+ errorMessage = 'Permission denied. Please check your credentials and access rights.';
355
+ }
356
+ else if (error.code === 3) {
357
+ // INVALID_ARGUMENT
358
+ errorMessage = `Invalid argument: ${error.message}`;
359
+ }
360
+ return {
361
+ isError: true,
362
+ content: [
363
+ {
364
+ type: 'text',
365
+ text: errorMessage,
366
+ },
367
+ ],
368
+ };
369
+ }
370
+ };