@samik081/mcp-pve 0.1.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 +21 -0
- package/README.md +368 -0
- package/dist/core/client.d.ts +43 -0
- package/dist/core/client.js +143 -0
- package/dist/core/config.d.ts +15 -0
- package/dist/core/config.js +68 -0
- package/dist/core/errors.d.ts +24 -0
- package/dist/core/errors.js +43 -0
- package/dist/core/logger.d.ts +15 -0
- package/dist/core/logger.js +26 -0
- package/dist/core/server.d.ts +15 -0
- package/dist/core/server.js +30 -0
- package/dist/core/tools.d.ts +29 -0
- package/dist/core/tools.js +64 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +51 -0
- package/dist/tools/access.d.ts +7 -0
- package/dist/tools/access.js +234 -0
- package/dist/tools/backup.d.ts +7 -0
- package/dist/tools/backup.js +176 -0
- package/dist/tools/cluster.d.ts +7 -0
- package/dist/tools/cluster.js +150 -0
- package/dist/tools/firewall.d.ts +7 -0
- package/dist/tools/firewall.js +215 -0
- package/dist/tools/ha.d.ts +9 -0
- package/dist/tools/ha.js +138 -0
- package/dist/tools/index.d.ts +10 -0
- package/dist/tools/index.js +32 -0
- package/dist/tools/lxc.d.ts +7 -0
- package/dist/tools/lxc.js +371 -0
- package/dist/tools/network.d.ts +7 -0
- package/dist/tools/network.js +157 -0
- package/dist/tools/nodes.d.ts +7 -0
- package/dist/tools/nodes.js +131 -0
- package/dist/tools/pools.d.ts +7 -0
- package/dist/tools/pools.js +98 -0
- package/dist/tools/qemu.d.ts +7 -0
- package/dist/tools/qemu.js +394 -0
- package/dist/tools/storage.d.ts +7 -0
- package/dist/tools/storage.js +216 -0
- package/dist/tools/tasks.d.ts +7 -0
- package/dist/tools/tasks.js +106 -0
- package/dist/types/index.d.ts +25 -0
- package/dist/types/index.js +17 -0
- package/package.json +55 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Backup tools: backup jobs, manual backup execution, and job management.
|
|
3
|
+
*/
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { registerTool } from "../core/tools.js";
|
|
6
|
+
export function registerBackupTools(server, client, config) {
|
|
7
|
+
// --- Read-only tools ---
|
|
8
|
+
registerTool(server, config, {
|
|
9
|
+
name: "pve_list_backup_jobs",
|
|
10
|
+
description: "List all scheduled backup jobs in the cluster",
|
|
11
|
+
category: "backup",
|
|
12
|
+
accessTier: "read-only",
|
|
13
|
+
handler: async () => {
|
|
14
|
+
const data = await client.get("/cluster/backup");
|
|
15
|
+
return JSON.stringify(data, null, 2);
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
registerTool(server, config, {
|
|
19
|
+
name: "pve_get_backup_job",
|
|
20
|
+
description: "Get the configuration of a specific backup job",
|
|
21
|
+
category: "backup",
|
|
22
|
+
accessTier: "read-only",
|
|
23
|
+
inputSchema: {
|
|
24
|
+
id: z.string().describe("The backup job ID"),
|
|
25
|
+
},
|
|
26
|
+
handler: async (args) => {
|
|
27
|
+
const data = await client.get(`/cluster/backup/${args.id}`);
|
|
28
|
+
return JSON.stringify(data, null, 2);
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
// --- Read-execute tools ---
|
|
32
|
+
registerTool(server, config, {
|
|
33
|
+
name: "pve_run_backup",
|
|
34
|
+
description: "Run an immediate backup (vzdump) of one or more VMs/containers on a node",
|
|
35
|
+
category: "backup",
|
|
36
|
+
accessTier: "read-execute",
|
|
37
|
+
inputSchema: {
|
|
38
|
+
node: z.string().describe("The node name"),
|
|
39
|
+
vmid: z
|
|
40
|
+
.string()
|
|
41
|
+
.optional()
|
|
42
|
+
.describe("Comma-separated list of VMIDs to back up (omit for all)"),
|
|
43
|
+
storage: z
|
|
44
|
+
.string()
|
|
45
|
+
.optional()
|
|
46
|
+
.describe("Target storage for the backup"),
|
|
47
|
+
mode: z
|
|
48
|
+
.enum(["snapshot", "suspend", "stop"])
|
|
49
|
+
.optional()
|
|
50
|
+
.describe("Backup mode (default: snapshot)"),
|
|
51
|
+
compress: z
|
|
52
|
+
.enum(["0", "gzip", "lzo", "zstd"])
|
|
53
|
+
.optional()
|
|
54
|
+
.describe("Compression algorithm"),
|
|
55
|
+
mailnotification: z
|
|
56
|
+
.enum(["always", "failure"])
|
|
57
|
+
.optional()
|
|
58
|
+
.describe("When to send email notification"),
|
|
59
|
+
mailto: z
|
|
60
|
+
.string()
|
|
61
|
+
.optional()
|
|
62
|
+
.describe("Email address for backup notifications"),
|
|
63
|
+
},
|
|
64
|
+
handler: async (args) => {
|
|
65
|
+
const body = {};
|
|
66
|
+
if (args.vmid !== undefined)
|
|
67
|
+
body.vmid = args.vmid;
|
|
68
|
+
if (args.storage !== undefined)
|
|
69
|
+
body.storage = args.storage;
|
|
70
|
+
if (args.mode !== undefined)
|
|
71
|
+
body.mode = args.mode;
|
|
72
|
+
if (args.compress !== undefined)
|
|
73
|
+
body.compress = args.compress;
|
|
74
|
+
if (args.mailnotification !== undefined)
|
|
75
|
+
body.mailnotification = args.mailnotification;
|
|
76
|
+
if (args.mailto !== undefined)
|
|
77
|
+
body.mailto = args.mailto;
|
|
78
|
+
const data = await client.post(`/nodes/${args.node}/vzdump`, body);
|
|
79
|
+
return `Backup initiated on node ${args.node}. Task: ${data}`;
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
// --- Full access tools ---
|
|
83
|
+
registerTool(server, config, {
|
|
84
|
+
name: "pve_create_backup_job",
|
|
85
|
+
description: "Create a new scheduled backup job",
|
|
86
|
+
category: "backup",
|
|
87
|
+
accessTier: "full",
|
|
88
|
+
inputSchema: {
|
|
89
|
+
vmid: z
|
|
90
|
+
.string()
|
|
91
|
+
.optional()
|
|
92
|
+
.describe("Comma-separated list of VMIDs to include (omit for all)"),
|
|
93
|
+
storage: z
|
|
94
|
+
.string()
|
|
95
|
+
.optional()
|
|
96
|
+
.describe("Target storage for backups"),
|
|
97
|
+
schedule: z
|
|
98
|
+
.string()
|
|
99
|
+
.optional()
|
|
100
|
+
.describe("Backup schedule in cron-like format or PVE calendar event"),
|
|
101
|
+
mode: z
|
|
102
|
+
.enum(["snapshot", "suspend", "stop"])
|
|
103
|
+
.optional()
|
|
104
|
+
.describe("Backup mode (default: snapshot)"),
|
|
105
|
+
compress: z
|
|
106
|
+
.enum(["0", "gzip", "lzo", "zstd"])
|
|
107
|
+
.optional()
|
|
108
|
+
.describe("Compression algorithm"),
|
|
109
|
+
mailnotification: z
|
|
110
|
+
.enum(["always", "failure"])
|
|
111
|
+
.optional()
|
|
112
|
+
.describe("When to send email notification"),
|
|
113
|
+
mailto: z
|
|
114
|
+
.string()
|
|
115
|
+
.optional()
|
|
116
|
+
.describe("Email address for backup notifications"),
|
|
117
|
+
enabled: z
|
|
118
|
+
.boolean()
|
|
119
|
+
.optional()
|
|
120
|
+
.describe("Enable the backup job (default: true)"),
|
|
121
|
+
node: z
|
|
122
|
+
.string()
|
|
123
|
+
.optional()
|
|
124
|
+
.describe("Restrict to specific node"),
|
|
125
|
+
pool: z
|
|
126
|
+
.string()
|
|
127
|
+
.optional()
|
|
128
|
+
.describe("Backup all VMs in this pool"),
|
|
129
|
+
maxfiles: z
|
|
130
|
+
.number()
|
|
131
|
+
.optional()
|
|
132
|
+
.describe("Maximum number of backup files per VM (0 = unlimited)"),
|
|
133
|
+
},
|
|
134
|
+
handler: async (args) => {
|
|
135
|
+
const body = {};
|
|
136
|
+
if (args.vmid !== undefined)
|
|
137
|
+
body.vmid = args.vmid;
|
|
138
|
+
if (args.storage !== undefined)
|
|
139
|
+
body.storage = args.storage;
|
|
140
|
+
if (args.schedule !== undefined)
|
|
141
|
+
body.schedule = args.schedule;
|
|
142
|
+
if (args.mode !== undefined)
|
|
143
|
+
body.mode = args.mode;
|
|
144
|
+
if (args.compress !== undefined)
|
|
145
|
+
body.compress = args.compress;
|
|
146
|
+
if (args.mailnotification !== undefined)
|
|
147
|
+
body.mailnotification = args.mailnotification;
|
|
148
|
+
if (args.mailto !== undefined)
|
|
149
|
+
body.mailto = args.mailto;
|
|
150
|
+
if (args.enabled !== undefined)
|
|
151
|
+
body.enabled = args.enabled ? 1 : 0;
|
|
152
|
+
if (args.node !== undefined)
|
|
153
|
+
body.node = args.node;
|
|
154
|
+
if (args.pool !== undefined)
|
|
155
|
+
body.pool = args.pool;
|
|
156
|
+
if (args.maxfiles !== undefined)
|
|
157
|
+
body.maxfiles = args.maxfiles;
|
|
158
|
+
await client.post("/cluster/backup", body);
|
|
159
|
+
return "Backup job created successfully.";
|
|
160
|
+
},
|
|
161
|
+
});
|
|
162
|
+
registerTool(server, config, {
|
|
163
|
+
name: "pve_delete_backup_job",
|
|
164
|
+
description: "Delete a scheduled backup job",
|
|
165
|
+
category: "backup",
|
|
166
|
+
accessTier: "full",
|
|
167
|
+
annotations: { destructiveHint: true },
|
|
168
|
+
inputSchema: {
|
|
169
|
+
id: z.string().describe("The backup job ID to delete"),
|
|
170
|
+
},
|
|
171
|
+
handler: async (args) => {
|
|
172
|
+
await client.delete(`/cluster/backup/${args.id}`);
|
|
173
|
+
return `Backup job '${args.id}' deleted successfully.`;
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cluster tools: status, resources, options, and cluster-wide information.
|
|
3
|
+
*/
|
|
4
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
|
+
import type { PveClient } from "../core/client.js";
|
|
6
|
+
import type { AppConfig } from "../types/index.js";
|
|
7
|
+
export declare function registerClusterTools(server: McpServer, client: PveClient, config: AppConfig): void;
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cluster tools: status, resources, options, and cluster-wide information.
|
|
3
|
+
*/
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { registerTool } from "../core/tools.js";
|
|
6
|
+
export function registerClusterTools(server, client, config) {
|
|
7
|
+
// --- Read-only tools ---
|
|
8
|
+
registerTool(server, config, {
|
|
9
|
+
name: "pve_get_cluster_status",
|
|
10
|
+
description: "Get the current cluster status including node membership and quorum",
|
|
11
|
+
category: "cluster",
|
|
12
|
+
accessTier: "read-only",
|
|
13
|
+
handler: async () => {
|
|
14
|
+
const data = await client.get("/cluster/status");
|
|
15
|
+
return JSON.stringify(data, null, 2);
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
registerTool(server, config, {
|
|
19
|
+
name: "pve_list_cluster_resources",
|
|
20
|
+
description: "List all cluster resources (VMs, containers, storage, nodes) with optional type filter",
|
|
21
|
+
category: "cluster",
|
|
22
|
+
accessTier: "read-only",
|
|
23
|
+
inputSchema: {
|
|
24
|
+
type: z
|
|
25
|
+
.enum(["vm", "storage", "node", "sdn"])
|
|
26
|
+
.optional()
|
|
27
|
+
.describe("Filter by resource type"),
|
|
28
|
+
},
|
|
29
|
+
handler: async (args) => {
|
|
30
|
+
let path = "/cluster/resources";
|
|
31
|
+
if (args.type)
|
|
32
|
+
path += `?type=${args.type}`;
|
|
33
|
+
const data = await client.get(path);
|
|
34
|
+
return JSON.stringify(data, null, 2);
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
registerTool(server, config, {
|
|
38
|
+
name: "pve_get_next_vmid",
|
|
39
|
+
description: "Get the next available VMID in the cluster",
|
|
40
|
+
category: "cluster",
|
|
41
|
+
accessTier: "read-only",
|
|
42
|
+
handler: async () => {
|
|
43
|
+
const data = await client.get("/cluster/nextid");
|
|
44
|
+
return `Next available VMID: ${data}`;
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
registerTool(server, config, {
|
|
48
|
+
name: "pve_get_cluster_log",
|
|
49
|
+
description: "Get recent cluster log entries",
|
|
50
|
+
category: "cluster",
|
|
51
|
+
accessTier: "read-only",
|
|
52
|
+
inputSchema: {
|
|
53
|
+
max: z
|
|
54
|
+
.number()
|
|
55
|
+
.optional()
|
|
56
|
+
.describe("Maximum number of log entries to return"),
|
|
57
|
+
},
|
|
58
|
+
handler: async (args) => {
|
|
59
|
+
let path = "/cluster/log";
|
|
60
|
+
if (args.max !== undefined)
|
|
61
|
+
path += `?max=${args.max}`;
|
|
62
|
+
const data = await client.get(path);
|
|
63
|
+
return JSON.stringify(data, null, 2);
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
registerTool(server, config, {
|
|
67
|
+
name: "pve_get_cluster_options",
|
|
68
|
+
description: "Get cluster-wide datacenter options",
|
|
69
|
+
category: "cluster",
|
|
70
|
+
accessTier: "read-only",
|
|
71
|
+
handler: async () => {
|
|
72
|
+
const data = await client.get("/cluster/options");
|
|
73
|
+
return JSON.stringify(data, null, 2);
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
registerTool(server, config, {
|
|
77
|
+
name: "pve_list_cluster_backup_info",
|
|
78
|
+
description: "List guests that are not covered by any backup job",
|
|
79
|
+
category: "cluster",
|
|
80
|
+
accessTier: "read-only",
|
|
81
|
+
handler: async () => {
|
|
82
|
+
const data = await client.get("/cluster/backup-info/not-backed-up");
|
|
83
|
+
return JSON.stringify(data, null, 2);
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
registerTool(server, config, {
|
|
87
|
+
name: "pve_get_cluster_ha_status",
|
|
88
|
+
description: "Get the current HA manager status",
|
|
89
|
+
category: "cluster",
|
|
90
|
+
accessTier: "read-only",
|
|
91
|
+
handler: async () => {
|
|
92
|
+
const data = await client.get("/cluster/ha/status/current");
|
|
93
|
+
return JSON.stringify(data, null, 2);
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
registerTool(server, config, {
|
|
97
|
+
name: "pve_list_cluster_replication",
|
|
98
|
+
description: "List all replication jobs in the cluster",
|
|
99
|
+
category: "cluster",
|
|
100
|
+
accessTier: "read-only",
|
|
101
|
+
handler: async () => {
|
|
102
|
+
const data = await client.get("/cluster/replication");
|
|
103
|
+
return JSON.stringify(data, null, 2);
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
// --- Full access tools ---
|
|
107
|
+
registerTool(server, config, {
|
|
108
|
+
name: "pve_update_cluster_options",
|
|
109
|
+
description: "Update cluster-wide datacenter options",
|
|
110
|
+
category: "cluster",
|
|
111
|
+
accessTier: "full",
|
|
112
|
+
inputSchema: {
|
|
113
|
+
keyboard: z
|
|
114
|
+
.string()
|
|
115
|
+
.optional()
|
|
116
|
+
.describe("Default keyboard layout for VNC"),
|
|
117
|
+
language: z
|
|
118
|
+
.string()
|
|
119
|
+
.optional()
|
|
120
|
+
.describe("Default GUI language"),
|
|
121
|
+
console: z
|
|
122
|
+
.enum(["applet", "vv", "html5", "xtermjs"])
|
|
123
|
+
.optional()
|
|
124
|
+
.describe("Default console viewer"),
|
|
125
|
+
http_proxy: z
|
|
126
|
+
.string()
|
|
127
|
+
.optional()
|
|
128
|
+
.describe("HTTP proxy configuration"),
|
|
129
|
+
migration_unsecure: z
|
|
130
|
+
.boolean()
|
|
131
|
+
.optional()
|
|
132
|
+
.describe("Allow insecure migration"),
|
|
133
|
+
},
|
|
134
|
+
handler: async (args) => {
|
|
135
|
+
const body = {};
|
|
136
|
+
if (args.keyboard !== undefined)
|
|
137
|
+
body.keyboard = args.keyboard;
|
|
138
|
+
if (args.language !== undefined)
|
|
139
|
+
body.language = args.language;
|
|
140
|
+
if (args.console !== undefined)
|
|
141
|
+
body.console = args.console;
|
|
142
|
+
if (args.http_proxy !== undefined)
|
|
143
|
+
body.http_proxy = args.http_proxy;
|
|
144
|
+
if (args.migration_unsecure !== undefined)
|
|
145
|
+
body["migration_unsecure"] = args.migration_unsecure ? 1 : 0;
|
|
146
|
+
await client.put("/cluster/options", body);
|
|
147
|
+
return "Cluster options updated successfully.";
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Firewall tools: cluster-level firewall options, rules, aliases, and IP sets.
|
|
3
|
+
*/
|
|
4
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
|
+
import type { PveClient } from "../core/client.js";
|
|
6
|
+
import type { AppConfig } from "../types/index.js";
|
|
7
|
+
export declare function registerFirewallTools(server: McpServer, client: PveClient, config: AppConfig): void;
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Firewall tools: cluster-level firewall options, rules, aliases, and IP sets.
|
|
3
|
+
*/
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { registerTool } from "../core/tools.js";
|
|
6
|
+
export function registerFirewallTools(server, client, config) {
|
|
7
|
+
// --- Read-only tools ---
|
|
8
|
+
registerTool(server, config, {
|
|
9
|
+
name: "pve_get_firewall_options",
|
|
10
|
+
description: "Get the cluster-level firewall options",
|
|
11
|
+
category: "firewall",
|
|
12
|
+
accessTier: "read-only",
|
|
13
|
+
handler: async () => {
|
|
14
|
+
const data = await client.get("/cluster/firewall/options");
|
|
15
|
+
return JSON.stringify(data, null, 2);
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
registerTool(server, config, {
|
|
19
|
+
name: "pve_list_firewall_rules",
|
|
20
|
+
description: "List all cluster-level firewall rules",
|
|
21
|
+
category: "firewall",
|
|
22
|
+
accessTier: "read-only",
|
|
23
|
+
handler: async () => {
|
|
24
|
+
const data = await client.get("/cluster/firewall/rules");
|
|
25
|
+
return JSON.stringify(data, null, 2);
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
registerTool(server, config, {
|
|
29
|
+
name: "pve_list_firewall_aliases",
|
|
30
|
+
description: "List all cluster-level firewall aliases (named IP/CIDR entries)",
|
|
31
|
+
category: "firewall",
|
|
32
|
+
accessTier: "read-only",
|
|
33
|
+
handler: async () => {
|
|
34
|
+
const data = await client.get("/cluster/firewall/aliases");
|
|
35
|
+
return JSON.stringify(data, null, 2);
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
registerTool(server, config, {
|
|
39
|
+
name: "pve_list_firewall_ipsets",
|
|
40
|
+
description: "List all cluster-level firewall IP sets",
|
|
41
|
+
category: "firewall",
|
|
42
|
+
accessTier: "read-only",
|
|
43
|
+
handler: async () => {
|
|
44
|
+
const data = await client.get("/cluster/firewall/ipset");
|
|
45
|
+
return JSON.stringify(data, null, 2);
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
// --- Full access tools ---
|
|
49
|
+
registerTool(server, config, {
|
|
50
|
+
name: "pve_update_firewall_options",
|
|
51
|
+
description: "Update the cluster-level firewall options (e.g. enable/disable firewall)",
|
|
52
|
+
category: "firewall",
|
|
53
|
+
accessTier: "full",
|
|
54
|
+
inputSchema: {
|
|
55
|
+
enable: z
|
|
56
|
+
.boolean()
|
|
57
|
+
.optional()
|
|
58
|
+
.describe("Enable or disable the cluster firewall"),
|
|
59
|
+
policy_in: z
|
|
60
|
+
.enum(["ACCEPT", "REJECT", "DROP"])
|
|
61
|
+
.optional()
|
|
62
|
+
.describe("Default input policy"),
|
|
63
|
+
policy_out: z
|
|
64
|
+
.enum(["ACCEPT", "REJECT", "DROP"])
|
|
65
|
+
.optional()
|
|
66
|
+
.describe("Default output policy"),
|
|
67
|
+
log_ratelimit: z
|
|
68
|
+
.string()
|
|
69
|
+
.optional()
|
|
70
|
+
.describe("Log rate limit (e.g. 'enable=1,rate=1/second,burst=5')"),
|
|
71
|
+
},
|
|
72
|
+
handler: async (args) => {
|
|
73
|
+
const body = {};
|
|
74
|
+
if (args.enable !== undefined)
|
|
75
|
+
body.enable = args.enable ? 1 : 0;
|
|
76
|
+
if (args.policy_in !== undefined)
|
|
77
|
+
body.policy_in = args.policy_in;
|
|
78
|
+
if (args.policy_out !== undefined)
|
|
79
|
+
body.policy_out = args.policy_out;
|
|
80
|
+
if (args.log_ratelimit !== undefined)
|
|
81
|
+
body.log_ratelimit = args.log_ratelimit;
|
|
82
|
+
await client.put("/cluster/firewall/options", body);
|
|
83
|
+
return "Cluster firewall options updated successfully.";
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
registerTool(server, config, {
|
|
87
|
+
name: "pve_create_firewall_rule",
|
|
88
|
+
description: "Create a new cluster-level firewall rule",
|
|
89
|
+
category: "firewall",
|
|
90
|
+
accessTier: "full",
|
|
91
|
+
inputSchema: {
|
|
92
|
+
action: z
|
|
93
|
+
.enum(["ACCEPT", "DROP", "REJECT"])
|
|
94
|
+
.describe("Rule action"),
|
|
95
|
+
type: z.enum(["in", "out", "group"]).describe("Rule type (direction)"),
|
|
96
|
+
enable: z
|
|
97
|
+
.boolean()
|
|
98
|
+
.optional()
|
|
99
|
+
.describe("Enable the rule (default: true)"),
|
|
100
|
+
source: z
|
|
101
|
+
.string()
|
|
102
|
+
.optional()
|
|
103
|
+
.describe("Source address/CIDR or alias"),
|
|
104
|
+
dest: z
|
|
105
|
+
.string()
|
|
106
|
+
.optional()
|
|
107
|
+
.describe("Destination address/CIDR or alias"),
|
|
108
|
+
proto: z
|
|
109
|
+
.string()
|
|
110
|
+
.optional()
|
|
111
|
+
.describe("Protocol (e.g. tcp, udp, icmp)"),
|
|
112
|
+
dport: z
|
|
113
|
+
.string()
|
|
114
|
+
.optional()
|
|
115
|
+
.describe("Destination port or port range"),
|
|
116
|
+
sport: z
|
|
117
|
+
.string()
|
|
118
|
+
.optional()
|
|
119
|
+
.describe("Source port or port range"),
|
|
120
|
+
comment: z.string().optional().describe("Rule comment"),
|
|
121
|
+
pos: z
|
|
122
|
+
.number()
|
|
123
|
+
.optional()
|
|
124
|
+
.describe("Position in the rule list (0-based)"),
|
|
125
|
+
},
|
|
126
|
+
handler: async (args) => {
|
|
127
|
+
const body = {
|
|
128
|
+
action: args.action,
|
|
129
|
+
type: args.type,
|
|
130
|
+
};
|
|
131
|
+
if (args.enable !== undefined)
|
|
132
|
+
body.enable = args.enable ? 1 : 0;
|
|
133
|
+
if (args.source !== undefined)
|
|
134
|
+
body.source = args.source;
|
|
135
|
+
if (args.dest !== undefined)
|
|
136
|
+
body.dest = args.dest;
|
|
137
|
+
if (args.proto !== undefined)
|
|
138
|
+
body.proto = args.proto;
|
|
139
|
+
if (args.dport !== undefined)
|
|
140
|
+
body.dport = args.dport;
|
|
141
|
+
if (args.sport !== undefined)
|
|
142
|
+
body.sport = args.sport;
|
|
143
|
+
if (args.comment !== undefined)
|
|
144
|
+
body.comment = args.comment;
|
|
145
|
+
if (args.pos !== undefined)
|
|
146
|
+
body.pos = args.pos;
|
|
147
|
+
await client.post("/cluster/firewall/rules", body);
|
|
148
|
+
return "Cluster firewall rule created successfully.";
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
registerTool(server, config, {
|
|
152
|
+
name: "pve_update_firewall_rule",
|
|
153
|
+
description: "Update an existing cluster-level firewall rule by position",
|
|
154
|
+
category: "firewall",
|
|
155
|
+
accessTier: "full",
|
|
156
|
+
inputSchema: {
|
|
157
|
+
pos: z.number().describe("Rule position (0-based index)"),
|
|
158
|
+
action: z
|
|
159
|
+
.enum(["ACCEPT", "DROP", "REJECT"])
|
|
160
|
+
.optional()
|
|
161
|
+
.describe("Rule action"),
|
|
162
|
+
enable: z
|
|
163
|
+
.boolean()
|
|
164
|
+
.optional()
|
|
165
|
+
.describe("Enable or disable the rule"),
|
|
166
|
+
source: z.string().optional().describe("Source address/CIDR or alias"),
|
|
167
|
+
dest: z
|
|
168
|
+
.string()
|
|
169
|
+
.optional()
|
|
170
|
+
.describe("Destination address/CIDR or alias"),
|
|
171
|
+
proto: z.string().optional().describe("Protocol"),
|
|
172
|
+
dport: z
|
|
173
|
+
.string()
|
|
174
|
+
.optional()
|
|
175
|
+
.describe("Destination port or port range"),
|
|
176
|
+
sport: z.string().optional().describe("Source port or port range"),
|
|
177
|
+
comment: z.string().optional().describe("Rule comment"),
|
|
178
|
+
},
|
|
179
|
+
handler: async (args) => {
|
|
180
|
+
const body = {};
|
|
181
|
+
if (args.action !== undefined)
|
|
182
|
+
body.action = args.action;
|
|
183
|
+
if (args.enable !== undefined)
|
|
184
|
+
body.enable = args.enable ? 1 : 0;
|
|
185
|
+
if (args.source !== undefined)
|
|
186
|
+
body.source = args.source;
|
|
187
|
+
if (args.dest !== undefined)
|
|
188
|
+
body.dest = args.dest;
|
|
189
|
+
if (args.proto !== undefined)
|
|
190
|
+
body.proto = args.proto;
|
|
191
|
+
if (args.dport !== undefined)
|
|
192
|
+
body.dport = args.dport;
|
|
193
|
+
if (args.sport !== undefined)
|
|
194
|
+
body.sport = args.sport;
|
|
195
|
+
if (args.comment !== undefined)
|
|
196
|
+
body.comment = args.comment;
|
|
197
|
+
await client.put(`/cluster/firewall/rules/${args.pos}`, body);
|
|
198
|
+
return `Cluster firewall rule at position ${args.pos} updated successfully.`;
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
registerTool(server, config, {
|
|
202
|
+
name: "pve_delete_firewall_rule",
|
|
203
|
+
description: "Delete a cluster-level firewall rule by position",
|
|
204
|
+
category: "firewall",
|
|
205
|
+
accessTier: "full",
|
|
206
|
+
annotations: { destructiveHint: true },
|
|
207
|
+
inputSchema: {
|
|
208
|
+
pos: z.number().describe("Rule position (0-based index) to delete"),
|
|
209
|
+
},
|
|
210
|
+
handler: async (args) => {
|
|
211
|
+
await client.delete(`/cluster/firewall/rules/${args.pos}`);
|
|
212
|
+
return `Cluster firewall rule at position ${args.pos} deleted successfully.`;
|
|
213
|
+
},
|
|
214
|
+
});
|
|
215
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HA (High Availability) tools: CRUD for HA-managed resources.
|
|
3
|
+
*
|
|
4
|
+
* SID format: type:vmid (e.g. vm:100, ct:200)
|
|
5
|
+
*/
|
|
6
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
7
|
+
import type { PveClient } from "../core/client.js";
|
|
8
|
+
import type { AppConfig } from "../types/index.js";
|
|
9
|
+
export declare function registerHaTools(server: McpServer, client: PveClient, config: AppConfig): void;
|