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,156 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
function toolResult(text, isError = false) {
|
|
3
|
+
return { content: [{ type: "text", text }], isError };
|
|
4
|
+
}
|
|
5
|
+
const ALERT_CATEGORIES = [
|
|
6
|
+
"kDisk", "kNode", "kCluster", "kChassis", "kPowerSupply", "kCPU", "kMemory",
|
|
7
|
+
"kTemperature", "kFan", "kNIC", "kFirmware", "kNodeHealth", "kOperatingSystem",
|
|
8
|
+
"kDataPath", "kMetadata", "kIndexing", "kHelios", "kSystemService", "kLicense",
|
|
9
|
+
"kSecurity", "kUpgrade", "kClusterManagement", "kAuditLog", "kNetworking",
|
|
10
|
+
"kConfiguration", "kStorageUsage", "kFaultTolerance", "kBackupRestore",
|
|
11
|
+
"kArchivalRestore", "kRemoteReplication", "kQuota", "kCDP", "kDisasterRecovery",
|
|
12
|
+
"kAgent",
|
|
13
|
+
];
|
|
14
|
+
export function registerNotificationTools(server, client) {
|
|
15
|
+
// ── List Notification Rules ──────────────────────────────────────────
|
|
16
|
+
server.tool("list_notification_rules", "List all alert notification rules. Each rule defines which alert categories/severities trigger email, SNMP, syslog, or webhook notifications.", {
|
|
17
|
+
ids: z
|
|
18
|
+
.array(z.number())
|
|
19
|
+
.optional()
|
|
20
|
+
.describe("Filter by specific rule IDs. Omit to return all rules."),
|
|
21
|
+
}, async ({ ids }) => {
|
|
22
|
+
try {
|
|
23
|
+
const params = {};
|
|
24
|
+
if (ids)
|
|
25
|
+
params.ids = ids.join(",");
|
|
26
|
+
const result = await client.getV2("alerts/config/notification-rules", params);
|
|
27
|
+
return toolResult(JSON.stringify(result, null, 2));
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
return toolResult(`Error listing notification rules: ${error}`, true);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
// ── Create Notification Rule ─────────────────────────────────────────
|
|
34
|
+
server.tool("create_notification_rule", "Create an alert notification rule to send emails or webhooks when specific alert categories or severities occur.", {
|
|
35
|
+
rule_name: z.string().describe("Name for the notification rule"),
|
|
36
|
+
categories: z
|
|
37
|
+
.array(z.enum(ALERT_CATEGORIES))
|
|
38
|
+
.optional()
|
|
39
|
+
.describe("Alert categories this rule applies to (e.g. kBackupRestore, kDisasterRecovery, kSecurity)"),
|
|
40
|
+
severities: z
|
|
41
|
+
.array(z.enum(["kCritical", "kWarning", "kInfo"]))
|
|
42
|
+
.optional()
|
|
43
|
+
.describe("Alert severity levels this rule applies to"),
|
|
44
|
+
email_addresses: z
|
|
45
|
+
.array(z.string())
|
|
46
|
+
.optional()
|
|
47
|
+
.describe("Email addresses to notify (e.g. ['admin@example.com'])"),
|
|
48
|
+
webhook_url: z
|
|
49
|
+
.string()
|
|
50
|
+
.optional()
|
|
51
|
+
.describe("Webhook URL to POST alert payload to (e.g. Slack, PagerDuty, Teams)"),
|
|
52
|
+
snmp_enabled: z
|
|
53
|
+
.boolean()
|
|
54
|
+
.optional()
|
|
55
|
+
.default(false)
|
|
56
|
+
.describe("Enable SNMP trap notifications"),
|
|
57
|
+
syslog_enabled: z
|
|
58
|
+
.boolean()
|
|
59
|
+
.optional()
|
|
60
|
+
.default(false)
|
|
61
|
+
.describe("Enable syslog notifications"),
|
|
62
|
+
}, async ({ rule_name, categories, severities, email_addresses, webhook_url, snmp_enabled, syslog_enabled }) => {
|
|
63
|
+
try {
|
|
64
|
+
const body = {
|
|
65
|
+
ruleName: rule_name,
|
|
66
|
+
snmpEnabled: snmp_enabled,
|
|
67
|
+
syslogEnabled: syslog_enabled,
|
|
68
|
+
};
|
|
69
|
+
if (categories)
|
|
70
|
+
body.categories = categories;
|
|
71
|
+
if (severities)
|
|
72
|
+
body.severities = severities;
|
|
73
|
+
if (email_addresses?.length) {
|
|
74
|
+
body.emailDeliveryTargets = email_addresses.map((email) => ({
|
|
75
|
+
emailAddress: email,
|
|
76
|
+
}));
|
|
77
|
+
}
|
|
78
|
+
if (webhook_url) {
|
|
79
|
+
body.webhookDeliveryTargets = [{ url: webhook_url }];
|
|
80
|
+
}
|
|
81
|
+
const result = await client.postV2("alerts/config/notification-rules", body);
|
|
82
|
+
return toolResult(JSON.stringify(result, null, 2));
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
return toolResult(`Error creating notification rule: ${error}`, true);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
// ── Update Notification Rule ─────────────────────────────────────────
|
|
89
|
+
server.tool("update_notification_rule", "Update an existing alert notification rule by ID.", {
|
|
90
|
+
id: z.number().describe("Notification rule ID to update"),
|
|
91
|
+
rule_name: z.string().describe("Rule name"),
|
|
92
|
+
categories: z
|
|
93
|
+
.array(z.enum(ALERT_CATEGORIES))
|
|
94
|
+
.optional()
|
|
95
|
+
.describe("Alert categories this rule applies to"),
|
|
96
|
+
severities: z
|
|
97
|
+
.array(z.enum(["kCritical", "kWarning", "kInfo"]))
|
|
98
|
+
.optional()
|
|
99
|
+
.describe("Alert severity levels"),
|
|
100
|
+
email_addresses: z
|
|
101
|
+
.array(z.string())
|
|
102
|
+
.optional()
|
|
103
|
+
.describe("Email addresses to notify"),
|
|
104
|
+
webhook_url: z
|
|
105
|
+
.string()
|
|
106
|
+
.optional()
|
|
107
|
+
.describe("Webhook URL"),
|
|
108
|
+
snmp_enabled: z
|
|
109
|
+
.boolean()
|
|
110
|
+
.optional()
|
|
111
|
+
.default(false)
|
|
112
|
+
.describe("Enable SNMP trap notifications"),
|
|
113
|
+
syslog_enabled: z
|
|
114
|
+
.boolean()
|
|
115
|
+
.optional()
|
|
116
|
+
.default(false)
|
|
117
|
+
.describe("Enable syslog notifications"),
|
|
118
|
+
}, async ({ id, rule_name, categories, severities, email_addresses, webhook_url, snmp_enabled, syslog_enabled }) => {
|
|
119
|
+
try {
|
|
120
|
+
const body = {
|
|
121
|
+
ruleName: rule_name,
|
|
122
|
+
snmpEnabled: snmp_enabled,
|
|
123
|
+
syslogEnabled: syslog_enabled,
|
|
124
|
+
};
|
|
125
|
+
if (categories)
|
|
126
|
+
body.categories = categories;
|
|
127
|
+
if (severities)
|
|
128
|
+
body.severities = severities;
|
|
129
|
+
if (email_addresses?.length) {
|
|
130
|
+
body.emailDeliveryTargets = email_addresses.map((email) => ({
|
|
131
|
+
emailAddress: email,
|
|
132
|
+
}));
|
|
133
|
+
}
|
|
134
|
+
if (webhook_url) {
|
|
135
|
+
body.webhookDeliveryTargets = [{ url: webhook_url }];
|
|
136
|
+
}
|
|
137
|
+
const result = await client.putV2(`alerts/config/notification-rules/${id}`, body);
|
|
138
|
+
return toolResult(JSON.stringify(result, null, 2));
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
return toolResult(`Error updating notification rule ${id}: ${error}`, true);
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
// ── Delete Notification Rule ─────────────────────────────────────────
|
|
145
|
+
server.tool("delete_notification_rule", "Delete an alert notification rule by ID.", {
|
|
146
|
+
id: z.number().describe("Notification rule ID to delete"),
|
|
147
|
+
}, async ({ id }) => {
|
|
148
|
+
try {
|
|
149
|
+
await client.deleteV2(`alerts/config/notification-rules/${id}`);
|
|
150
|
+
return toolResult(`Notification rule ${id} deleted successfully.`);
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
return toolResult(`Error deleting notification rule ${id}: ${error}`, true);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
}
|
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
function toolResult(text, isError = false) {
|
|
3
|
+
return { content: [{ type: "text", text }], isError };
|
|
4
|
+
}
|
|
5
|
+
export function registerProtectionTools(server, client) {
|
|
6
|
+
// ── List Protection Policies ─────────────────────────────────────────
|
|
7
|
+
server.tool("list_protection_policies", "List all Cohesity data protection policies including schedule, retention, and replication settings", {
|
|
8
|
+
name: z
|
|
9
|
+
.string()
|
|
10
|
+
.optional()
|
|
11
|
+
.describe("Filter policies by name (partial match)"),
|
|
12
|
+
}, async ({ name }) => {
|
|
13
|
+
try {
|
|
14
|
+
const params = {};
|
|
15
|
+
if (name)
|
|
16
|
+
params.policyNames = name;
|
|
17
|
+
const result = await client.getV2("data-protect/policies", params);
|
|
18
|
+
return toolResult(JSON.stringify(result, null, 2));
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
return toolResult(`Error fetching protection policies: ${error}`, true);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
// ── Create Protection Policy ─────────────────────────────────────────
|
|
25
|
+
server.tool("create_protection_policy", `Create a new Cohesity protection policy. Supports:
|
|
26
|
+
- Basic: incremental-only with retention (e.g. daily incremental, keep 30 days)
|
|
27
|
+
- GFS (Grandfather-Father-Son): incremental + weekly full + extended retention tiers (weekly/monthly/yearly). GFS requires full_schedule_day_of_week and at least one of gfs_weekly_retention/gfs_monthly_retention/gfs_yearly_retention.
|
|
28
|
+
- DataLock: WORM protection that prevents snapshot deletion. When enabled, applies to all retention tiers automatically.
|
|
29
|
+
- Retry options and description also supported.`, {
|
|
30
|
+
name: z.string().describe("Name for the new protection policy"),
|
|
31
|
+
description: z.string().optional().describe("Optional description"),
|
|
32
|
+
// Incremental schedule
|
|
33
|
+
incremental_schedule_unit: z
|
|
34
|
+
.enum(["Minutes", "Hours", "Days", "Weeks"])
|
|
35
|
+
.describe("How often to run incremental backups (e.g. Days for daily)"),
|
|
36
|
+
incremental_frequency: z
|
|
37
|
+
.number()
|
|
38
|
+
.optional()
|
|
39
|
+
.default(1)
|
|
40
|
+
.describe("Frequency multiplier (e.g. 1 = every 1 day, 4 = every 4 hours)"),
|
|
41
|
+
// Base retention
|
|
42
|
+
retention_duration: z
|
|
43
|
+
.number()
|
|
44
|
+
.describe("How long to keep daily backups (number of retention_unit)"),
|
|
45
|
+
retention_unit: z
|
|
46
|
+
.enum(["Days", "Weeks", "Months", "Years"])
|
|
47
|
+
.describe("Unit for base retention duration"),
|
|
48
|
+
// Full backup schedule (required for GFS)
|
|
49
|
+
full_schedule_day_of_week: z
|
|
50
|
+
.array(z.enum(["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]))
|
|
51
|
+
.optional()
|
|
52
|
+
.describe("Days to run weekly full backups. Required when using GFS extended retention."),
|
|
53
|
+
// GFS extended retention tiers
|
|
54
|
+
gfs_weekly_retention: z
|
|
55
|
+
.number()
|
|
56
|
+
.optional()
|
|
57
|
+
.describe("Keep weekly full backups for this many weeks (GFS weekly tier)"),
|
|
58
|
+
gfs_monthly_retention: z
|
|
59
|
+
.number()
|
|
60
|
+
.optional()
|
|
61
|
+
.describe("Keep monthly full backups for this many months (GFS monthly tier)"),
|
|
62
|
+
gfs_yearly_retention: z
|
|
63
|
+
.number()
|
|
64
|
+
.optional()
|
|
65
|
+
.describe("Keep yearly full backups for this many years (GFS yearly tier)"),
|
|
66
|
+
// DataLock
|
|
67
|
+
datalock_mode: z
|
|
68
|
+
.enum(["Administrative", "Compliance"])
|
|
69
|
+
.optional()
|
|
70
|
+
.describe("Enable DataLock (WORM). Administrative allows admin override; Compliance is strict. Applied to all retention tiers."),
|
|
71
|
+
datalock_duration: z
|
|
72
|
+
.number()
|
|
73
|
+
.optional()
|
|
74
|
+
.describe("DataLock duration (uses same value and unit as each retention tier if not specified)"),
|
|
75
|
+
// Retry options
|
|
76
|
+
retries: z
|
|
77
|
+
.number()
|
|
78
|
+
.optional()
|
|
79
|
+
.default(3)
|
|
80
|
+
.describe("Number of retry attempts on backup failure"),
|
|
81
|
+
retry_interval_mins: z
|
|
82
|
+
.number()
|
|
83
|
+
.optional()
|
|
84
|
+
.default(5)
|
|
85
|
+
.describe("Minutes to wait between retry attempts"),
|
|
86
|
+
}, async ({ name, description, incremental_schedule_unit, incremental_frequency, retention_duration, retention_unit, full_schedule_day_of_week, gfs_weekly_retention, gfs_monthly_retention, gfs_yearly_retention, datalock_mode, datalock_duration, retries, retry_interval_mins, }) => {
|
|
87
|
+
try {
|
|
88
|
+
const hasGFS = !!(gfs_weekly_retention || gfs_monthly_retention || gfs_yearly_retention);
|
|
89
|
+
// Helper to build a dataLockConfig for a given duration+unit
|
|
90
|
+
const buildDataLock = (duration, unit) => datalock_mode
|
|
91
|
+
? { dataLockConfig: { mode: datalock_mode, unit, duration: datalock_duration ?? duration } }
|
|
92
|
+
: {};
|
|
93
|
+
// Incremental schedule
|
|
94
|
+
const scheduleKey = incremental_schedule_unit === "Minutes" ? "minuteSchedule" :
|
|
95
|
+
incremental_schedule_unit === "Hours" ? "hourSchedule" :
|
|
96
|
+
incremental_schedule_unit === "Days" ? "daySchedule" : "weekSchedule";
|
|
97
|
+
const regular = {
|
|
98
|
+
incremental: {
|
|
99
|
+
schedule: { unit: incremental_schedule_unit, [scheduleKey]: { frequency: incremental_frequency } },
|
|
100
|
+
},
|
|
101
|
+
retention: {
|
|
102
|
+
unit: retention_unit,
|
|
103
|
+
duration: retention_duration,
|
|
104
|
+
...buildDataLock(retention_duration, retention_unit),
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
// Full backup schedule — required when using GFS
|
|
108
|
+
if (hasGFS || full_schedule_day_of_week) {
|
|
109
|
+
regular.full = {
|
|
110
|
+
schedule: {
|
|
111
|
+
unit: "Weeks",
|
|
112
|
+
weekSchedule: { dayOfWeek: full_schedule_day_of_week ?? ["Sunday"] },
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
// GFS extended retention tiers (all use runType: "Full")
|
|
117
|
+
const extendedRetention = [];
|
|
118
|
+
if (gfs_weekly_retention) {
|
|
119
|
+
extendedRetention.push({
|
|
120
|
+
schedule: { unit: "Weeks", frequency: 1 },
|
|
121
|
+
retention: { unit: "Weeks", duration: gfs_weekly_retention, ...buildDataLock(gfs_weekly_retention, "Weeks") },
|
|
122
|
+
runType: "Full",
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
if (gfs_monthly_retention) {
|
|
126
|
+
extendedRetention.push({
|
|
127
|
+
schedule: { unit: "Months", frequency: 1 },
|
|
128
|
+
retention: { unit: "Months", duration: gfs_monthly_retention, ...buildDataLock(gfs_monthly_retention, "Months") },
|
|
129
|
+
runType: "Full",
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
if (gfs_yearly_retention) {
|
|
133
|
+
extendedRetention.push({
|
|
134
|
+
schedule: { unit: "Years", frequency: 1 },
|
|
135
|
+
retention: { unit: "Years", duration: gfs_yearly_retention, ...buildDataLock(gfs_yearly_retention, "Years") },
|
|
136
|
+
runType: "Full",
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
const body = {
|
|
140
|
+
name,
|
|
141
|
+
backupPolicy: { regular },
|
|
142
|
+
retryOptions: { retries, retryIntervalMins: retry_interval_mins },
|
|
143
|
+
};
|
|
144
|
+
if (description)
|
|
145
|
+
body.description = description;
|
|
146
|
+
if (extendedRetention.length > 0)
|
|
147
|
+
body.extendedRetention = extendedRetention;
|
|
148
|
+
const result = await client.postV2("data-protect/policies", body);
|
|
149
|
+
return toolResult(JSON.stringify(result, null, 2));
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
return toolResult(`Error creating protection policy: ${error}`, true);
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
// ── Update Protection Policy ─────────────────────────────────────────
|
|
156
|
+
server.tool("update_protection_policy", "Update an existing Cohesity protection policy. Fetches the current policy and applies changes. Use list_protection_policies to get the policy ID.", {
|
|
157
|
+
id: z.string().describe("Protection policy ID to update"),
|
|
158
|
+
name: z.string().optional().describe("New name for the policy"),
|
|
159
|
+
description: z.string().optional().describe("New description"),
|
|
160
|
+
incremental_schedule_unit: z
|
|
161
|
+
.enum(["Minutes", "Hours", "Days", "Weeks", "Months"])
|
|
162
|
+
.optional()
|
|
163
|
+
.describe("Change the incremental backup schedule unit"),
|
|
164
|
+
incremental_frequency: z
|
|
165
|
+
.number()
|
|
166
|
+
.optional()
|
|
167
|
+
.describe("Change the incremental backup frequency"),
|
|
168
|
+
retention_duration: z
|
|
169
|
+
.number()
|
|
170
|
+
.optional()
|
|
171
|
+
.describe("Change retention duration"),
|
|
172
|
+
retention_unit: z
|
|
173
|
+
.enum(["Days", "Weeks", "Months", "Years"])
|
|
174
|
+
.optional()
|
|
175
|
+
.describe("Change retention unit"),
|
|
176
|
+
}, async ({ id, name, description, incremental_schedule_unit, incremental_frequency, retention_duration, retention_unit }) => {
|
|
177
|
+
try {
|
|
178
|
+
const current = await client.getV2(`data-protect/policies/${id}`);
|
|
179
|
+
const body = { ...current };
|
|
180
|
+
if (name)
|
|
181
|
+
body.name = name;
|
|
182
|
+
if (description !== undefined)
|
|
183
|
+
body.description = description;
|
|
184
|
+
if (incremental_schedule_unit || incremental_frequency !== undefined || retention_duration !== undefined || retention_unit) {
|
|
185
|
+
const backupPolicy = (body.backupPolicy ?? {});
|
|
186
|
+
const regular = (backupPolicy.regular ?? {});
|
|
187
|
+
if (incremental_schedule_unit || incremental_frequency !== undefined) {
|
|
188
|
+
const unit = incremental_schedule_unit ??
|
|
189
|
+
regular.incremental?.schedule?.unit ?? "Days";
|
|
190
|
+
const freq = incremental_frequency ?? 1;
|
|
191
|
+
const scheduleKey = unit === "Minutes" ? "minuteSchedule" :
|
|
192
|
+
unit === "Hours" ? "hourSchedule" :
|
|
193
|
+
unit === "Days" ? "daySchedule" :
|
|
194
|
+
unit === "Weeks" ? "weekSchedule" : "monthSchedule";
|
|
195
|
+
regular.incremental = { schedule: { unit, [scheduleKey]: { frequency: freq } } };
|
|
196
|
+
}
|
|
197
|
+
if (retention_duration !== undefined || retention_unit) {
|
|
198
|
+
const currentRetention = (regular.retention ?? {});
|
|
199
|
+
regular.retention = {
|
|
200
|
+
...currentRetention,
|
|
201
|
+
...(retention_duration !== undefined ? { duration: retention_duration } : {}),
|
|
202
|
+
...(retention_unit ? { unit: retention_unit } : {}),
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
body.backupPolicy = { ...backupPolicy, regular };
|
|
206
|
+
}
|
|
207
|
+
const result = await client.putV2(`data-protect/policies/${id}`, body);
|
|
208
|
+
return toolResult(JSON.stringify(result, null, 2));
|
|
209
|
+
}
|
|
210
|
+
catch (error) {
|
|
211
|
+
return toolResult(`Error updating protection policy ${id}: ${error}`, true);
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
// ── Delete Protection Policy ─────────────────────────────────────────
|
|
215
|
+
server.tool("delete_protection_policy", "Delete a Cohesity protection policy. The policy must not be in use by any protection groups.", {
|
|
216
|
+
id: z.string().describe("Protection policy ID to delete"),
|
|
217
|
+
}, async ({ id }) => {
|
|
218
|
+
try {
|
|
219
|
+
await client.deleteV2(`data-protect/policies/${id}`);
|
|
220
|
+
return toolResult(`Protection policy ${id} deleted successfully.`);
|
|
221
|
+
}
|
|
222
|
+
catch (error) {
|
|
223
|
+
return toolResult(`Error deleting protection policy ${id}: ${error}`, true);
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
// ── List Protection Groups ───────────────────────────────────────────
|
|
227
|
+
server.tool("list_protection_groups", "List Cohesity protection groups (backup jobs) with status, schedule, and last run information", {
|
|
228
|
+
name: z
|
|
229
|
+
.string()
|
|
230
|
+
.optional()
|
|
231
|
+
.describe("Filter protection groups by name (partial match)"),
|
|
232
|
+
environment: z
|
|
233
|
+
.string()
|
|
234
|
+
.optional()
|
|
235
|
+
.describe("Filter by environment type (e.g., kVMware, kPhysical, kNas, kSQL)"),
|
|
236
|
+
is_active: z
|
|
237
|
+
.boolean()
|
|
238
|
+
.optional()
|
|
239
|
+
.describe("Filter by active/inactive state"),
|
|
240
|
+
is_paused: z
|
|
241
|
+
.boolean()
|
|
242
|
+
.optional()
|
|
243
|
+
.describe("Filter by paused state"),
|
|
244
|
+
max_results: z
|
|
245
|
+
.number()
|
|
246
|
+
.optional()
|
|
247
|
+
.default(50)
|
|
248
|
+
.describe("Maximum number of results to return"),
|
|
249
|
+
}, async ({ name, environment, is_active, is_paused, max_results }) => {
|
|
250
|
+
try {
|
|
251
|
+
const params = {
|
|
252
|
+
maxCount: String(max_results),
|
|
253
|
+
includeTenants: "true",
|
|
254
|
+
includeLastRunInfo: "true",
|
|
255
|
+
};
|
|
256
|
+
if (name)
|
|
257
|
+
params.names = name;
|
|
258
|
+
if (environment)
|
|
259
|
+
params.environments = environment;
|
|
260
|
+
if (is_active !== undefined)
|
|
261
|
+
params.isActive = String(is_active);
|
|
262
|
+
if (is_paused !== undefined)
|
|
263
|
+
params.isPaused = String(is_paused);
|
|
264
|
+
const result = await client.getV2("data-protect/protection-groups", params);
|
|
265
|
+
return toolResult(JSON.stringify(result, null, 2));
|
|
266
|
+
}
|
|
267
|
+
catch (error) {
|
|
268
|
+
return toolResult(`Error fetching protection groups: ${error}`, true);
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
// ── Get Protection Group ─────────────────────────────────────────────
|
|
272
|
+
server.tool("get_protection_group", "Get detailed information about a specific Cohesity protection group including configuration, schedule, and last run status", {
|
|
273
|
+
id: z
|
|
274
|
+
.string()
|
|
275
|
+
.describe("Protection group ID"),
|
|
276
|
+
include_last_run_info: z
|
|
277
|
+
.boolean()
|
|
278
|
+
.optional()
|
|
279
|
+
.default(true)
|
|
280
|
+
.describe("Include information about the last backup run"),
|
|
281
|
+
}, async ({ id, include_last_run_info }) => {
|
|
282
|
+
try {
|
|
283
|
+
const params = {
|
|
284
|
+
includeLastRunInfo: String(include_last_run_info),
|
|
285
|
+
};
|
|
286
|
+
const result = await client.getV2(`data-protect/protection-groups/${id}`, params);
|
|
287
|
+
return toolResult(JSON.stringify(result, null, 2));
|
|
288
|
+
}
|
|
289
|
+
catch (error) {
|
|
290
|
+
return toolResult(`Error fetching protection group ${id}: ${error}`, true);
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
// ── Create Protection Group ──────────────────────────────────────────
|
|
294
|
+
server.tool("create_protection_group", "Create a new Cohesity protection group (backup job). For VMware, provide vm_ids to protect specific VMs. Requires a policy ID and storage domain ID.", {
|
|
295
|
+
name: z.string().describe("Name for the new protection group"),
|
|
296
|
+
policy_id: z.string().describe("ID of the protection policy to apply"),
|
|
297
|
+
environment: z
|
|
298
|
+
.enum([
|
|
299
|
+
"kVMware",
|
|
300
|
+
"kPhysical",
|
|
301
|
+
"kPhysicalFiles",
|
|
302
|
+
"kSQL",
|
|
303
|
+
"kOracle",
|
|
304
|
+
"kNetapp",
|
|
305
|
+
"kGenericNas",
|
|
306
|
+
"kIsilon",
|
|
307
|
+
"kView",
|
|
308
|
+
"kO365",
|
|
309
|
+
"kAWS",
|
|
310
|
+
"kAzure",
|
|
311
|
+
"kGCP",
|
|
312
|
+
"kHyperV",
|
|
313
|
+
"kS3Compatible",
|
|
314
|
+
])
|
|
315
|
+
.describe("Environment type of the objects to protect"),
|
|
316
|
+
storage_domain_id: z
|
|
317
|
+
.number()
|
|
318
|
+
.describe("Storage domain (view box) ID where backups will be written"),
|
|
319
|
+
vm_ids: z
|
|
320
|
+
.array(z.number())
|
|
321
|
+
.optional()
|
|
322
|
+
.describe("Object IDs to protect (VMs for kVMware/kHyperV/kAWS/kAzure/kGCP, hosts for kPhysical, etc.)"),
|
|
323
|
+
aws_workload_type: z
|
|
324
|
+
.enum(["kEC2", "kRDS", "kS3", "kDynamoDB", "kAurora", "kRedshift"])
|
|
325
|
+
.optional()
|
|
326
|
+
.describe("Required for kAWS environment — selects which AWS workload params to use (default: kEC2)"),
|
|
327
|
+
description: z.string().optional().describe("Optional description for the group"),
|
|
328
|
+
priority: z
|
|
329
|
+
.enum(["kLow", "kMedium", "kHigh"])
|
|
330
|
+
.optional()
|
|
331
|
+
.describe("Priority of the protection group"),
|
|
332
|
+
}, async ({ name, policy_id, environment, storage_domain_id, vm_ids, aws_workload_type, description, priority }) => {
|
|
333
|
+
try {
|
|
334
|
+
await client.refreshAllSources();
|
|
335
|
+
const body = {
|
|
336
|
+
name,
|
|
337
|
+
policyId: policy_id,
|
|
338
|
+
environment,
|
|
339
|
+
storageDomainId: storage_domain_id,
|
|
340
|
+
};
|
|
341
|
+
if (description)
|
|
342
|
+
body.description = description;
|
|
343
|
+
if (priority)
|
|
344
|
+
body.priority = priority;
|
|
345
|
+
if (environment === "kVMware" && vm_ids) {
|
|
346
|
+
body.vmwareParams = { objects: vm_ids.map((id) => ({ id })) };
|
|
347
|
+
}
|
|
348
|
+
else if (environment === "kHyperV" && vm_ids) {
|
|
349
|
+
body.hypervParams = { objects: vm_ids.map((id) => ({ id })) };
|
|
350
|
+
}
|
|
351
|
+
else if (environment === "kAWS" && vm_ids) {
|
|
352
|
+
// Cohesity uses workload-specific subkeys under awsParams
|
|
353
|
+
// (awsEc2Params, awsRdsParams, awsS3Params, etc.). Default to EC2.
|
|
354
|
+
const subkey = {
|
|
355
|
+
kEC2: "awsEc2Params",
|
|
356
|
+
kRDS: "awsRdsParams",
|
|
357
|
+
kS3: "awsS3Params",
|
|
358
|
+
kDynamoDB: "awsDynamoDbParams",
|
|
359
|
+
kAurora: "awsAuroraParams",
|
|
360
|
+
kRedshift: "awsRedshiftParams",
|
|
361
|
+
}[aws_workload_type ?? "kEC2"];
|
|
362
|
+
body.awsParams = {
|
|
363
|
+
protectionType: aws_workload_type ?? "kEC2",
|
|
364
|
+
[subkey]: { objects: vm_ids.map((id) => ({ id })) },
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
else if (environment === "kAzure" && vm_ids) {
|
|
368
|
+
body.azureParams = {
|
|
369
|
+
protectionType: "kAgent",
|
|
370
|
+
azureAgentParams: { objects: vm_ids.map((id) => ({ id })) },
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
else if (environment === "kGCP" && vm_ids) {
|
|
374
|
+
body.gcpParams = { objects: vm_ids.map((id) => ({ id })) };
|
|
375
|
+
}
|
|
376
|
+
else if (environment === "kS3Compatible" && vm_ids) {
|
|
377
|
+
body.s3CompatibleParams = { objects: vm_ids.map((id) => ({ id })) };
|
|
378
|
+
}
|
|
379
|
+
const result = await client.postV2("data-protect/protection-groups", body);
|
|
380
|
+
return toolResult(JSON.stringify(result, null, 2));
|
|
381
|
+
}
|
|
382
|
+
catch (error) {
|
|
383
|
+
return toolResult(`Error creating protection group: ${error}`, true);
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
// ── Update Protection Group (add/remove VMs) ─────────────────────────
|
|
387
|
+
server.tool("update_protection_group", "Update a Cohesity VMware protection group to add or remove VMs. Fetches the current group state, applies the changes, and saves. Use search_objects to find VM IDs first.", {
|
|
388
|
+
id: z.string().describe("Protection group ID to update"),
|
|
389
|
+
vm_ids_to_add: z
|
|
390
|
+
.array(z.number())
|
|
391
|
+
.optional()
|
|
392
|
+
.describe("VM object IDs to add to the protection group"),
|
|
393
|
+
vm_ids_to_remove: z
|
|
394
|
+
.array(z.number())
|
|
395
|
+
.optional()
|
|
396
|
+
.describe("VM object IDs to remove from the protection group"),
|
|
397
|
+
name: z.string().optional().describe("New name for the protection group"),
|
|
398
|
+
policy_id: z.string().optional().describe("New policy ID to assign"),
|
|
399
|
+
is_paused: z.boolean().optional().describe("Pause or resume the protection group"),
|
|
400
|
+
}, async ({ id, vm_ids_to_add, vm_ids_to_remove, name, policy_id, is_paused }) => {
|
|
401
|
+
try {
|
|
402
|
+
await client.refreshAllSources();
|
|
403
|
+
// Fetch current group state to preserve all existing fields
|
|
404
|
+
const current = await client.getV2(`data-protect/protection-groups/${id}`, { includeLastRunInfo: "false" });
|
|
405
|
+
const body = { ...current };
|
|
406
|
+
if (name)
|
|
407
|
+
body.name = name;
|
|
408
|
+
if (policy_id)
|
|
409
|
+
body.policyId = policy_id;
|
|
410
|
+
if (is_paused !== undefined)
|
|
411
|
+
body.isPaused = is_paused;
|
|
412
|
+
// Modify the VMware objects list
|
|
413
|
+
if (vm_ids_to_add?.length || vm_ids_to_remove?.length) {
|
|
414
|
+
const vmwareParams = (body.vmwareParams ?? {});
|
|
415
|
+
let objects = (vmwareParams.objects ?? []);
|
|
416
|
+
if (vm_ids_to_remove?.length) {
|
|
417
|
+
const removeSet = new Set(vm_ids_to_remove);
|
|
418
|
+
objects = objects.filter((o) => !removeSet.has(o.id));
|
|
419
|
+
}
|
|
420
|
+
if (vm_ids_to_add?.length) {
|
|
421
|
+
const existingIds = new Set(objects.map((o) => o.id));
|
|
422
|
+
for (const vmId of vm_ids_to_add) {
|
|
423
|
+
if (!existingIds.has(vmId))
|
|
424
|
+
objects.push({ id: vmId });
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
body.vmwareParams = { ...vmwareParams, objects };
|
|
428
|
+
}
|
|
429
|
+
const result = await client.putV2(`data-protect/protection-groups/${id}`, body);
|
|
430
|
+
return toolResult(JSON.stringify(result, null, 2));
|
|
431
|
+
}
|
|
432
|
+
catch (error) {
|
|
433
|
+
return toolResult(`Error updating protection group ${id}: ${error}`, true);
|
|
434
|
+
}
|
|
435
|
+
});
|
|
436
|
+
// ── Delete Protection Group ──────────────────────────────────────────
|
|
437
|
+
server.tool("delete_protection_group", "Delete a Cohesity protection group. Optionally delete all associated snapshots.", {
|
|
438
|
+
id: z.string().describe("Protection group ID to delete"),
|
|
439
|
+
delete_snapshots: z
|
|
440
|
+
.boolean()
|
|
441
|
+
.optional()
|
|
442
|
+
.default(false)
|
|
443
|
+
.describe("If true, also delete all snapshots associated with this group"),
|
|
444
|
+
}, async ({ id, delete_snapshots }) => {
|
|
445
|
+
try {
|
|
446
|
+
await client.refreshAllSources();
|
|
447
|
+
await client.deleteV2(`data-protect/protection-groups/${id}?deleteSnapshots=${delete_snapshots}`);
|
|
448
|
+
return toolResult(`Protection group ${id} deleted successfully.`);
|
|
449
|
+
}
|
|
450
|
+
catch (error) {
|
|
451
|
+
return toolResult(`Error deleting protection group ${id}: ${error}`, true);
|
|
452
|
+
}
|
|
453
|
+
});
|
|
454
|
+
// ── Pause Protection Group ───────────────────────────────────────────
|
|
455
|
+
server.tool("pause_protection_group", "Pause a Cohesity protection group so it stops running scheduled backups", {
|
|
456
|
+
id: z.string().describe("Protection group ID to pause"),
|
|
457
|
+
}, async ({ id }) => {
|
|
458
|
+
try {
|
|
459
|
+
await client.refreshAllSources();
|
|
460
|
+
const current = await client.getV2(`data-protect/protection-groups/${id}`, { includeLastRunInfo: "false" });
|
|
461
|
+
const result = await client.putV2(`data-protect/protection-groups/${id}`, { ...current, isPaused: true });
|
|
462
|
+
return toolResult(JSON.stringify(result, null, 2));
|
|
463
|
+
}
|
|
464
|
+
catch (error) {
|
|
465
|
+
return toolResult(`Error pausing protection group ${id}: ${error}`, true);
|
|
466
|
+
}
|
|
467
|
+
});
|
|
468
|
+
// ── Resume Protection Group ──────────────────────────────────────────
|
|
469
|
+
server.tool("resume_protection_group", "Resume a paused Cohesity protection group so it resumes scheduled backups", {
|
|
470
|
+
id: z.string().describe("Protection group ID to resume"),
|
|
471
|
+
}, async ({ id }) => {
|
|
472
|
+
try {
|
|
473
|
+
await client.refreshAllSources();
|
|
474
|
+
const current = await client.getV2(`data-protect/protection-groups/${id}`, { includeLastRunInfo: "false" });
|
|
475
|
+
const result = await client.putV2(`data-protect/protection-groups/${id}`, { ...current, isPaused: false });
|
|
476
|
+
return toolResult(JSON.stringify(result, null, 2));
|
|
477
|
+
}
|
|
478
|
+
catch (error) {
|
|
479
|
+
return toolResult(`Error resuming protection group ${id}: ${error}`, true);
|
|
480
|
+
}
|
|
481
|
+
});
|
|
482
|
+
// ── Run Protection Group ─────────────────────────────────────────────
|
|
483
|
+
server.tool("run_protection_group", "Trigger an on-demand backup run for a Cohesity protection group", {
|
|
484
|
+
id: z
|
|
485
|
+
.string()
|
|
486
|
+
.describe("Protection group ID to run"),
|
|
487
|
+
run_type: z
|
|
488
|
+
.enum(["kRegular", "kFull", "kLog", "kSystem"])
|
|
489
|
+
.optional()
|
|
490
|
+
.default("kRegular")
|
|
491
|
+
.describe("Type of backup run: kRegular (incremental), kFull, kLog (log backup), kSystem"),
|
|
492
|
+
objects: z
|
|
493
|
+
.array(z.object({
|
|
494
|
+
id: z.number().describe("Object ID to back up"),
|
|
495
|
+
}))
|
|
496
|
+
.optional()
|
|
497
|
+
.describe("Specific objects to back up. Omit to back up all objects in the group."),
|
|
498
|
+
}, async ({ id, run_type, objects }) => {
|
|
499
|
+
try {
|
|
500
|
+
await client.refreshAllSources();
|
|
501
|
+
const body = {
|
|
502
|
+
runType: run_type,
|
|
503
|
+
};
|
|
504
|
+
if (objects) {
|
|
505
|
+
body.objects = objects;
|
|
506
|
+
}
|
|
507
|
+
const result = await client.postV2(`data-protect/protection-groups/${id}/runs`, body);
|
|
508
|
+
return toolResult(`Protection group run initiated successfully.\n${JSON.stringify(result, null, 2)}`);
|
|
509
|
+
}
|
|
510
|
+
catch (error) {
|
|
511
|
+
return toolResult(`Error running protection group ${id}: ${error}`, true);
|
|
512
|
+
}
|
|
513
|
+
});
|
|
514
|
+
}
|