mcp-cohesity 2.0.0
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/LICENSE +77 -0
- package/README.md +494 -0
- package/dist/cohesity-client.d.ts +47 -0
- package/dist/cohesity-client.js +126 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +149 -0
- package/dist/tools/active-directory.d.ts +11 -0
- package/dist/tools/active-directory.js +82 -0
- package/dist/tools/alerts.d.ts +6 -0
- package/dist/tools/alerts.js +111 -0
- package/dist/tools/antivirus.d.ts +24 -0
- package/dist/tools/antivirus.js +180 -0
- package/dist/tools/audit-logs.d.ts +10 -0
- package/dist/tools/audit-logs.js +161 -0
- package/dist/tools/clones.d.ts +24 -0
- package/dist/tools/clones.js +107 -0
- package/dist/tools/cluster-reports.d.ts +10 -0
- package/dist/tools/cluster-reports.js +224 -0
- package/dist/tools/cluster.d.ts +6 -0
- package/dist/tools/cluster.js +33 -0
- package/dist/tools/external-targets.d.ts +3 -0
- package/dist/tools/external-targets.js +145 -0
- package/dist/tools/kms.d.ts +14 -0
- package/dist/tools/kms.js +164 -0
- package/dist/tools/notifications.d.ts +3 -0
- package/dist/tools/notifications.js +156 -0
- package/dist/tools/protection.d.ts +3 -0
- package/dist/tools/protection.js +514 -0
- package/dist/tools/recovery.d.ts +6 -0
- package/dist/tools/recovery.js +101 -0
- package/dist/tools/reports.d.ts +3 -0
- package/dist/tools/reports.js +346 -0
- package/dist/tools/restore.d.ts +3 -0
- package/dist/tools/restore.js +220 -0
- package/dist/tools/roles.d.ts +11 -0
- package/dist/tools/roles.js +95 -0
- package/dist/tools/run-actions.d.ts +17 -0
- package/dist/tools/run-actions.js +190 -0
- package/dist/tools/runs.d.ts +6 -0
- package/dist/tools/runs.js +94 -0
- package/dist/tools/source-registration.d.ts +11 -0
- package/dist/tools/source-registration.js +456 -0
- package/dist/tools/sources.d.ts +3 -0
- package/dist/tools/sources.js +161 -0
- package/dist/tools/stats.d.ts +3 -0
- package/dist/tools/stats.js +164 -0
- package/dist/tools/storage.d.ts +3 -0
- package/dist/tools/storage.js +191 -0
- package/dist/tools/tiering.d.ts +3 -0
- package/dist/tools/tiering.js +132 -0
- package/dist/tools/users.d.ts +13 -0
- package/dist/tools/users.js +203 -0
- package/package.json +57 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
function toolResult(text, isError = false) {
|
|
3
|
+
return { content: [{ type: "text", text }], isError };
|
|
4
|
+
}
|
|
5
|
+
export function registerStatsTools(server, client) {
|
|
6
|
+
// ── Cluster Storage Stats ────────────────────────────────────────────
|
|
7
|
+
server.tool("get_cluster_storage_stats", "Get current cluster storage statistics including total capacity, used space, data reduction ratios, and storage breakdown by category. Useful for capacity planning.", {}, async () => {
|
|
8
|
+
try {
|
|
9
|
+
const result = await client.getV2("stats/cluster-storage");
|
|
10
|
+
return toolResult(JSON.stringify(result, null, 2));
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
return toolResult(`Error fetching cluster storage stats: ${error}`, true);
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
// ── Workload Stats ───────────────────────────────────────────────────
|
|
17
|
+
server.tool("get_workload_stats", "Get high-level workload statistics showing data volumes and counts per workload type (VMware, Physical, NAS, SQL, etc.) on the cluster.", {}, async () => {
|
|
18
|
+
try {
|
|
19
|
+
const result = await client.getV2("stats/workload-stats");
|
|
20
|
+
return toolResult(JSON.stringify(result, null, 2));
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
return toolResult(`Error fetching workload stats: ${error}`, true);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
// ── Replication Backlog Stats ────────────────────────────────────────
|
|
27
|
+
server.tool("get_replication_backlog", "Get replication backlog statistics showing how much data is pending replication to remote clusters. Useful for monitoring replication health.", {
|
|
28
|
+
is_inbound: z
|
|
29
|
+
.boolean()
|
|
30
|
+
.optional()
|
|
31
|
+
.default(false)
|
|
32
|
+
.describe("If true, returns inbound replication stats. Default is outbound."),
|
|
33
|
+
target_cluster_ids: z
|
|
34
|
+
.array(z.number())
|
|
35
|
+
.optional()
|
|
36
|
+
.describe("Filter to specific remote cluster IDs"),
|
|
37
|
+
}, async ({ is_inbound, target_cluster_ids }) => {
|
|
38
|
+
try {
|
|
39
|
+
const params = {
|
|
40
|
+
isInBound: String(is_inbound),
|
|
41
|
+
};
|
|
42
|
+
if (target_cluster_ids)
|
|
43
|
+
params.targetClusterList = target_cluster_ids.join(",");
|
|
44
|
+
const result = await client.getV2("stats/replication-backlog", params);
|
|
45
|
+
return toolResult(JSON.stringify(result, null, 2));
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
return toolResult(`Error fetching replication backlog: ${error}`, true);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
// ── Replication Clusters ─────────────────────────────────────────────
|
|
52
|
+
// The cluster requires startTimeMsecs, rollupIntervalSecs, and
|
|
53
|
+
// targetClusterList. Defaults: last 30 days, daily rollup, all clusters.
|
|
54
|
+
server.tool("get_replication_clusters", "Get list of remote replication clusters with total data replicated to/from each. Shows replication partner health at a glance.", {
|
|
55
|
+
start_time_msecs: z
|
|
56
|
+
.number()
|
|
57
|
+
.optional()
|
|
58
|
+
.describe("Start of stats window in Unix milliseconds (default: 30 days ago)"),
|
|
59
|
+
rollup_interval_secs: z
|
|
60
|
+
.number()
|
|
61
|
+
.optional()
|
|
62
|
+
.default(86400)
|
|
63
|
+
.describe("Granularity in seconds (default: 86400 = daily)"),
|
|
64
|
+
target_cluster_ids: z
|
|
65
|
+
.array(z.number())
|
|
66
|
+
.optional()
|
|
67
|
+
.describe("Restrict to these target cluster IDs (default: all clusters)"),
|
|
68
|
+
is_inbound: z
|
|
69
|
+
.boolean()
|
|
70
|
+
.optional()
|
|
71
|
+
.default(false)
|
|
72
|
+
.describe("If true, returns inbound replication stats. Default is outbound."),
|
|
73
|
+
}, async ({ start_time_msecs, rollup_interval_secs, target_cluster_ids, is_inbound }) => {
|
|
74
|
+
try {
|
|
75
|
+
const startMs = start_time_msecs ?? Date.now() - 30 * 86400 * 1000;
|
|
76
|
+
const targets = target_cluster_ids?.length
|
|
77
|
+
? target_cluster_ids.join(",")
|
|
78
|
+
: "0"; // 0 = treat as 'all'; cluster accepts and returns all
|
|
79
|
+
const params = {
|
|
80
|
+
startTimeMsecs: String(startMs),
|
|
81
|
+
rollupIntervalSecs: String(rollup_interval_secs),
|
|
82
|
+
targetClusterList: targets,
|
|
83
|
+
isInBound: String(is_inbound),
|
|
84
|
+
};
|
|
85
|
+
const result = await client.getV2("stats/replication-clusters", params);
|
|
86
|
+
return toolResult(JSON.stringify(result, null, 2));
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
return toolResult(`Error fetching replication clusters: ${error}`, true);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
// ── Replication Data Trend ───────────────────────────────────────────
|
|
93
|
+
server.tool("get_replication_data_trend", "Get replication data transfer trends over a time range. Returns time-series data showing how much data was replicated at each interval. Useful for capacity planning and SLA reporting.", {
|
|
94
|
+
start_time_msecs: z
|
|
95
|
+
.number()
|
|
96
|
+
.describe("Start time in Unix milliseconds"),
|
|
97
|
+
end_time_msecs: z
|
|
98
|
+
.number()
|
|
99
|
+
.optional()
|
|
100
|
+
.describe("End time in Unix milliseconds (defaults to now)"),
|
|
101
|
+
rollup_interval_secs: z
|
|
102
|
+
.number()
|
|
103
|
+
.optional()
|
|
104
|
+
.default(3600)
|
|
105
|
+
.describe("Granularity of data points in seconds (e.g. 3600 = hourly, 86400 = daily)"),
|
|
106
|
+
is_inbound: z
|
|
107
|
+
.boolean()
|
|
108
|
+
.optional()
|
|
109
|
+
.default(false)
|
|
110
|
+
.describe("If true, returns inbound replication trends"),
|
|
111
|
+
}, async ({ start_time_msecs, end_time_msecs, rollup_interval_secs, is_inbound }) => {
|
|
112
|
+
try {
|
|
113
|
+
const params = {
|
|
114
|
+
startTimeMsecs: String(start_time_msecs),
|
|
115
|
+
rollupIntervalSecs: String(rollup_interval_secs),
|
|
116
|
+
isInBound: String(is_inbound),
|
|
117
|
+
};
|
|
118
|
+
if (end_time_msecs)
|
|
119
|
+
params.endTimeMsecs = String(end_time_msecs);
|
|
120
|
+
const result = await client.getV2("stats/replication-data-trend", params);
|
|
121
|
+
return toolResult(JSON.stringify(result, null, 2));
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
return toolResult(`Error fetching replication data trend: ${error}`, true);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
// ── Replication Objects ──────────────────────────────────────────────
|
|
128
|
+
server.tool("get_replication_objects", "List all objects that have been replicated to remote clusters in the given time range, with per-object replication status and data size.", {
|
|
129
|
+
start_time_msecs: z
|
|
130
|
+
.number()
|
|
131
|
+
.optional()
|
|
132
|
+
.describe("Start time in Unix milliseconds"),
|
|
133
|
+
end_time_msecs: z
|
|
134
|
+
.number()
|
|
135
|
+
.optional()
|
|
136
|
+
.describe("End time in Unix milliseconds"),
|
|
137
|
+
is_inbound: z
|
|
138
|
+
.boolean()
|
|
139
|
+
.optional()
|
|
140
|
+
.default(false)
|
|
141
|
+
.describe("If true, returns inbound replicated objects"),
|
|
142
|
+
target_cluster_ids: z
|
|
143
|
+
.array(z.number())
|
|
144
|
+
.optional()
|
|
145
|
+
.describe("Filter to specific remote cluster IDs"),
|
|
146
|
+
}, async ({ start_time_msecs, end_time_msecs, is_inbound, target_cluster_ids }) => {
|
|
147
|
+
try {
|
|
148
|
+
const params = {
|
|
149
|
+
isInBound: String(is_inbound),
|
|
150
|
+
};
|
|
151
|
+
if (start_time_msecs)
|
|
152
|
+
params.startTimeMsecs = String(start_time_msecs);
|
|
153
|
+
if (end_time_msecs)
|
|
154
|
+
params.endTimeMsecs = String(end_time_msecs);
|
|
155
|
+
if (target_cluster_ids)
|
|
156
|
+
params.targetClusterList = target_cluster_ids.join(",");
|
|
157
|
+
const result = await client.getV2("stats/replication-objects", params);
|
|
158
|
+
return toolResult(JSON.stringify(result, null, 2));
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
return toolResult(`Error fetching replication objects: ${error}`, true);
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
function toolResult(text, isError = false) {
|
|
3
|
+
return { content: [{ type: "text", text }], isError };
|
|
4
|
+
}
|
|
5
|
+
export function registerStorageTools(server, client) {
|
|
6
|
+
// ── List Storage Domains ─────────────────────────────────────────────
|
|
7
|
+
server.tool("list_storage_domains", "List Cohesity storage domains (view boxes) where backups are stored. The storage domain ID is required when creating protection groups.", {
|
|
8
|
+
name: z
|
|
9
|
+
.string()
|
|
10
|
+
.optional()
|
|
11
|
+
.describe("Filter storage domains by name (partial match)"),
|
|
12
|
+
max_results: z
|
|
13
|
+
.number()
|
|
14
|
+
.optional()
|
|
15
|
+
.default(50)
|
|
16
|
+
.describe("Maximum number of results to return"),
|
|
17
|
+
}, async ({ name, max_results }) => {
|
|
18
|
+
try {
|
|
19
|
+
const params = {
|
|
20
|
+
maxCount: String(max_results),
|
|
21
|
+
};
|
|
22
|
+
if (name)
|
|
23
|
+
params.name = name;
|
|
24
|
+
const result = await client.getV2("storage-domains", params);
|
|
25
|
+
return toolResult(JSON.stringify(result, null, 2));
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
return toolResult(`Error fetching storage domains: ${error}`, true);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
// ── List Objects ─────────────────────────────────────────────────────
|
|
32
|
+
server.tool("list_objects", "List protectable objects under a registered source (e.g., all VMs under a vCenter). Returns objects with direct 'id' fields suitable for use in protection groups. Requires a parentId (source or container ID from list_sources).", {
|
|
33
|
+
parent_id: z
|
|
34
|
+
.number()
|
|
35
|
+
.describe("ID of the parent source or container (e.g., vCenter ID, datacenter ID)"),
|
|
36
|
+
environments: z
|
|
37
|
+
.array(z.enum([
|
|
38
|
+
"kVMware", "kHyperV", "kPhysical", "kSQL", "kOracle",
|
|
39
|
+
"kNetapp", "kGenericNas", "kIsilon", "kFlashBlade",
|
|
40
|
+
"kKubernetes", "kAWS", "kAzure", "kGCP",
|
|
41
|
+
]))
|
|
42
|
+
.optional()
|
|
43
|
+
.describe("Filter by environment type"),
|
|
44
|
+
max_results: z
|
|
45
|
+
.number()
|
|
46
|
+
.optional()
|
|
47
|
+
.default(100)
|
|
48
|
+
.describe("Maximum number of results to return"),
|
|
49
|
+
}, async ({ parent_id, environments, max_results }) => {
|
|
50
|
+
try {
|
|
51
|
+
await client.refreshAllSources();
|
|
52
|
+
const params = {
|
|
53
|
+
parentId: String(parent_id),
|
|
54
|
+
maxCount: String(max_results),
|
|
55
|
+
};
|
|
56
|
+
if (environments)
|
|
57
|
+
params.environments = environments.join(",");
|
|
58
|
+
const result = await client.getV2("data-protect/objects", params);
|
|
59
|
+
return toolResult(JSON.stringify(result, null, 2));
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
return toolResult(`Error listing objects for parent ${parent_id}: ${error}`, true);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
// ── List Snapshots ───────────────────────────────────────────────────
|
|
66
|
+
server.tool("list_snapshots", "List available backup snapshots for a specific object (VM, database, etc.). Use the object ID from search_objects (objectProtectionInfos[0].objectId) or list_objects. Snapshot IDs are required for recovery operations.", {
|
|
67
|
+
object_id: z
|
|
68
|
+
.number()
|
|
69
|
+
.describe("Object ID to list snapshots for"),
|
|
70
|
+
protection_group_ids: z
|
|
71
|
+
.array(z.string())
|
|
72
|
+
.optional()
|
|
73
|
+
.describe("Filter snapshots by protection group IDs"),
|
|
74
|
+
run_types: z
|
|
75
|
+
.array(z.enum(["kRegular", "kFull", "kLog", "kSystem"]))
|
|
76
|
+
.optional()
|
|
77
|
+
.describe("Filter by backup run type"),
|
|
78
|
+
from_time_usecs: z
|
|
79
|
+
.number()
|
|
80
|
+
.optional()
|
|
81
|
+
.describe("Return snapshots taken after this Unix timestamp in microseconds"),
|
|
82
|
+
to_time_usecs: z
|
|
83
|
+
.number()
|
|
84
|
+
.optional()
|
|
85
|
+
.describe("Return snapshots taken before this Unix timestamp in microseconds"),
|
|
86
|
+
max_results: z
|
|
87
|
+
.number()
|
|
88
|
+
.optional()
|
|
89
|
+
.default(25)
|
|
90
|
+
.describe("Maximum number of snapshots to return"),
|
|
91
|
+
}, async ({ object_id, protection_group_ids, run_types, from_time_usecs, to_time_usecs, max_results }) => {
|
|
92
|
+
try {
|
|
93
|
+
const params = {
|
|
94
|
+
maxCount: String(max_results),
|
|
95
|
+
};
|
|
96
|
+
if (protection_group_ids)
|
|
97
|
+
params.protectionGroupIds = protection_group_ids.join(",");
|
|
98
|
+
if (run_types)
|
|
99
|
+
params.runTypes = run_types.join(",");
|
|
100
|
+
if (from_time_usecs !== undefined)
|
|
101
|
+
params.fromTimeUsecs = String(from_time_usecs);
|
|
102
|
+
if (to_time_usecs !== undefined)
|
|
103
|
+
params.toTimeUsecs = String(to_time_usecs);
|
|
104
|
+
const result = await client.getV2(`data-protect/objects/${object_id}/snapshots`, params);
|
|
105
|
+
return toolResult(JSON.stringify(result, null, 2));
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
return toolResult(`Error listing snapshots for object ${object_id}: ${error}`, true);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
// ── Browse Snapshot Files ────────────────────────────────────────────
|
|
112
|
+
server.tool("browse_snapshot_files", "Browse files and folders in a VM snapshot using Cohesity's indexed file search. Returns immediate children of the specified path. Requires indexing to be enabled on the protection group. Use object_id from search_objects (objectProtectionInfos[0].objectId). If indexing is not enabled, enable it via update_protection_group first.", {
|
|
113
|
+
object_id: z
|
|
114
|
+
.number()
|
|
115
|
+
.describe("Object (VM) ID to browse — use objectProtectionInfos[0].objectId from search_objects"),
|
|
116
|
+
path: z
|
|
117
|
+
.string()
|
|
118
|
+
.optional()
|
|
119
|
+
.default("/")
|
|
120
|
+
.describe("Directory path to browse (e.g. '/home/zerto', '/etc'). Defaults to root."),
|
|
121
|
+
search_string: z
|
|
122
|
+
.string()
|
|
123
|
+
.optional()
|
|
124
|
+
.describe("Filename pattern to search (e.g. '*.log', 'config*'). Defaults to the last segment of the path, which finds files in that directory."),
|
|
125
|
+
max_results: z
|
|
126
|
+
.number()
|
|
127
|
+
.optional()
|
|
128
|
+
.default(200)
|
|
129
|
+
.describe("Maximum number of indexed entries to fetch"),
|
|
130
|
+
}, async ({ object_id, path, search_string, max_results }) => {
|
|
131
|
+
try {
|
|
132
|
+
// Normalize path: strip trailing slash, ensure leading slash
|
|
133
|
+
const normPath = ("/" + path.replace(/^\/+|\/+$/g, "")).replace(/\/+/g, "/");
|
|
134
|
+
// Derive a useful search string from the path when not provided.
|
|
135
|
+
// Using "*" alone alphabetically exhausts count before reaching deeper paths like /home.
|
|
136
|
+
// Using the last path segment (e.g. "zerto" for /home/zerto) finds files in that directory.
|
|
137
|
+
const segments = normPath.split("/").filter(Boolean);
|
|
138
|
+
const effectiveSearch = search_string ?? (segments.length > 0 ? segments[segments.length - 1] : "*");
|
|
139
|
+
const body = {
|
|
140
|
+
objectType: "Files",
|
|
141
|
+
fileParams: {
|
|
142
|
+
searchString: effectiveSearch,
|
|
143
|
+
sourceEnvironments: ["kVMware"],
|
|
144
|
+
},
|
|
145
|
+
objectIds: [object_id],
|
|
146
|
+
count: max_results,
|
|
147
|
+
};
|
|
148
|
+
const result = await client.postV2("data-protect/search/indexed-objects", body);
|
|
149
|
+
const files = result.files ?? [];
|
|
150
|
+
if (files.length === 0) {
|
|
151
|
+
return toolResult(`No indexed files found for object ${object_id}. Indexing may not be enabled on this VM's protection group. ` +
|
|
152
|
+
`Enable it with update_protection_group using vmwareParams.indexingPolicy.enableIndexing=true, then wait for the next backup run.`);
|
|
153
|
+
}
|
|
154
|
+
// Each entry has { name, path } where path is the parent directory with lvol_N/ prefix.
|
|
155
|
+
// Full path = path + "/" + name, then strip lvol_N/ prefix.
|
|
156
|
+
const toClean = (p) => ("/" + p.replace(/^lvol_\d+\/?/, "")).replace(/\/+/g, "/");
|
|
157
|
+
// Build full clean paths for all entries
|
|
158
|
+
const allEntries = files.map(f => {
|
|
159
|
+
const rawFull = f.path + "/" + f.name;
|
|
160
|
+
const cleanFull = toClean(rawFull);
|
|
161
|
+
const cleanParent = toClean(f.path);
|
|
162
|
+
return { name: f.name, cleanFull, cleanParent, type: f.type };
|
|
163
|
+
});
|
|
164
|
+
// Filter to entries whose parent equals the requested path (direct children)
|
|
165
|
+
const isRoot = normPath === "/";
|
|
166
|
+
const children = allEntries.filter(e => {
|
|
167
|
+
if (isRoot) {
|
|
168
|
+
// Direct children of root: parent is "/" or ""
|
|
169
|
+
return e.cleanParent === "/" || e.cleanParent === "";
|
|
170
|
+
}
|
|
171
|
+
return e.cleanParent === normPath;
|
|
172
|
+
});
|
|
173
|
+
if (children.length === 0) {
|
|
174
|
+
// No direct children found — show all unique top-level paths as hint
|
|
175
|
+
const uniquePaths = [...new Set(allEntries.map(e => e.cleanParent))].sort().slice(0, 20);
|
|
176
|
+
return toolResult(`No entries found directly under '${normPath}'. ` +
|
|
177
|
+
`Indexed paths available: ${uniquePaths.join(", ")}. ` +
|
|
178
|
+
`Try browsing one of these paths, or use a broader search_string.`);
|
|
179
|
+
}
|
|
180
|
+
const entries = children.map(e => ({
|
|
181
|
+
name: e.name,
|
|
182
|
+
path: e.cleanFull,
|
|
183
|
+
type: e.type.toLowerCase().replace("k", ""),
|
|
184
|
+
}));
|
|
185
|
+
return toolResult(JSON.stringify({ browsed_path: normPath, object_id, count: entries.length, entries }, null, 2));
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
return toolResult(`Error browsing snapshot files for object ${object_id}: ${error}`, true);
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
function toolResult(text, isError = false) {
|
|
3
|
+
return { content: [{ type: "text", text }], isError };
|
|
4
|
+
}
|
|
5
|
+
export function registerTieringTools(server, client) {
|
|
6
|
+
// ── List Data Tiering Tasks ──────────────────────────────────────────
|
|
7
|
+
server.tool("list_tiering_tasks", "List all data tiering tasks that move cold data from primary storage to external targets (cloud, vault). Shows status, schedule, and source/target configuration.", {
|
|
8
|
+
ids: z
|
|
9
|
+
.array(z.string())
|
|
10
|
+
.optional()
|
|
11
|
+
.describe("Filter by specific task IDs"),
|
|
12
|
+
include_downtiered_locations: z
|
|
13
|
+
.boolean()
|
|
14
|
+
.optional()
|
|
15
|
+
.default(false)
|
|
16
|
+
.describe("Include list of locations where data has been down-tiered"),
|
|
17
|
+
}, async ({ ids, include_downtiered_locations }) => {
|
|
18
|
+
try {
|
|
19
|
+
const params = {
|
|
20
|
+
includeDowntieredDataLocation: String(include_downtiered_locations),
|
|
21
|
+
};
|
|
22
|
+
if (ids)
|
|
23
|
+
params.ids = ids.join(",");
|
|
24
|
+
const result = await client.getV2("data-tiering/tasks", params);
|
|
25
|
+
return toolResult(JSON.stringify(result, null, 2));
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
return toolResult(`Error listing tiering tasks: ${error}`, true);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
// ── Get Tiering Task ─────────────────────────────────────────────────
|
|
32
|
+
server.tool("get_tiering_task", "Get details of a specific data tiering task by ID.", {
|
|
33
|
+
id: z.string().describe("Tiering task ID"),
|
|
34
|
+
}, async ({ id }) => {
|
|
35
|
+
try {
|
|
36
|
+
const result = await client.getV2(`data-tiering/tasks/${id}`);
|
|
37
|
+
return toolResult(JSON.stringify(result, null, 2));
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
return toolResult(`Error fetching tiering task ${id}: ${error}`, true);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
// ── Create Tiering Task ──────────────────────────────────────────────
|
|
44
|
+
server.tool("create_tiering_task", "Create a data tiering task to automatically move cold data from a NAS/View source to an external target. Requires a registered external target (use list_external_targets to find IDs).", {
|
|
45
|
+
name: z.string().describe("Name for the tiering task"),
|
|
46
|
+
description: z.string().optional().describe("Optional description"),
|
|
47
|
+
source_id: z
|
|
48
|
+
.number()
|
|
49
|
+
.describe("Source protection source ID (NAS or View source from list_sources)"),
|
|
50
|
+
external_target_id: z
|
|
51
|
+
.number()
|
|
52
|
+
.describe("Destination external target ID (from list_external_targets)"),
|
|
53
|
+
days_cold: z
|
|
54
|
+
.number()
|
|
55
|
+
.optional()
|
|
56
|
+
.default(90)
|
|
57
|
+
.describe("Tier data that has not been accessed for this many days"),
|
|
58
|
+
schedule_unit: z
|
|
59
|
+
.enum(["Days", "Weeks"])
|
|
60
|
+
.optional()
|
|
61
|
+
.default("Days")
|
|
62
|
+
.describe("Tiering schedule frequency unit"),
|
|
63
|
+
schedule_frequency: z
|
|
64
|
+
.number()
|
|
65
|
+
.optional()
|
|
66
|
+
.default(1)
|
|
67
|
+
.describe("How often to run the tiering task (e.g. 1 = daily)"),
|
|
68
|
+
}, async ({ name, description, source_id, external_target_id, days_cold, schedule_unit, schedule_frequency }) => {
|
|
69
|
+
try {
|
|
70
|
+
const body = {
|
|
71
|
+
name,
|
|
72
|
+
type: "Downtier",
|
|
73
|
+
source: { id: source_id },
|
|
74
|
+
targets: [{ id: external_target_id }],
|
|
75
|
+
filtersConfig: {
|
|
76
|
+
lastAccessTimeFilter: {
|
|
77
|
+
daysCount: days_cold,
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
schedule: {
|
|
81
|
+
unit: schedule_unit,
|
|
82
|
+
frequency: schedule_frequency,
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
if (description)
|
|
86
|
+
body.description = description;
|
|
87
|
+
const result = await client.postV2("data-tiering/tasks", body);
|
|
88
|
+
return toolResult(JSON.stringify(result, null, 2));
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
return toolResult(`Error creating tiering task: ${error}`, true);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
// ── Run Tiering Task ─────────────────────────────────────────────────
|
|
95
|
+
server.tool("run_tiering_task", "Trigger an on-demand run of a data tiering task.", {
|
|
96
|
+
id: z.string().describe("Tiering task ID to run"),
|
|
97
|
+
}, async ({ id }) => {
|
|
98
|
+
try {
|
|
99
|
+
await client.postV2(`data-tiering/tasks/${id}/runs`, {});
|
|
100
|
+
return toolResult(`Tiering task ${id} run initiated successfully.`);
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
return toolResult(`Error running tiering task ${id}: ${error}`, true);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
// ── Pause / Resume Tiering Task ──────────────────────────────────────
|
|
107
|
+
server.tool("update_tiering_task_state", "Pause or resume one or more data tiering tasks.", {
|
|
108
|
+
ids: z.array(z.string()).describe("List of tiering task IDs"),
|
|
109
|
+
action: z.enum(["Pause", "Resume"]).describe("Action to perform"),
|
|
110
|
+
}, async ({ ids, action }) => {
|
|
111
|
+
try {
|
|
112
|
+
const body = { ids, action };
|
|
113
|
+
const result = await client.postV2("data-tiering/tasks/states", body);
|
|
114
|
+
return toolResult(JSON.stringify(result, null, 2));
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
return toolResult(`Error updating tiering task state: ${error}`, true);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
// ── Delete Tiering Task ──────────────────────────────────────────────
|
|
121
|
+
server.tool("delete_tiering_task", "Delete a data tiering task.", {
|
|
122
|
+
id: z.string().describe("Tiering task ID to delete"),
|
|
123
|
+
}, async ({ id }) => {
|
|
124
|
+
try {
|
|
125
|
+
await client.deleteV2(`data-tiering/tasks/${id}`);
|
|
126
|
+
return toolResult(`Tiering task ${id} deleted successfully.`);
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
return toolResult(`Error deleting tiering task ${id}: ${error}`, true);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User management tools — list, create, update, and delete Cohesity users
|
|
3
|
+
* (both LOCAL and AD/IdP-backed). All shapes verified against cluster_v2_api.yaml.
|
|
4
|
+
*
|
|
5
|
+
* GET /v2/users — UsersList
|
|
6
|
+
* POST /v2/users — CreateUsersParameters (array of CreateUserParameters)
|
|
7
|
+
* POST /v2/users/delete — DeleteUsersRequest { sids[] }
|
|
8
|
+
* GET /v2/users/{sid} — User
|
|
9
|
+
* PUT /v2/users/{sid} — UpdateUserParameters
|
|
10
|
+
*/
|
|
11
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
12
|
+
import { CohesityClient } from "../cohesity-client.js";
|
|
13
|
+
export declare function registerUserTools(server: McpServer, client: CohesityClient): void;
|