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