@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.
- package/GEMINI.md +200 -0
- package/LICENSE +201 -0
- package/README.md +374 -0
- package/build/index.js +185 -0
- package/build/logger.js +19 -0
- package/build/registry/register-tools.js +101 -0
- package/build/registry/tool-registry.js +27 -0
- package/build/tools/active-directory-tools.js +124 -0
- package/build/tools/backup-policy-tools.js +140 -0
- package/build/tools/backup-tools.js +178 -0
- package/build/tools/backup-vault-tools.js +147 -0
- package/build/tools/handlers/active-directory-handler.js +321 -0
- package/build/tools/handlers/backup-handler.js +451 -0
- package/build/tools/handlers/backup-policy-handler.js +275 -0
- package/build/tools/handlers/backup-vault-handler.js +370 -0
- package/build/tools/handlers/kms-config-handler.js +327 -0
- package/build/tools/handlers/operation-handler.js +254 -0
- package/build/tools/handlers/quota-rule-handler.js +411 -0
- package/build/tools/handlers/replication-handler.js +504 -0
- package/build/tools/handlers/snapshot-handler.js +320 -0
- package/build/tools/handlers/storage-pool-handler.js +346 -0
- package/build/tools/handlers/volume-handler.js +353 -0
- package/build/tools/kms-config-tools.js +162 -0
- package/build/tools/operation-tools.js +64 -0
- package/build/tools/quota-rule-tools.js +166 -0
- package/build/tools/replication-tools.js +227 -0
- package/build/tools/snapshot-tools.js +124 -0
- package/build/tools/storage-pool-tools.js +215 -0
- package/build/tools/volume-tools.js +216 -0
- package/build/types/tool.js +1 -0
- package/build/utils/netapp-client-factory.js +53 -0
- package/gemini-extension.json +12 -0
- package/package.json +66 -0
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
import { NetAppClientFactory } from '../../utils/netapp-client-factory.js';
|
|
2
|
+
import { logger } from '../../logger.js';
|
|
3
|
+
const log = logger.child({ module: 'volume-handler' });
|
|
4
|
+
// Helper to format volume data for responses
|
|
5
|
+
function formatVolumeData(volume) {
|
|
6
|
+
const result = {};
|
|
7
|
+
if (!volume)
|
|
8
|
+
return result;
|
|
9
|
+
if (volume.name) {
|
|
10
|
+
// Extract volumeId from name (last part after last slash)
|
|
11
|
+
const nameParts = volume.name.split('/');
|
|
12
|
+
result.name = volume.name;
|
|
13
|
+
result.volumeId = nameParts[nameParts.length - 1];
|
|
14
|
+
}
|
|
15
|
+
// Extract storage pool from name
|
|
16
|
+
if (volume.storagePool) {
|
|
17
|
+
result.storagePool = volume.storagePool;
|
|
18
|
+
}
|
|
19
|
+
// Copy basic properties
|
|
20
|
+
if (volume.capacityGib)
|
|
21
|
+
result.capacityGib = Number(volume.capacityGib);
|
|
22
|
+
if (volume.usedGib)
|
|
23
|
+
result.usedGib = Number(volume.usedGib);
|
|
24
|
+
if (volume.state)
|
|
25
|
+
result.state = volume.state;
|
|
26
|
+
if (volume.stateDetails)
|
|
27
|
+
result.stateDetails = volume.stateDetails;
|
|
28
|
+
if (volume.shareName)
|
|
29
|
+
result.shareName = volume.shareName;
|
|
30
|
+
if (volume.protocols)
|
|
31
|
+
result.protocols = volume.protocols;
|
|
32
|
+
if (volume.serviceLevel)
|
|
33
|
+
result.serviceLevel = volume.serviceLevel;
|
|
34
|
+
if (volume.network)
|
|
35
|
+
result.network = volume.network;
|
|
36
|
+
if (volume.psaRange)
|
|
37
|
+
result.psaRange = volume.psaRange;
|
|
38
|
+
if (volume.securityStyle)
|
|
39
|
+
result.securityStyle = volume.securityStyle;
|
|
40
|
+
// Format timestamps if they exist
|
|
41
|
+
if (volume.createTime) {
|
|
42
|
+
result.createTime = new Date(volume.createTime.seconds * 1000);
|
|
43
|
+
}
|
|
44
|
+
// Copy optional properties
|
|
45
|
+
if (volume.description)
|
|
46
|
+
result.description = volume.description;
|
|
47
|
+
if (volume.labels)
|
|
48
|
+
result.labels = volume.labels;
|
|
49
|
+
if (volume.unixPermissions)
|
|
50
|
+
result.unixPermissions = volume.unixPermissions;
|
|
51
|
+
if (volume.kmsConfig)
|
|
52
|
+
result.kmsConfig = volume.kmsConfig;
|
|
53
|
+
if (volume.encryptionType)
|
|
54
|
+
result.encryptionType = volume.encryptionType;
|
|
55
|
+
if (volume.backupConfig)
|
|
56
|
+
result.backupConfig = volume.backupConfig;
|
|
57
|
+
if (volume.tieringPolicy)
|
|
58
|
+
result.tieringPolicy = volume.tieringPolicy;
|
|
59
|
+
if (volume.throughputMibps !== undefined)
|
|
60
|
+
result.throughputMibps = volume.throughputMibps;
|
|
61
|
+
if (volume.replicaZone)
|
|
62
|
+
result.replicaZone = volume.replicaZone;
|
|
63
|
+
if (volume.zone)
|
|
64
|
+
result.zone = volume.zone;
|
|
65
|
+
if (volume.coldTierSizeGib !== undefined)
|
|
66
|
+
result.coldTierSizeGib = Number(volume.coldTierSizeGib);
|
|
67
|
+
if (volume.hotTierSizeUsedGib !== undefined)
|
|
68
|
+
result.hotTierSizeUsedGib = Number(volume.hotTierSizeUsedGib);
|
|
69
|
+
if (volume.largeCapacity !== undefined)
|
|
70
|
+
result.largeCapacity = volume.largeCapacity;
|
|
71
|
+
if (volume.multipleEndpoints !== undefined)
|
|
72
|
+
result.multipleEndpoints = volume.multipleEndpoints;
|
|
73
|
+
if (volume.hasReplication !== undefined)
|
|
74
|
+
result.hasReplication = volume.hasReplication;
|
|
75
|
+
if (volume.restrictedActions)
|
|
76
|
+
result.restrictedActions = volume.restrictedActions;
|
|
77
|
+
// Format mount points
|
|
78
|
+
if (volume.mountOptions && volume.mountOptions.length > 0) {
|
|
79
|
+
result.mountOptions = volume.mountOptions.map((mp) => ({
|
|
80
|
+
protocol: mp.protocol || '',
|
|
81
|
+
ipAddress: mp.ipAddress || '',
|
|
82
|
+
export: mp.export || '',
|
|
83
|
+
exportFull: mp.exportFull || '',
|
|
84
|
+
}));
|
|
85
|
+
}
|
|
86
|
+
// Surface export policy if present
|
|
87
|
+
if (volume.exportPolicy)
|
|
88
|
+
result.exportPolicy = volume.exportPolicy;
|
|
89
|
+
// Surface SMB settings if present
|
|
90
|
+
if (volume.smbSettings)
|
|
91
|
+
result.smbSettings = volume.smbSettings;
|
|
92
|
+
return result;
|
|
93
|
+
}
|
|
94
|
+
// Create Volume Handler
|
|
95
|
+
export const createVolumeHandler = async (args) => {
|
|
96
|
+
try {
|
|
97
|
+
const { projectId, location, storagePoolId, volumeId, capacityGib, protocols, description, labels, backupConfig, exportPolicy, shareName, } = args;
|
|
98
|
+
// Create a new NetApp client using the factory
|
|
99
|
+
const netAppClient = NetAppClientFactory.createClient();
|
|
100
|
+
// Format the parent path for the volume
|
|
101
|
+
const parent = `projects/${projectId}/locations/${location}`;
|
|
102
|
+
// Create the volume request
|
|
103
|
+
const request = {
|
|
104
|
+
parent,
|
|
105
|
+
volumeId,
|
|
106
|
+
volume: {
|
|
107
|
+
storagePool: storagePoolId,
|
|
108
|
+
capacityGib,
|
|
109
|
+
protocols: protocols || ['NFS3'],
|
|
110
|
+
description,
|
|
111
|
+
labels,
|
|
112
|
+
backupConfig,
|
|
113
|
+
shareName: shareName || volumeId,
|
|
114
|
+
exportPolicy,
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
log.info({ request }, 'Create Volume request');
|
|
118
|
+
// Call the API to create a volume
|
|
119
|
+
const [operation] = await netAppClient.createVolume(request);
|
|
120
|
+
log.info({ operation }, 'Create Volume operation');
|
|
121
|
+
return {
|
|
122
|
+
content: [
|
|
123
|
+
{
|
|
124
|
+
type: 'text',
|
|
125
|
+
text: `Created volume ${volumeId}. Operation ID: ${operation.name || ''}`,
|
|
126
|
+
},
|
|
127
|
+
],
|
|
128
|
+
structuredContent: {
|
|
129
|
+
name: `projects/${projectId}/locations/${location}/volumes/${volumeId}`,
|
|
130
|
+
operationId: operation.name || '',
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
log.error({ err: error }, 'Error creating volume');
|
|
136
|
+
return {
|
|
137
|
+
isError: true,
|
|
138
|
+
content: [
|
|
139
|
+
{
|
|
140
|
+
type: 'text',
|
|
141
|
+
text: `Error creating volume: ${error.message || 'Unknown error'}`,
|
|
142
|
+
},
|
|
143
|
+
],
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
// Delete Volume Handler
|
|
148
|
+
export const deleteVolumeHandler = async (args) => {
|
|
149
|
+
try {
|
|
150
|
+
const { projectId, location, volumeId, force = false } = args;
|
|
151
|
+
// Create a new NetApp client using the factory
|
|
152
|
+
const netAppClient = NetAppClientFactory.createClient();
|
|
153
|
+
// Format the name for the volume
|
|
154
|
+
const name = `projects/${projectId}/locations/${location}/volumes/${volumeId}`;
|
|
155
|
+
// Call the API to delete the volume
|
|
156
|
+
const request = { name };
|
|
157
|
+
// Only add force if it's true to avoid API errors
|
|
158
|
+
if (force) {
|
|
159
|
+
request.force = true;
|
|
160
|
+
}
|
|
161
|
+
log.info({ request }, 'Delete Volume request');
|
|
162
|
+
const [operation] = await netAppClient.deleteVolume(request);
|
|
163
|
+
log.info({ operation }, 'Delete Volume operation');
|
|
164
|
+
return {
|
|
165
|
+
content: [
|
|
166
|
+
{
|
|
167
|
+
type: 'text',
|
|
168
|
+
text: JSON.stringify({
|
|
169
|
+
message: `Volume ${volumeId} deletion requested`,
|
|
170
|
+
operation: operation,
|
|
171
|
+
}, null, 2),
|
|
172
|
+
},
|
|
173
|
+
],
|
|
174
|
+
structuredContent: {
|
|
175
|
+
success: true,
|
|
176
|
+
operationId: operation.name || '',
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
log.error({ err: error }, 'Error deleting volume');
|
|
182
|
+
return {
|
|
183
|
+
isError: true,
|
|
184
|
+
content: [
|
|
185
|
+
{
|
|
186
|
+
type: 'text',
|
|
187
|
+
text: `Error deleting volume: ${error.message || 'Unknown error'}`,
|
|
188
|
+
},
|
|
189
|
+
],
|
|
190
|
+
structuredContent: {
|
|
191
|
+
success: false,
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
// Get Volume Handler
|
|
197
|
+
export const getVolumeHandler = async (args) => {
|
|
198
|
+
try {
|
|
199
|
+
const { projectId, location, volumeId } = args;
|
|
200
|
+
// Create a new NetApp client using the factory
|
|
201
|
+
const netAppClient = NetAppClientFactory.createClient();
|
|
202
|
+
// Format the name for the volume
|
|
203
|
+
const name = `projects/${projectId}/locations/${location}/volumes/${volumeId}`;
|
|
204
|
+
// Call the API to get the volume
|
|
205
|
+
log.info({ name }, 'Get Volume request');
|
|
206
|
+
const [volume] = await netAppClient.getVolume({ name });
|
|
207
|
+
log.info({ volume }, 'Get Volume response');
|
|
208
|
+
// Format the volume data
|
|
209
|
+
const formattedVolume = formatVolumeData(volume);
|
|
210
|
+
return {
|
|
211
|
+
content: [
|
|
212
|
+
{
|
|
213
|
+
type: 'text',
|
|
214
|
+
text: JSON.stringify(volume, null, 2),
|
|
215
|
+
},
|
|
216
|
+
],
|
|
217
|
+
structuredContent: { volume: formattedVolume },
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
catch (error) {
|
|
221
|
+
log.error({ err: error }, 'Error getting volume');
|
|
222
|
+
return {
|
|
223
|
+
isError: true,
|
|
224
|
+
content: [
|
|
225
|
+
{
|
|
226
|
+
type: 'text',
|
|
227
|
+
text: `Error getting volume: ${error.message || 'Unknown error'}`,
|
|
228
|
+
},
|
|
229
|
+
],
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
// List Volumes Handler
|
|
234
|
+
export const listVolumesHandler = async (args) => {
|
|
235
|
+
try {
|
|
236
|
+
const { projectId, location, filter, pageSize, pageToken } = args;
|
|
237
|
+
// Create a new NetApp client using the factory
|
|
238
|
+
const netAppClient = NetAppClientFactory.createClient();
|
|
239
|
+
// Format the parent path
|
|
240
|
+
const parent = `projects/${projectId}/locations/${location}`;
|
|
241
|
+
// Create the request object
|
|
242
|
+
const request = { parent };
|
|
243
|
+
if (filter)
|
|
244
|
+
request.filter = filter;
|
|
245
|
+
if (pageSize)
|
|
246
|
+
request.pageSize = pageSize;
|
|
247
|
+
if (pageToken)
|
|
248
|
+
request.pageToken = pageToken;
|
|
249
|
+
// Call the API to list volumes
|
|
250
|
+
log.info({ request }, 'List Volumes request');
|
|
251
|
+
const [volumes, , nextPageToken] = await netAppClient.listVolumes(request);
|
|
252
|
+
log.info({ volumes, nextPageToken }, 'List Volumes response');
|
|
253
|
+
const formattedVolumes = volumes.map(formatVolumeData);
|
|
254
|
+
return {
|
|
255
|
+
content: [
|
|
256
|
+
{
|
|
257
|
+
type: 'text',
|
|
258
|
+
text: JSON.stringify({ volumes: volumes, nextPageToken: nextPageToken }, null, 2),
|
|
259
|
+
},
|
|
260
|
+
],
|
|
261
|
+
structuredContent: {
|
|
262
|
+
volumes: formattedVolumes,
|
|
263
|
+
nextPageToken: pageToken || '',
|
|
264
|
+
},
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
catch (error) {
|
|
268
|
+
log.error({ err: error }, 'Error listing volumes');
|
|
269
|
+
return {
|
|
270
|
+
isError: true,
|
|
271
|
+
content: [
|
|
272
|
+
{
|
|
273
|
+
type: 'text',
|
|
274
|
+
text: `Error listing volumes: ${error.message || 'Unknown error'}`,
|
|
275
|
+
},
|
|
276
|
+
],
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
// Update Volume Handler
|
|
281
|
+
// TODO: update is not tested
|
|
282
|
+
// FIX_ME: errors "reason: 'RESOURCE_PROJECT_INVALID'
|
|
283
|
+
export const updateVolumeHandler = async (args) => {
|
|
284
|
+
try {
|
|
285
|
+
const { projectId, location, volumeId, capacityGib, description, labels, backupConfig, exportPolicy, } = args;
|
|
286
|
+
// Create a new NetApp client using the factory
|
|
287
|
+
const netAppClient = NetAppClientFactory.createClient();
|
|
288
|
+
// Format the name for the volume
|
|
289
|
+
const name = `projects/${projectId}/locations/${location}/volumes/${volumeId}`;
|
|
290
|
+
// Create the update mask
|
|
291
|
+
const updateMask = [];
|
|
292
|
+
const volume = { name };
|
|
293
|
+
// Add fields to update mask if they're provided
|
|
294
|
+
if (capacityGib !== undefined) {
|
|
295
|
+
volume.capacityGib = capacityGib;
|
|
296
|
+
updateMask.push('capacity_gib');
|
|
297
|
+
}
|
|
298
|
+
if (description !== undefined) {
|
|
299
|
+
volume.description = description;
|
|
300
|
+
updateMask.push('description');
|
|
301
|
+
}
|
|
302
|
+
if (labels !== undefined) {
|
|
303
|
+
volume.labels = labels;
|
|
304
|
+
updateMask.push('labels');
|
|
305
|
+
}
|
|
306
|
+
if (exportPolicy !== undefined) {
|
|
307
|
+
volume.exportPolicy = exportPolicy;
|
|
308
|
+
updateMask.push('export_policy');
|
|
309
|
+
}
|
|
310
|
+
if (backupConfig !== undefined) {
|
|
311
|
+
volume.backupConfig = backupConfig;
|
|
312
|
+
// Update mask must use proto (snake_case) field names
|
|
313
|
+
if (backupConfig.backupPolicies !== undefined)
|
|
314
|
+
updateMask.push('backup_config.backup_policies');
|
|
315
|
+
if (backupConfig.backupVault !== undefined)
|
|
316
|
+
updateMask.push('backup_config.backup_vault');
|
|
317
|
+
if (backupConfig.scheduledBackupEnabled !== undefined)
|
|
318
|
+
updateMask.push('backup_config.scheduled_backup_enabled');
|
|
319
|
+
}
|
|
320
|
+
// Create the request
|
|
321
|
+
const request = {
|
|
322
|
+
volume,
|
|
323
|
+
updateMask: { paths: updateMask },
|
|
324
|
+
};
|
|
325
|
+
log.info({ request }, 'Update Volume request');
|
|
326
|
+
const [operation] = await netAppClient.updateVolume(request);
|
|
327
|
+
log.info({ operation }, 'Update Volume operation');
|
|
328
|
+
return {
|
|
329
|
+
content: [
|
|
330
|
+
{
|
|
331
|
+
type: 'text',
|
|
332
|
+
text: `Updated volume ${volumeId}. Operation ID: ${operation.name || ''}`,
|
|
333
|
+
},
|
|
334
|
+
],
|
|
335
|
+
structuredContent: {
|
|
336
|
+
name,
|
|
337
|
+
operationId: operation.name || '',
|
|
338
|
+
},
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
catch (error) {
|
|
342
|
+
log.error({ err: error }, 'Error updating volume');
|
|
343
|
+
return {
|
|
344
|
+
isError: true,
|
|
345
|
+
content: [
|
|
346
|
+
{
|
|
347
|
+
type: 'text',
|
|
348
|
+
text: `Error updating volume: ${error.message || 'Unknown error'}`,
|
|
349
|
+
},
|
|
350
|
+
],
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
};
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
// Create KMS Config Tool
|
|
3
|
+
export const createKmsConfigTool = {
|
|
4
|
+
name: 'gcnv_kms_config_create',
|
|
5
|
+
title: 'Create KMS Config',
|
|
6
|
+
description: 'Creates a new KMS (Key Management Service) configuration',
|
|
7
|
+
inputSchema: {
|
|
8
|
+
projectId: z.string().describe('The ID of the Google Cloud project'),
|
|
9
|
+
location: z.string().describe('The location where the KMS config should be created'),
|
|
10
|
+
kmsConfigId: z.string().describe('The ID to assign to the KMS config'),
|
|
11
|
+
cryptoKeyName: z.string().describe('The full name of the crypto key'),
|
|
12
|
+
description: z.string().optional().describe('Optional description'),
|
|
13
|
+
labels: z.record(z.string()).optional().describe('Optional labels'),
|
|
14
|
+
instructions: z.string().optional().describe('Instructions for accessing the KMS'),
|
|
15
|
+
},
|
|
16
|
+
outputSchema: {
|
|
17
|
+
name: z.string().describe('The name of the created KMS config'),
|
|
18
|
+
operationId: z.string().describe('The ID of the long-running operation'),
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
// Delete KMS Config Tool
|
|
22
|
+
export const deleteKmsConfigTool = {
|
|
23
|
+
name: 'gcnv_kms_config_delete',
|
|
24
|
+
title: 'Delete KMS Config',
|
|
25
|
+
description: 'Deletes a KMS configuration',
|
|
26
|
+
inputSchema: {
|
|
27
|
+
projectId: z.string().describe('The ID of the Google Cloud project'),
|
|
28
|
+
location: z.string().describe('The location of the KMS config'),
|
|
29
|
+
kmsConfigId: z.string().describe('The ID of the KMS config to delete'),
|
|
30
|
+
},
|
|
31
|
+
outputSchema: {
|
|
32
|
+
success: z.boolean().describe('Whether the deletion was successful'),
|
|
33
|
+
operationId: z.string().optional().describe('The ID of the long-running operation'),
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
// Get KMS Config Tool
|
|
37
|
+
export const getKmsConfigTool = {
|
|
38
|
+
name: 'gcnv_kms_config_get',
|
|
39
|
+
title: 'Get KMS Config',
|
|
40
|
+
description: 'Gets details of a specific KMS configuration',
|
|
41
|
+
inputSchema: {
|
|
42
|
+
projectId: z.string().describe('The ID of the Google Cloud project'),
|
|
43
|
+
location: z.string().describe('The location of the KMS config'),
|
|
44
|
+
kmsConfigId: z.string().describe('The ID of the KMS config to retrieve'),
|
|
45
|
+
},
|
|
46
|
+
outputSchema: {
|
|
47
|
+
name: z.string().describe('The name of the KMS config'),
|
|
48
|
+
kmsConfigId: z.string().describe('The ID of the KMS config'),
|
|
49
|
+
cryptoKeyName: z.string().optional().describe('The name of the crypto key'),
|
|
50
|
+
state: z
|
|
51
|
+
.string()
|
|
52
|
+
.optional()
|
|
53
|
+
.describe('The current state (e.g., READY, KEY_CHECK_PENDING—run instructions if pending)'),
|
|
54
|
+
instructions: z
|
|
55
|
+
.string()
|
|
56
|
+
.optional()
|
|
57
|
+
.describe('Steps to grant the service account access when state is KEY_CHECK_PENDING'),
|
|
58
|
+
stateDetails: z
|
|
59
|
+
.string()
|
|
60
|
+
.optional()
|
|
61
|
+
.describe('Additional state details when the KMS config is not ready'),
|
|
62
|
+
createTime: z.date().optional().describe('The creation timestamp'),
|
|
63
|
+
description: z.string().optional().describe('Description'),
|
|
64
|
+
labels: z.record(z.string()).optional().describe('Labels'),
|
|
65
|
+
serviceAccount: z
|
|
66
|
+
.string()
|
|
67
|
+
.optional()
|
|
68
|
+
.describe('Service account used by NetApp to access the KMS key'),
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
// List KMS Configs Tool
|
|
72
|
+
export const listKmsConfigsTool = {
|
|
73
|
+
name: 'gcnv_kms_config_list',
|
|
74
|
+
title: 'List KMS Configs',
|
|
75
|
+
description: 'Lists all KMS configurations in the specified location',
|
|
76
|
+
inputSchema: {
|
|
77
|
+
projectId: z.string().describe('The ID of the Google Cloud project'),
|
|
78
|
+
location: z.string().describe('The location to list KMS configs from'),
|
|
79
|
+
filter: z.string().optional().describe('Filter expression'),
|
|
80
|
+
pageSize: z.number().optional().describe('Maximum number of items to return'),
|
|
81
|
+
pageToken: z.string().optional().describe('Page token from previous request'),
|
|
82
|
+
orderBy: z.string().optional().describe('Sort order'),
|
|
83
|
+
},
|
|
84
|
+
outputSchema: {
|
|
85
|
+
kmsConfigs: z
|
|
86
|
+
.array(z.object({
|
|
87
|
+
name: z.string().describe('The name of the KMS config'),
|
|
88
|
+
kmsConfigId: z.string().describe('The ID of the KMS config'),
|
|
89
|
+
cryptoKeyName: z.string().optional().describe('The name of the crypto key'),
|
|
90
|
+
state: z
|
|
91
|
+
.string()
|
|
92
|
+
.optional()
|
|
93
|
+
.describe('The current state (e.g., READY, KEY_CHECK_PENDING—run instructions if pending)'),
|
|
94
|
+
instructions: z
|
|
95
|
+
.string()
|
|
96
|
+
.optional()
|
|
97
|
+
.describe('Steps to grant the service account access when state is KEY_CHECK_PENDING'),
|
|
98
|
+
stateDetails: z
|
|
99
|
+
.string()
|
|
100
|
+
.optional()
|
|
101
|
+
.describe('Additional state details when the KMS config is not ready'),
|
|
102
|
+
createTime: z.date().optional().describe('The creation timestamp'),
|
|
103
|
+
description: z.string().optional().describe('Description'),
|
|
104
|
+
labels: z.record(z.string()).optional().describe('Labels'),
|
|
105
|
+
serviceAccount: z
|
|
106
|
+
.string()
|
|
107
|
+
.optional()
|
|
108
|
+
.describe('Service account used by NetApp to access the KMS key'),
|
|
109
|
+
}))
|
|
110
|
+
.describe('List of KMS configs'),
|
|
111
|
+
nextPageToken: z.string().optional().describe('Token for next page'),
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
// Update KMS Config Tool
|
|
115
|
+
export const updateKmsConfigTool = {
|
|
116
|
+
name: 'gcnv_kms_config_update',
|
|
117
|
+
title: 'Update KMS Config',
|
|
118
|
+
description: 'Updates a KMS configuration',
|
|
119
|
+
inputSchema: {
|
|
120
|
+
projectId: z.string().describe('The ID of the Google Cloud project'),
|
|
121
|
+
location: z.string().describe('The location of the KMS config'),
|
|
122
|
+
kmsConfigId: z.string().describe('The ID of the KMS config to update'),
|
|
123
|
+
cryptoKeyName: z.string().optional().describe('The name of the crypto key'),
|
|
124
|
+
description: z.string().optional().describe('New description'),
|
|
125
|
+
labels: z.record(z.string()).optional().describe('New labels'),
|
|
126
|
+
instructions: z.string().optional().describe('Instructions for accessing the KMS'),
|
|
127
|
+
},
|
|
128
|
+
outputSchema: {
|
|
129
|
+
name: z.string().describe('The name of the updated KMS config'),
|
|
130
|
+
operationId: z.string().describe('The ID of the long-running operation'),
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
// Verify KMS Config Tool
|
|
134
|
+
export const verifyKmsConfigTool = {
|
|
135
|
+
name: 'gcnv_kms_config_verify',
|
|
136
|
+
title: 'Verify KMS Config',
|
|
137
|
+
description: 'Verifies KMS config reachability after granting CMEK permissions; run this after applying the returned instructions so the service marks the config READY for pool creation',
|
|
138
|
+
inputSchema: {
|
|
139
|
+
projectId: z.string().describe('The ID of the Google Cloud project'),
|
|
140
|
+
location: z.string().describe('The location of the KMS config'),
|
|
141
|
+
kmsConfigId: z.string().describe('The ID of the KMS config to verify'),
|
|
142
|
+
},
|
|
143
|
+
outputSchema: {
|
|
144
|
+
reachable: z.boolean().optional().describe('Whether the KMS config is reachable'),
|
|
145
|
+
healthError: z.string().optional().describe('Health error if any'),
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
// Encrypt Volumes Tool
|
|
149
|
+
export const encryptVolumesTool = {
|
|
150
|
+
name: 'gcnv_kms_config_encrypt_volumes',
|
|
151
|
+
title: 'Encrypt Volumes',
|
|
152
|
+
description: 'Encrypts existing volumes without CMEK encryption with the desired KMS config',
|
|
153
|
+
inputSchema: {
|
|
154
|
+
projectId: z.string().describe('The ID of the Google Cloud project'),
|
|
155
|
+
location: z.string().describe('The location of the KMS config'),
|
|
156
|
+
kmsConfigId: z.string().describe('The ID of the KMS config to use for encryption'),
|
|
157
|
+
},
|
|
158
|
+
outputSchema: {
|
|
159
|
+
name: z.string().describe('The name of the KMS config'),
|
|
160
|
+
operationId: z.string().describe('The ID of the long-running operation'),
|
|
161
|
+
},
|
|
162
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
// Get Operation Tool
|
|
3
|
+
export const getOperationTool = {
|
|
4
|
+
name: 'gcnv_operation_get',
|
|
5
|
+
title: 'Get Operation',
|
|
6
|
+
description: 'Get details of a long-running operation by its ID',
|
|
7
|
+
inputSchema: {
|
|
8
|
+
operationName: z.string().describe('The full name of the operation to retrieve'),
|
|
9
|
+
},
|
|
10
|
+
outputSchema: {
|
|
11
|
+
name: z.string().describe('The name of the operation'),
|
|
12
|
+
done: z.boolean().describe('Whether the operation is complete'),
|
|
13
|
+
success: z.boolean().describe('True when done is true; done determines the final result'),
|
|
14
|
+
metadata: z.record(z.any()).optional().describe('Metadata about the operation'),
|
|
15
|
+
error: z.record(z.any()).optional().describe('Error details if the operation failed'),
|
|
16
|
+
response: z.record(z.any()).optional().describe('The response if the operation succeeded'),
|
|
17
|
+
createTime: z.string().optional().describe('When the operation was created'),
|
|
18
|
+
target: z.string().optional().describe('The target resource of the operation'),
|
|
19
|
+
verb: z.string().optional().describe('The operation verb (create, update, delete)'),
|
|
20
|
+
statusMessage: z.string().optional().describe('Current status message of the operation'),
|
|
21
|
+
cancelRequested: z.boolean().optional().describe('Whether cancellation was requested'),
|
|
22
|
+
apiVersion: z.string().optional().describe('API version used for the operation'),
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
// Cancel Operation Tool
|
|
26
|
+
export const cancelOperationTool = {
|
|
27
|
+
name: 'gcnv_operation_cancel',
|
|
28
|
+
title: 'Cancel Operation',
|
|
29
|
+
description: 'Cancels a long-running operation that is still in progress',
|
|
30
|
+
inputSchema: {
|
|
31
|
+
operationName: z.string().describe('The full name of the operation to cancel'),
|
|
32
|
+
},
|
|
33
|
+
outputSchema: {
|
|
34
|
+
success: z.boolean().describe('Whether the cancellation request was successful'),
|
|
35
|
+
message: z.string().describe('Status message about the cancellation attempt'),
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
// List Operations Tool
|
|
39
|
+
export const listOperationsTool = {
|
|
40
|
+
name: 'gcnv_operation_list',
|
|
41
|
+
title: 'List Operations',
|
|
42
|
+
description: 'Lists all active long-running operations in the project',
|
|
43
|
+
inputSchema: {
|
|
44
|
+
projectId: z.string().describe('The ID of the Google Cloud project'),
|
|
45
|
+
location: z.string().describe('The location to list operations from'),
|
|
46
|
+
filter: z.string().optional().describe('Filter expression for operations'),
|
|
47
|
+
pageSize: z.number().optional().describe('The maximum number of operations to return'),
|
|
48
|
+
pageToken: z.string().optional().describe('Page token from a previous list request'),
|
|
49
|
+
},
|
|
50
|
+
outputSchema: {
|
|
51
|
+
operations: z
|
|
52
|
+
.array(z.object({
|
|
53
|
+
name: z.string().describe('The name of the operation'),
|
|
54
|
+
done: z.boolean().describe('Whether the operation is complete'),
|
|
55
|
+
success: z.boolean().describe('True when done is true; done determines the final result'),
|
|
56
|
+
target: z.string().optional().describe('The target resource of the operation'),
|
|
57
|
+
verb: z.string().optional().describe('The operation verb (create, update, delete)'),
|
|
58
|
+
createTime: z.string().optional().describe('When the operation was created'),
|
|
59
|
+
statusMessage: z.string().optional().describe('Current status message of the operation'),
|
|
60
|
+
}))
|
|
61
|
+
.describe('List of operations'),
|
|
62
|
+
nextPageToken: z.string().optional().describe('Token to retrieve the next page of results'),
|
|
63
|
+
},
|
|
64
|
+
};
|