mcp-server-kubernetes 1.6.2 → 2.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/README.md +46 -22
- package/dist/config/container-templates.d.ts +2 -2
- package/dist/index.d.ts +314 -736
- package/dist/index.js +93 -200
- package/dist/models/resource-models.d.ts +6 -6
- package/dist/tools/kubectl-apply.d.ts +46 -0
- package/dist/tools/kubectl-apply.js +110 -0
- package/dist/tools/kubectl-context.d.ts +49 -0
- package/dist/tools/kubectl-context.js +233 -0
- package/dist/tools/kubectl-create.d.ts +150 -0
- package/dist/tools/kubectl-create.js +321 -0
- package/dist/tools/kubectl-delete.d.ts +77 -0
- package/dist/tools/kubectl-delete.js +177 -0
- package/dist/tools/kubectl-describe.d.ts +47 -0
- package/dist/tools/kubectl-describe.js +96 -0
- package/dist/tools/kubectl-generic.d.ts +71 -0
- package/dist/tools/kubectl-generic.js +121 -0
- package/dist/tools/kubectl-get.d.ts +72 -0
- package/dist/tools/kubectl-get.js +251 -0
- package/dist/tools/kubectl-list.d.ts +61 -0
- package/dist/tools/kubectl-list.js +189 -0
- package/dist/tools/{get_logs.d.ts → kubectl-logs.d.ts} +35 -19
- package/dist/tools/kubectl-logs.js +312 -0
- package/dist/tools/kubectl-patch.d.ts +57 -0
- package/dist/tools/kubectl-patch.js +128 -0
- package/dist/tools/kubectl-rollout.d.ts +67 -0
- package/dist/tools/kubectl-rollout.js +115 -0
- package/dist/tools/{scale_deployment.d.ts → kubectl-scale.d.ts} +13 -3
- package/dist/tools/kubectl-scale.js +73 -0
- package/dist/utils/kubernetes-manager.d.ts +4 -0
- package/dist/utils/kubernetes-manager.js +21 -3
- package/package.json +1 -1
- package/dist/tools/create_configmap.d.ts +0 -33
- package/dist/tools/create_configmap.js +0 -66
- package/dist/tools/create_cronjob.d.ts +0 -47
- package/dist/tools/create_cronjob.js +0 -93
- package/dist/tools/create_deployment.d.ts +0 -135
- package/dist/tools/create_deployment.js +0 -162
- package/dist/tools/create_namespace.d.ts +0 -22
- package/dist/tools/create_namespace.js +0 -48
- package/dist/tools/create_pod.d.ts +0 -130
- package/dist/tools/create_pod.js +0 -153
- package/dist/tools/create_service.d.ts +0 -74
- package/dist/tools/create_service.js +0 -102
- package/dist/tools/delete_configmap.d.ts +0 -26
- package/dist/tools/delete_configmap.js +0 -49
- package/dist/tools/delete_cronjob.d.ts +0 -26
- package/dist/tools/delete_cronjob.js +0 -48
- package/dist/tools/delete_deployment.d.ts +0 -31
- package/dist/tools/delete_deployment.js +0 -47
- package/dist/tools/delete_namespace.d.ts +0 -27
- package/dist/tools/delete_namespace.js +0 -44
- package/dist/tools/delete_pod.d.ts +0 -31
- package/dist/tools/delete_pod.js +0 -45
- package/dist/tools/delete_service.d.ts +0 -32
- package/dist/tools/delete_service.js +0 -46
- package/dist/tools/describe_cronjob.d.ts +0 -27
- package/dist/tools/describe_cronjob.js +0 -83
- package/dist/tools/describe_deployment.d.ts +0 -26
- package/dist/tools/describe_deployment.js +0 -40
- package/dist/tools/describe_node.d.ts +0 -22
- package/dist/tools/describe_node.js +0 -84
- package/dist/tools/describe_pod.d.ts +0 -33
- package/dist/tools/describe_pod.js +0 -81
- package/dist/tools/describe_service.d.ts +0 -34
- package/dist/tools/describe_service.js +0 -85
- package/dist/tools/get_configmap.d.ts +0 -27
- package/dist/tools/get_configmap.js +0 -48
- package/dist/tools/get_current_context.d.ts +0 -23
- package/dist/tools/get_current_context.js +0 -55
- package/dist/tools/get_events.d.ts +0 -28
- package/dist/tools/get_events.js +0 -66
- package/dist/tools/get_job_logs.d.ts +0 -40
- package/dist/tools/get_job_logs.js +0 -104
- package/dist/tools/get_logs.js +0 -150
- package/dist/tools/list_contexts.d.ts +0 -23
- package/dist/tools/list_contexts.js +0 -39
- package/dist/tools/list_cronjobs.d.ts +0 -23
- package/dist/tools/list_cronjobs.js +0 -35
- package/dist/tools/list_deployments.d.ts +0 -23
- package/dist/tools/list_deployments.js +0 -30
- package/dist/tools/list_jobs.d.ts +0 -29
- package/dist/tools/list_jobs.js +0 -77
- package/dist/tools/list_nodes.d.ts +0 -15
- package/dist/tools/list_nodes.js +0 -21
- package/dist/tools/list_pods.d.ts +0 -23
- package/dist/tools/list_pods.js +0 -29
- package/dist/tools/list_services.d.ts +0 -23
- package/dist/tools/list_services.js +0 -31
- package/dist/tools/scale_deployment.js +0 -50
- package/dist/tools/set_current_context.d.ts +0 -23
- package/dist/tools/set_current_context.js +0 -35
- package/dist/tools/update_configmap.d.ts +0 -33
- package/dist/tools/update_configmap.js +0 -71
- package/dist/tools/update_deployment.d.ts +0 -113
- package/dist/tools/update_deployment.js +0 -155
- package/dist/tools/update_service.d.ts +0 -72
- package/dist/tools/update_service.js +0 -125
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
import { execSync } from "child_process";
|
|
2
|
+
import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
|
|
3
|
+
import * as fs from "fs";
|
|
4
|
+
import * as path from "path";
|
|
5
|
+
import * as os from "os";
|
|
6
|
+
export const kubectlCreateSchema = {
|
|
7
|
+
name: "kubectl_create",
|
|
8
|
+
description: "Create Kubernetes resources using various methods (from file or using subcommands)",
|
|
9
|
+
inputSchema: {
|
|
10
|
+
type: "object",
|
|
11
|
+
properties: {
|
|
12
|
+
// General options
|
|
13
|
+
dryRun: {
|
|
14
|
+
type: "boolean",
|
|
15
|
+
description: "If true, only validate the resource, don't actually create it",
|
|
16
|
+
default: false
|
|
17
|
+
},
|
|
18
|
+
output: {
|
|
19
|
+
type: "string",
|
|
20
|
+
enum: ["json", "yaml", "name", "go-template", "go-template-file", "template", "templatefile", "jsonpath", "jsonpath-as-json", "jsonpath-file"],
|
|
21
|
+
description: "Output format. One of: json|yaml|name|go-template|go-template-file|template|templatefile|jsonpath|jsonpath-as-json|jsonpath-file",
|
|
22
|
+
default: "yaml"
|
|
23
|
+
},
|
|
24
|
+
validate: {
|
|
25
|
+
type: "boolean",
|
|
26
|
+
description: "If true, validate resource schema against server schema",
|
|
27
|
+
default: true
|
|
28
|
+
},
|
|
29
|
+
// Create from file method
|
|
30
|
+
manifest: {
|
|
31
|
+
type: "string",
|
|
32
|
+
description: "YAML manifest to create resources from"
|
|
33
|
+
},
|
|
34
|
+
filename: {
|
|
35
|
+
type: "string",
|
|
36
|
+
description: "Path to a YAML file to create resources from"
|
|
37
|
+
},
|
|
38
|
+
// Resource type to create (determines which subcommand to use)
|
|
39
|
+
resourceType: {
|
|
40
|
+
type: "string",
|
|
41
|
+
description: "Type of resource to create (namespace, configmap, deployment, service, etc.)"
|
|
42
|
+
},
|
|
43
|
+
// Common parameters for most resource types
|
|
44
|
+
name: {
|
|
45
|
+
type: "string",
|
|
46
|
+
description: "Name of the resource to create"
|
|
47
|
+
},
|
|
48
|
+
namespace: {
|
|
49
|
+
type: "string",
|
|
50
|
+
description: "Namespace to create the resource in",
|
|
51
|
+
default: "default"
|
|
52
|
+
},
|
|
53
|
+
// ConfigMap specific parameters
|
|
54
|
+
fromLiteral: {
|
|
55
|
+
type: "array",
|
|
56
|
+
items: { type: "string" },
|
|
57
|
+
description: "Key-value pair for creating configmap (e.g. [\"key1=value1\", \"key2=value2\"])"
|
|
58
|
+
},
|
|
59
|
+
fromFile: {
|
|
60
|
+
type: "array",
|
|
61
|
+
items: { type: "string" },
|
|
62
|
+
description: "Path to file for creating configmap (e.g. [\"key1=/path/to/file1\", \"key2=/path/to/file2\"])"
|
|
63
|
+
},
|
|
64
|
+
// Namespace specific parameters
|
|
65
|
+
// No special parameters for namespace, just name is needed
|
|
66
|
+
// Secret specific parameters
|
|
67
|
+
secretType: {
|
|
68
|
+
type: "string",
|
|
69
|
+
enum: ["generic", "docker-registry", "tls"],
|
|
70
|
+
description: "Type of secret to create (generic, docker-registry, tls)"
|
|
71
|
+
},
|
|
72
|
+
// Service specific parameters
|
|
73
|
+
serviceType: {
|
|
74
|
+
type: "string",
|
|
75
|
+
enum: ["clusterip", "nodeport", "loadbalancer", "externalname"],
|
|
76
|
+
description: "Type of service to create (clusterip, nodeport, loadbalancer, externalname)"
|
|
77
|
+
},
|
|
78
|
+
tcpPort: {
|
|
79
|
+
type: "array",
|
|
80
|
+
items: { type: "string" },
|
|
81
|
+
description: "Port pairs for tcp service (e.g. [\"80:8080\", \"443:8443\"])"
|
|
82
|
+
},
|
|
83
|
+
// Deployment specific parameters
|
|
84
|
+
image: {
|
|
85
|
+
type: "string",
|
|
86
|
+
description: "Image to use for the containers in the deployment"
|
|
87
|
+
},
|
|
88
|
+
replicas: {
|
|
89
|
+
type: "number",
|
|
90
|
+
description: "Number of replicas to create for the deployment",
|
|
91
|
+
default: 1
|
|
92
|
+
},
|
|
93
|
+
port: {
|
|
94
|
+
type: "number",
|
|
95
|
+
description: "Port that the container exposes"
|
|
96
|
+
},
|
|
97
|
+
// CronJob specific parameters
|
|
98
|
+
schedule: {
|
|
99
|
+
type: "string",
|
|
100
|
+
description: "Cron schedule expression for the CronJob (e.g. \"*/5 * * * *\")"
|
|
101
|
+
},
|
|
102
|
+
suspend: {
|
|
103
|
+
type: "boolean",
|
|
104
|
+
description: "Whether to suspend the CronJob",
|
|
105
|
+
default: false
|
|
106
|
+
},
|
|
107
|
+
// Job specific parameters
|
|
108
|
+
command: {
|
|
109
|
+
type: "array",
|
|
110
|
+
items: { type: "string" },
|
|
111
|
+
description: "Command to run in the container"
|
|
112
|
+
},
|
|
113
|
+
// Additional common parameters
|
|
114
|
+
labels: {
|
|
115
|
+
type: "array",
|
|
116
|
+
items: { type: "string" },
|
|
117
|
+
description: "Labels to apply to the resource (e.g. [\"key1=value1\", \"key2=value2\"])"
|
|
118
|
+
},
|
|
119
|
+
annotations: {
|
|
120
|
+
type: "array",
|
|
121
|
+
items: { type: "string" },
|
|
122
|
+
description: "Annotations to apply to the resource (e.g. [\"key1=value1\", \"key2=value2\"])"
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
required: [],
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
export async function kubectlCreate(k8sManager, input) {
|
|
129
|
+
try {
|
|
130
|
+
// Check if we have enough information to proceed
|
|
131
|
+
if (!input.manifest && !input.filename && !input.resourceType) {
|
|
132
|
+
throw new McpError(ErrorCode.InvalidRequest, "Either manifest, filename, or resourceType must be provided");
|
|
133
|
+
}
|
|
134
|
+
// If resourceType is provided, check if name is provided for most resource types
|
|
135
|
+
if (input.resourceType && !input.name && input.resourceType !== "namespace") {
|
|
136
|
+
throw new McpError(ErrorCode.InvalidRequest, `Name is required when creating a ${input.resourceType}`);
|
|
137
|
+
}
|
|
138
|
+
// Set up common parameters
|
|
139
|
+
const namespace = input.namespace || "default";
|
|
140
|
+
const dryRun = input.dryRun || false;
|
|
141
|
+
const validate = input.validate ?? true;
|
|
142
|
+
const output = input.output || "yaml";
|
|
143
|
+
let command = "kubectl create";
|
|
144
|
+
let tempFile = null;
|
|
145
|
+
// Process manifest content if provided (file-based creation)
|
|
146
|
+
if (input.manifest || input.filename) {
|
|
147
|
+
if (input.manifest) {
|
|
148
|
+
// Create temporary file for the manifest
|
|
149
|
+
const tmpDir = os.tmpdir();
|
|
150
|
+
tempFile = path.join(tmpDir, `create-manifest-${Date.now()}.yaml`);
|
|
151
|
+
fs.writeFileSync(tempFile, input.manifest);
|
|
152
|
+
command += ` -f ${tempFile}`;
|
|
153
|
+
}
|
|
154
|
+
else if (input.filename) {
|
|
155
|
+
command += ` -f ${input.filename}`;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
// Process subcommand-based creation
|
|
160
|
+
switch (input.resourceType?.toLowerCase()) {
|
|
161
|
+
case "namespace":
|
|
162
|
+
command += ` namespace ${input.name}`;
|
|
163
|
+
break;
|
|
164
|
+
case "configmap":
|
|
165
|
+
command += ` configmap ${input.name}`;
|
|
166
|
+
// Add --from-literal arguments
|
|
167
|
+
if (input.fromLiteral && input.fromLiteral.length > 0) {
|
|
168
|
+
input.fromLiteral.forEach(literal => {
|
|
169
|
+
command += ` --from-literal=${literal}`;
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
// Add --from-file arguments
|
|
173
|
+
if (input.fromFile && input.fromFile.length > 0) {
|
|
174
|
+
input.fromFile.forEach(file => {
|
|
175
|
+
command += ` --from-file=${file}`;
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
break;
|
|
179
|
+
case "secret":
|
|
180
|
+
if (!input.secretType) {
|
|
181
|
+
throw new McpError(ErrorCode.InvalidRequest, "secretType is required when creating a secret");
|
|
182
|
+
}
|
|
183
|
+
command += ` secret ${input.secretType} ${input.name}`;
|
|
184
|
+
// Add --from-literal arguments
|
|
185
|
+
if (input.fromLiteral && input.fromLiteral.length > 0) {
|
|
186
|
+
input.fromLiteral.forEach(literal => {
|
|
187
|
+
command += ` --from-literal=${literal}`;
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
// Add --from-file arguments
|
|
191
|
+
if (input.fromFile && input.fromFile.length > 0) {
|
|
192
|
+
input.fromFile.forEach(file => {
|
|
193
|
+
command += ` --from-file=${file}`;
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
break;
|
|
197
|
+
case "service":
|
|
198
|
+
if (!input.serviceType) {
|
|
199
|
+
// Default to clusterip if not specified
|
|
200
|
+
input.serviceType = "clusterip";
|
|
201
|
+
}
|
|
202
|
+
command += ` service ${input.serviceType} ${input.name}`;
|
|
203
|
+
// Add --tcp arguments for ports
|
|
204
|
+
if (input.tcpPort && input.tcpPort.length > 0) {
|
|
205
|
+
input.tcpPort.forEach(port => {
|
|
206
|
+
command += ` --tcp=${port}`;
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
break;
|
|
210
|
+
case "cronjob":
|
|
211
|
+
if (!input.image) {
|
|
212
|
+
throw new McpError(ErrorCode.InvalidRequest, "image is required when creating a cronjob");
|
|
213
|
+
}
|
|
214
|
+
if (!input.schedule) {
|
|
215
|
+
throw new McpError(ErrorCode.InvalidRequest, "schedule is required when creating a cronjob");
|
|
216
|
+
}
|
|
217
|
+
command += ` cronjob ${input.name} --image=${input.image} --schedule="${input.schedule}"`;
|
|
218
|
+
// Add command if specified
|
|
219
|
+
if (input.command && input.command.length > 0) {
|
|
220
|
+
command += ` -- ${input.command.join(" ")}`;
|
|
221
|
+
}
|
|
222
|
+
// Add suspend flag if specified
|
|
223
|
+
if (input.suspend === true) {
|
|
224
|
+
command += ` --suspend`;
|
|
225
|
+
}
|
|
226
|
+
break;
|
|
227
|
+
case "deployment":
|
|
228
|
+
if (!input.image) {
|
|
229
|
+
throw new McpError(ErrorCode.InvalidRequest, "image is required when creating a deployment");
|
|
230
|
+
}
|
|
231
|
+
command += ` deployment ${input.name} --image=${input.image}`;
|
|
232
|
+
// Add replicas if specified
|
|
233
|
+
if (input.replicas) {
|
|
234
|
+
command += ` --replicas=${input.replicas}`;
|
|
235
|
+
}
|
|
236
|
+
// Add port if specified
|
|
237
|
+
if (input.port) {
|
|
238
|
+
command += ` --port=${input.port}`;
|
|
239
|
+
}
|
|
240
|
+
break;
|
|
241
|
+
case "job":
|
|
242
|
+
if (!input.image) {
|
|
243
|
+
throw new McpError(ErrorCode.InvalidRequest, "image is required when creating a job");
|
|
244
|
+
}
|
|
245
|
+
command += ` job ${input.name} --image=${input.image}`;
|
|
246
|
+
// Add command if specified
|
|
247
|
+
if (input.command && input.command.length > 0) {
|
|
248
|
+
command += ` -- ${input.command.join(" ")}`;
|
|
249
|
+
}
|
|
250
|
+
break;
|
|
251
|
+
default:
|
|
252
|
+
throw new McpError(ErrorCode.InvalidRequest, `Unsupported resource type: ${input.resourceType}`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
// Add namespace if not creating a namespace itself
|
|
256
|
+
if (input.resourceType !== "namespace") {
|
|
257
|
+
command += ` -n ${namespace}`;
|
|
258
|
+
}
|
|
259
|
+
// Add labels if specified
|
|
260
|
+
if (input.labels && input.labels.length > 0) {
|
|
261
|
+
input.labels.forEach(label => {
|
|
262
|
+
command += ` -l ${label}`;
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
// Add annotations if specified
|
|
266
|
+
if (input.annotations && input.annotations.length > 0) {
|
|
267
|
+
input.annotations.forEach(annotation => {
|
|
268
|
+
command += ` --annotation=${annotation}`;
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
// Add dry-run flag if requested
|
|
272
|
+
if (dryRun) {
|
|
273
|
+
command += " --dry-run=client";
|
|
274
|
+
}
|
|
275
|
+
// Add validate flag if needed
|
|
276
|
+
if (!validate) {
|
|
277
|
+
command += " --validate=false";
|
|
278
|
+
}
|
|
279
|
+
// Add output format
|
|
280
|
+
command += ` -o ${output}`;
|
|
281
|
+
// Execute the command
|
|
282
|
+
try {
|
|
283
|
+
const result = execSync(command, { encoding: "utf8" });
|
|
284
|
+
// Clean up temp file if created
|
|
285
|
+
if (tempFile) {
|
|
286
|
+
try {
|
|
287
|
+
fs.unlinkSync(tempFile);
|
|
288
|
+
}
|
|
289
|
+
catch (err) {
|
|
290
|
+
console.warn(`Failed to delete temporary file ${tempFile}: ${err}`);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return {
|
|
294
|
+
content: [
|
|
295
|
+
{
|
|
296
|
+
type: "text",
|
|
297
|
+
text: result,
|
|
298
|
+
},
|
|
299
|
+
],
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
catch (error) {
|
|
303
|
+
// Clean up temp file if created, even if command failed
|
|
304
|
+
if (tempFile) {
|
|
305
|
+
try {
|
|
306
|
+
fs.unlinkSync(tempFile);
|
|
307
|
+
}
|
|
308
|
+
catch (err) {
|
|
309
|
+
console.warn(`Failed to delete temporary file ${tempFile}: ${err}`);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
throw new McpError(ErrorCode.InternalError, `Failed to create resource: ${error.message}`);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
catch (error) {
|
|
316
|
+
if (error instanceof McpError) {
|
|
317
|
+
throw error;
|
|
318
|
+
}
|
|
319
|
+
throw new McpError(ErrorCode.InternalError, `Failed to execute kubectl create command: ${error.message}`);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { KubernetesManager } from "../types.js";
|
|
2
|
+
export declare const kubectlDeleteSchema: {
|
|
3
|
+
readonly name: "kubectl_delete";
|
|
4
|
+
readonly description: "Delete Kubernetes resources by resource type, name, labels, or from a manifest file";
|
|
5
|
+
readonly inputSchema: {
|
|
6
|
+
readonly type: "object";
|
|
7
|
+
readonly properties: {
|
|
8
|
+
readonly resourceType: {
|
|
9
|
+
readonly type: "string";
|
|
10
|
+
readonly description: "Type of resource to delete (e.g., pods, deployments, services, etc.)";
|
|
11
|
+
};
|
|
12
|
+
readonly name: {
|
|
13
|
+
readonly type: "string";
|
|
14
|
+
readonly description: "Name of the resource to delete";
|
|
15
|
+
};
|
|
16
|
+
readonly namespace: {
|
|
17
|
+
readonly type: "string";
|
|
18
|
+
readonly description: "Namespace of the resource (optional - defaults to 'default' for namespaced resources)";
|
|
19
|
+
readonly default: "default";
|
|
20
|
+
};
|
|
21
|
+
readonly labelSelector: {
|
|
22
|
+
readonly type: "string";
|
|
23
|
+
readonly description: "Delete resources matching this label selector (e.g. 'app=nginx')";
|
|
24
|
+
readonly optional: true;
|
|
25
|
+
};
|
|
26
|
+
readonly manifest: {
|
|
27
|
+
readonly type: "string";
|
|
28
|
+
readonly description: "YAML manifest defining resources to delete (optional)";
|
|
29
|
+
readonly optional: true;
|
|
30
|
+
};
|
|
31
|
+
readonly filename: {
|
|
32
|
+
readonly type: "string";
|
|
33
|
+
readonly description: "Path to a YAML file to delete resources from (optional)";
|
|
34
|
+
readonly optional: true;
|
|
35
|
+
};
|
|
36
|
+
readonly allNamespaces: {
|
|
37
|
+
readonly type: "boolean";
|
|
38
|
+
readonly description: "If true, delete resources across all namespaces";
|
|
39
|
+
readonly default: false;
|
|
40
|
+
};
|
|
41
|
+
readonly force: {
|
|
42
|
+
readonly type: "boolean";
|
|
43
|
+
readonly description: "If true, immediately remove resources from API and bypass graceful deletion";
|
|
44
|
+
readonly default: false;
|
|
45
|
+
};
|
|
46
|
+
readonly gracePeriodSeconds: {
|
|
47
|
+
readonly type: "number";
|
|
48
|
+
readonly description: "Period of time in seconds given to the resource to terminate gracefully";
|
|
49
|
+
readonly optional: true;
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
readonly required: readonly [];
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
export declare function kubectlDelete(k8sManager: KubernetesManager, input: {
|
|
56
|
+
resourceType?: string;
|
|
57
|
+
name?: string;
|
|
58
|
+
namespace?: string;
|
|
59
|
+
labelSelector?: string;
|
|
60
|
+
manifest?: string;
|
|
61
|
+
filename?: string;
|
|
62
|
+
allNamespaces?: boolean;
|
|
63
|
+
force?: boolean;
|
|
64
|
+
gracePeriodSeconds?: number;
|
|
65
|
+
}): Promise<{
|
|
66
|
+
content: {
|
|
67
|
+
type: string;
|
|
68
|
+
text: string;
|
|
69
|
+
}[];
|
|
70
|
+
isError?: undefined;
|
|
71
|
+
} | {
|
|
72
|
+
content: {
|
|
73
|
+
type: string;
|
|
74
|
+
text: string;
|
|
75
|
+
}[];
|
|
76
|
+
isError: boolean;
|
|
77
|
+
}>;
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { execSync } from "child_process";
|
|
2
|
+
import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
|
|
3
|
+
import * as fs from "fs";
|
|
4
|
+
import * as path from "path";
|
|
5
|
+
import * as os from "os";
|
|
6
|
+
export const kubectlDeleteSchema = {
|
|
7
|
+
name: "kubectl_delete",
|
|
8
|
+
description: "Delete Kubernetes resources by resource type, name, labels, or from a manifest file",
|
|
9
|
+
inputSchema: {
|
|
10
|
+
type: "object",
|
|
11
|
+
properties: {
|
|
12
|
+
resourceType: {
|
|
13
|
+
type: "string",
|
|
14
|
+
description: "Type of resource to delete (e.g., pods, deployments, services, etc.)"
|
|
15
|
+
},
|
|
16
|
+
name: {
|
|
17
|
+
type: "string",
|
|
18
|
+
description: "Name of the resource to delete"
|
|
19
|
+
},
|
|
20
|
+
namespace: {
|
|
21
|
+
type: "string",
|
|
22
|
+
description: "Namespace of the resource (optional - defaults to 'default' for namespaced resources)",
|
|
23
|
+
default: "default"
|
|
24
|
+
},
|
|
25
|
+
labelSelector: {
|
|
26
|
+
type: "string",
|
|
27
|
+
description: "Delete resources matching this label selector (e.g. 'app=nginx')",
|
|
28
|
+
optional: true
|
|
29
|
+
},
|
|
30
|
+
manifest: {
|
|
31
|
+
type: "string",
|
|
32
|
+
description: "YAML manifest defining resources to delete (optional)",
|
|
33
|
+
optional: true
|
|
34
|
+
},
|
|
35
|
+
filename: {
|
|
36
|
+
type: "string",
|
|
37
|
+
description: "Path to a YAML file to delete resources from (optional)",
|
|
38
|
+
optional: true
|
|
39
|
+
},
|
|
40
|
+
allNamespaces: {
|
|
41
|
+
type: "boolean",
|
|
42
|
+
description: "If true, delete resources across all namespaces",
|
|
43
|
+
default: false
|
|
44
|
+
},
|
|
45
|
+
force: {
|
|
46
|
+
type: "boolean",
|
|
47
|
+
description: "If true, immediately remove resources from API and bypass graceful deletion",
|
|
48
|
+
default: false
|
|
49
|
+
},
|
|
50
|
+
gracePeriodSeconds: {
|
|
51
|
+
type: "number",
|
|
52
|
+
description: "Period of time in seconds given to the resource to terminate gracefully",
|
|
53
|
+
optional: true
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
required: [],
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
export async function kubectlDelete(k8sManager, input) {
|
|
60
|
+
try {
|
|
61
|
+
// Validate input - need at least one way to identify resources
|
|
62
|
+
if (!input.resourceType && !input.manifest && !input.filename) {
|
|
63
|
+
throw new McpError(ErrorCode.InvalidRequest, "Either resourceType, manifest, or filename must be provided");
|
|
64
|
+
}
|
|
65
|
+
// If resourceType is provided, need either name or labelSelector
|
|
66
|
+
if (input.resourceType && !input.name && !input.labelSelector) {
|
|
67
|
+
throw new McpError(ErrorCode.InvalidRequest, "When using resourceType, either name or labelSelector must be provided");
|
|
68
|
+
}
|
|
69
|
+
const namespace = input.namespace || "default";
|
|
70
|
+
const allNamespaces = input.allNamespaces || false;
|
|
71
|
+
const force = input.force || false;
|
|
72
|
+
let command = "kubectl delete";
|
|
73
|
+
let tempFile = null;
|
|
74
|
+
// Handle deleting from manifest or file
|
|
75
|
+
if (input.manifest) {
|
|
76
|
+
// Create temporary file for the manifest
|
|
77
|
+
const tmpDir = os.tmpdir();
|
|
78
|
+
tempFile = path.join(tmpDir, `delete-manifest-${Date.now()}.yaml`);
|
|
79
|
+
fs.writeFileSync(tempFile, input.manifest);
|
|
80
|
+
command += ` -f ${tempFile}`;
|
|
81
|
+
}
|
|
82
|
+
else if (input.filename) {
|
|
83
|
+
command += ` -f ${input.filename}`;
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
// Handle deleting by resource type and name/selector
|
|
87
|
+
command += ` ${input.resourceType}`;
|
|
88
|
+
if (input.name) {
|
|
89
|
+
command += ` ${input.name}`;
|
|
90
|
+
}
|
|
91
|
+
if (input.labelSelector) {
|
|
92
|
+
command += ` -l ${input.labelSelector}`;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Add namespace flags
|
|
96
|
+
if (allNamespaces) {
|
|
97
|
+
command += " --all-namespaces";
|
|
98
|
+
}
|
|
99
|
+
else if (namespace && input.resourceType && !isNonNamespacedResource(input.resourceType)) {
|
|
100
|
+
command += ` -n ${namespace}`;
|
|
101
|
+
}
|
|
102
|
+
// Add force flag if requested
|
|
103
|
+
if (force) {
|
|
104
|
+
command += " --force";
|
|
105
|
+
}
|
|
106
|
+
// Add grace period if specified
|
|
107
|
+
if (input.gracePeriodSeconds !== undefined) {
|
|
108
|
+
command += ` --grace-period=${input.gracePeriodSeconds}`;
|
|
109
|
+
}
|
|
110
|
+
// Execute the command
|
|
111
|
+
try {
|
|
112
|
+
const result = execSync(command, { encoding: "utf8" });
|
|
113
|
+
// Clean up temp file if created
|
|
114
|
+
if (tempFile) {
|
|
115
|
+
try {
|
|
116
|
+
fs.unlinkSync(tempFile);
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
console.warn(`Failed to delete temporary file ${tempFile}: ${err}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
content: [
|
|
124
|
+
{
|
|
125
|
+
type: "text",
|
|
126
|
+
text: result,
|
|
127
|
+
},
|
|
128
|
+
],
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
// Clean up temp file if created, even if command failed
|
|
133
|
+
if (tempFile) {
|
|
134
|
+
try {
|
|
135
|
+
fs.unlinkSync(tempFile);
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
console.warn(`Failed to delete temporary file ${tempFile}: ${err}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (error.status === 404 || error.message.includes("not found")) {
|
|
142
|
+
return {
|
|
143
|
+
content: [
|
|
144
|
+
{
|
|
145
|
+
type: "text",
|
|
146
|
+
text: JSON.stringify({
|
|
147
|
+
error: `Resource not found`,
|
|
148
|
+
status: "not_found",
|
|
149
|
+
}, null, 2),
|
|
150
|
+
},
|
|
151
|
+
],
|
|
152
|
+
isError: true,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
throw new McpError(ErrorCode.InternalError, `Failed to delete resource: ${error.message}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
if (error instanceof McpError) {
|
|
160
|
+
throw error;
|
|
161
|
+
}
|
|
162
|
+
throw new McpError(ErrorCode.InternalError, `Failed to execute kubectl delete command: ${error.message}`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
// Helper function to determine if a resource is non-namespaced
|
|
166
|
+
function isNonNamespacedResource(resourceType) {
|
|
167
|
+
const nonNamespacedResources = [
|
|
168
|
+
"nodes", "node", "no",
|
|
169
|
+
"namespaces", "namespace", "ns",
|
|
170
|
+
"persistentvolumes", "pv",
|
|
171
|
+
"storageclasses", "sc",
|
|
172
|
+
"clusterroles",
|
|
173
|
+
"clusterrolebindings",
|
|
174
|
+
"customresourcedefinitions", "crd", "crds"
|
|
175
|
+
];
|
|
176
|
+
return nonNamespacedResources.includes(resourceType.toLowerCase());
|
|
177
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { KubernetesManager } from "../types.js";
|
|
2
|
+
export declare const kubectlDescribeSchema: {
|
|
3
|
+
readonly name: "kubectl_describe";
|
|
4
|
+
readonly description: "Describe Kubernetes resources by resource type, name, and optionally namespace";
|
|
5
|
+
readonly inputSchema: {
|
|
6
|
+
readonly type: "object";
|
|
7
|
+
readonly properties: {
|
|
8
|
+
readonly resourceType: {
|
|
9
|
+
readonly type: "string";
|
|
10
|
+
readonly description: "Type of resource to describe (e.g., pods, deployments, services, etc.)";
|
|
11
|
+
};
|
|
12
|
+
readonly name: {
|
|
13
|
+
readonly type: "string";
|
|
14
|
+
readonly description: "Name of the resource to describe";
|
|
15
|
+
};
|
|
16
|
+
readonly namespace: {
|
|
17
|
+
readonly type: "string";
|
|
18
|
+
readonly description: "Namespace of the resource (optional - defaults to 'default' for namespaced resources)";
|
|
19
|
+
readonly default: "default";
|
|
20
|
+
};
|
|
21
|
+
readonly allNamespaces: {
|
|
22
|
+
readonly type: "boolean";
|
|
23
|
+
readonly description: "If true, describe resources across all namespaces";
|
|
24
|
+
readonly default: false;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
readonly required: readonly ["resourceType", "name"];
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
export declare function kubectlDescribe(k8sManager: KubernetesManager, input: {
|
|
31
|
+
resourceType: string;
|
|
32
|
+
name: string;
|
|
33
|
+
namespace?: string;
|
|
34
|
+
allNamespaces?: boolean;
|
|
35
|
+
}): Promise<{
|
|
36
|
+
content: {
|
|
37
|
+
type: string;
|
|
38
|
+
text: string;
|
|
39
|
+
}[];
|
|
40
|
+
isError?: undefined;
|
|
41
|
+
} | {
|
|
42
|
+
content: {
|
|
43
|
+
type: string;
|
|
44
|
+
text: string;
|
|
45
|
+
}[];
|
|
46
|
+
isError: boolean;
|
|
47
|
+
}>;
|