mcp-server-kubernetes 0.1.5 → 0.2.3
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 +29 -9
- package/dist/config/cleanup-config.d.ts +8 -0
- package/dist/config/cleanup-config.js +8 -0
- package/dist/config/container-templates.d.ts +5 -0
- package/dist/config/container-templates.js +109 -0
- package/dist/config/deployment-config.d.ts +31 -0
- package/dist/config/deployment-config.js +23 -0
- package/dist/config/namespace-config.d.ts +8 -0
- package/dist/config/namespace-config.js +8 -0
- package/dist/config/server-config.d.ts +8 -0
- package/dist/config/server-config.js +8 -0
- package/dist/index.js +49 -928
- package/dist/models/helm-models.d.ts +39 -0
- package/dist/models/helm-models.js +8 -0
- package/dist/models/resource-models.d.ts +94 -0
- package/dist/models/resource-models.js +17 -0
- package/dist/models/response-schemas.d.ts +221 -0
- package/dist/models/response-schemas.js +36 -0
- package/dist/models/tool-models.d.ts +42 -0
- package/dist/models/tool-models.js +10 -0
- package/dist/resources/handlers.d.ts +22 -0
- package/dist/resources/handlers.js +112 -0
- package/dist/tools/create_pod.d.ts +39 -0
- package/dist/tools/create_pod.js +71 -0
- package/dist/tools/delete_pod.d.ts +31 -0
- package/dist/tools/delete_pod.js +45 -0
- package/dist/tools/describe_pod.d.ts +33 -0
- package/dist/tools/describe_pod.js +81 -0
- package/dist/tools/get_logs.d.ts +67 -0
- package/dist/tools/get_logs.js +150 -0
- package/dist/tools/helm-operations.d.ts +99 -0
- package/dist/tools/helm-operations.js +198 -0
- package/dist/tools/list_deployments.d.ts +23 -0
- package/dist/tools/list_deployments.js +30 -0
- package/dist/tools/list_nodes.d.ts +15 -0
- package/dist/tools/list_nodes.js +21 -0
- package/dist/tools/list_pods.d.ts +23 -0
- package/dist/tools/list_pods.js +29 -0
- package/dist/tools/list_services.d.ts +23 -0
- package/dist/tools/list_services.js +31 -0
- package/dist/types.d.ts +4 -433
- package/dist/types.js +6 -120
- package/dist/utils/kubernetes-manager.d.ts +21 -0
- package/dist/utils/kubernetes-manager.js +68 -0
- package/package.json +3 -2
- package/dist/helm.test.d.ts +0 -1
- package/dist/helm.test.js +0 -208
- package/dist/unit.test.d.ts +0 -1
- package/dist/unit.test.js +0 -293
package/dist/index.js
CHANGED
|
@@ -1,523 +1,60 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { listPods, listPodsSchema } from "./tools/list_pods.js";
|
|
5
|
+
import { listNodes, listNodesSchema } from "./tools/list_nodes.js";
|
|
6
|
+
import { listServices, listServicesSchema } from "./tools/list_services.js";
|
|
7
|
+
import { listDeployments, listDeploymentsSchema } from "./tools/list_deployments.js";
|
|
8
|
+
import { installHelmChart, installHelmChartSchema, upgradeHelmChart, upgradeHelmChartSchema, uninstallHelmChart, uninstallHelmChartSchema, } from "./tools/helm-operations.js";
|
|
9
|
+
import { createPod, createPodSchema } from "./tools/create_pod.js";
|
|
10
|
+
import { deletePod, deletePodSchema } from "./tools/delete_pod.js";
|
|
11
|
+
import { describePod, describePodSchema } from "./tools/describe_pod.js";
|
|
12
|
+
import { getLogs, getLogsSchema } from "./tools/get_logs.js";
|
|
13
|
+
import { getResourceHandlers } from "./resources/handlers.js";
|
|
4
14
|
import { ListResourcesRequestSchema, ReadResourceRequestSchema, ListToolsRequestSchema, CallToolRequestSchema, ErrorCode, McpError, } from "@modelcontextprotocol/sdk/types.js";
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
resources = [];
|
|
11
|
-
portForwards = [];
|
|
12
|
-
watches = [];
|
|
13
|
-
kc;
|
|
14
|
-
k8sApi;
|
|
15
|
-
k8sAppsApi;
|
|
16
|
-
constructor() {
|
|
17
|
-
this.kc = new k8s.KubeConfig();
|
|
18
|
-
this.kc.loadFromDefault();
|
|
19
|
-
this.k8sApi = this.kc.makeApiClient(k8s.CoreV1Api);
|
|
20
|
-
this.k8sAppsApi = this.kc.makeApiClient(k8s.AppsV1Api);
|
|
21
|
-
// process.on("SIGINT", () => this.cleanup());
|
|
22
|
-
// process.on("SIGTERM", () => this.cleanup());
|
|
23
|
-
}
|
|
24
|
-
async cleanup() {
|
|
25
|
-
// Stop watches
|
|
26
|
-
for (const watch of this.watches) {
|
|
27
|
-
watch.abort.abort();
|
|
28
|
-
}
|
|
29
|
-
// Delete tracked resources in reverse order
|
|
30
|
-
for (const resource of [...this.resources].reverse()) {
|
|
31
|
-
try {
|
|
32
|
-
await this.deleteResource(resource.kind, resource.name, resource.namespace);
|
|
33
|
-
}
|
|
34
|
-
catch (error) {
|
|
35
|
-
console.error(`Failed to delete ${resource.kind} ${resource.name}:`, error);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
// TODO: Cleanup port forwards when implemented
|
|
39
|
-
}
|
|
40
|
-
trackResource(kind, name, namespace) {
|
|
41
|
-
this.resources.push({ kind, name, namespace, createdAt: new Date() });
|
|
42
|
-
}
|
|
43
|
-
async deleteResource(kind, name, namespace) {
|
|
44
|
-
switch (kind.toLowerCase()) {
|
|
45
|
-
case "pod":
|
|
46
|
-
await this.k8sApi.deleteNamespacedPod(name, namespace);
|
|
47
|
-
break;
|
|
48
|
-
case "deployment":
|
|
49
|
-
await this.k8sAppsApi.deleteNamespacedDeployment(name, namespace);
|
|
50
|
-
break;
|
|
51
|
-
case "service":
|
|
52
|
-
await this.k8sApi.deleteNamespacedService(name, namespace);
|
|
53
|
-
break;
|
|
54
|
-
}
|
|
55
|
-
this.resources = this.resources.filter((r) => !(r.kind === kind && r.name === name && r.namespace === namespace));
|
|
56
|
-
}
|
|
57
|
-
trackPortForward(pf) {
|
|
58
|
-
this.portForwards.push(pf);
|
|
59
|
-
}
|
|
60
|
-
getPortForward(id) {
|
|
61
|
-
return this.portForwards.find((p) => p.id === id);
|
|
62
|
-
}
|
|
63
|
-
removePortForward(id) {
|
|
64
|
-
this.portForwards = this.portForwards.filter((p) => p.id !== id);
|
|
65
|
-
}
|
|
66
|
-
trackWatch(watch) {
|
|
67
|
-
this.watches.push(watch);
|
|
68
|
-
}
|
|
69
|
-
getKubeConfig() {
|
|
70
|
-
return this.kc;
|
|
71
|
-
}
|
|
72
|
-
getCoreApi() {
|
|
73
|
-
return this.k8sApi;
|
|
74
|
-
}
|
|
75
|
-
getAppsApi() {
|
|
76
|
-
return this.k8sAppsApi;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
15
|
+
import { KubernetesManager } from "./types.js";
|
|
16
|
+
import { serverConfig } from "./config/server-config.js";
|
|
17
|
+
import { createDeploymentSchema } from "./config/deployment-config.js";
|
|
18
|
+
import { listNamespacesSchema } from "./config/namespace-config.js";
|
|
19
|
+
import { cleanupSchema } from "./config/cleanup-config.js";
|
|
79
20
|
const k8sManager = new KubernetesManager();
|
|
80
|
-
// Template configurations with health checks and resource limits
|
|
81
|
-
// TODO: Update create_pod to accept custom images and custom template files
|
|
82
|
-
const containerTemplates = {
|
|
83
|
-
ubuntu: {
|
|
84
|
-
name: "main",
|
|
85
|
-
image: "ubuntu:latest",
|
|
86
|
-
command: ["/bin/bash"],
|
|
87
|
-
args: ["-c", "sleep infinity"],
|
|
88
|
-
resources: {
|
|
89
|
-
limits: {
|
|
90
|
-
cpu: "200m",
|
|
91
|
-
memory: "256Mi",
|
|
92
|
-
},
|
|
93
|
-
requests: {
|
|
94
|
-
cpu: "100m",
|
|
95
|
-
memory: "128Mi",
|
|
96
|
-
},
|
|
97
|
-
},
|
|
98
|
-
livenessProbe: {
|
|
99
|
-
exec: {
|
|
100
|
-
command: ["cat", "/proc/1/status"],
|
|
101
|
-
},
|
|
102
|
-
initialDelaySeconds: 5,
|
|
103
|
-
periodSeconds: 10,
|
|
104
|
-
},
|
|
105
|
-
},
|
|
106
|
-
nginx: {
|
|
107
|
-
name: "main",
|
|
108
|
-
image: "nginx:latest",
|
|
109
|
-
ports: [{ containerPort: 80 }],
|
|
110
|
-
resources: {
|
|
111
|
-
limits: {
|
|
112
|
-
cpu: "200m",
|
|
113
|
-
memory: "256Mi",
|
|
114
|
-
},
|
|
115
|
-
requests: {
|
|
116
|
-
cpu: "100m",
|
|
117
|
-
memory: "128Mi",
|
|
118
|
-
},
|
|
119
|
-
},
|
|
120
|
-
livenessProbe: {
|
|
121
|
-
httpGet: {
|
|
122
|
-
path: "/",
|
|
123
|
-
port: 80,
|
|
124
|
-
},
|
|
125
|
-
initialDelaySeconds: 5,
|
|
126
|
-
periodSeconds: 10,
|
|
127
|
-
},
|
|
128
|
-
readinessProbe: {
|
|
129
|
-
httpGet: {
|
|
130
|
-
path: "/",
|
|
131
|
-
port: 80,
|
|
132
|
-
},
|
|
133
|
-
initialDelaySeconds: 2,
|
|
134
|
-
periodSeconds: 5,
|
|
135
|
-
},
|
|
136
|
-
},
|
|
137
|
-
busybox: {
|
|
138
|
-
name: "main",
|
|
139
|
-
image: "busybox:latest",
|
|
140
|
-
command: ["sh"],
|
|
141
|
-
args: ["-c", "sleep infinity"],
|
|
142
|
-
resources: {
|
|
143
|
-
limits: {
|
|
144
|
-
cpu: "100m",
|
|
145
|
-
memory: "64Mi",
|
|
146
|
-
},
|
|
147
|
-
requests: {
|
|
148
|
-
cpu: "50m",
|
|
149
|
-
memory: "32Mi",
|
|
150
|
-
},
|
|
151
|
-
},
|
|
152
|
-
livenessProbe: {
|
|
153
|
-
exec: {
|
|
154
|
-
command: ["true"],
|
|
155
|
-
},
|
|
156
|
-
periodSeconds: 10,
|
|
157
|
-
},
|
|
158
|
-
},
|
|
159
|
-
alpine: {
|
|
160
|
-
name: "main",
|
|
161
|
-
image: "alpine:latest",
|
|
162
|
-
command: ["sh"],
|
|
163
|
-
args: ["-c", "sleep infinity"],
|
|
164
|
-
resources: {
|
|
165
|
-
limits: {
|
|
166
|
-
cpu: "100m",
|
|
167
|
-
memory: "64Mi",
|
|
168
|
-
},
|
|
169
|
-
requests: {
|
|
170
|
-
cpu: "50m",
|
|
171
|
-
memory: "32Mi",
|
|
172
|
-
},
|
|
173
|
-
},
|
|
174
|
-
livenessProbe: {
|
|
175
|
-
exec: {
|
|
176
|
-
command: ["true"],
|
|
177
|
-
},
|
|
178
|
-
periodSeconds: 10,
|
|
179
|
-
},
|
|
180
|
-
},
|
|
181
|
-
};
|
|
182
21
|
const server = new Server({
|
|
183
|
-
name:
|
|
184
|
-
version:
|
|
185
|
-
},
|
|
186
|
-
capabilities: {
|
|
187
|
-
resources: {},
|
|
188
|
-
tools: {},
|
|
189
|
-
},
|
|
190
|
-
});
|
|
191
|
-
// Helper function to execute shell commands
|
|
192
|
-
function execCommand(command) {
|
|
193
|
-
return new Promise((resolve, reject) => {
|
|
194
|
-
exec(command, (error, stdout, stderr) => {
|
|
195
|
-
if (error) {
|
|
196
|
-
reject(new Error(`Command failed: ${error.message}\n${stderr}`));
|
|
197
|
-
return;
|
|
198
|
-
}
|
|
199
|
-
resolve(stdout.trim());
|
|
200
|
-
});
|
|
201
|
-
});
|
|
202
|
-
}
|
|
22
|
+
name: serverConfig.name,
|
|
23
|
+
version: serverConfig.version,
|
|
24
|
+
}, serverConfig);
|
|
203
25
|
// Tools handlers
|
|
204
26
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
205
27
|
return {
|
|
206
28
|
tools: [
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
inputSchema: {
|
|
222
|
-
type: "object",
|
|
223
|
-
properties: {
|
|
224
|
-
namespace: { type: "string", default: "default" },
|
|
225
|
-
},
|
|
226
|
-
required: ["namespace"],
|
|
227
|
-
},
|
|
228
|
-
},
|
|
229
|
-
{
|
|
230
|
-
name: "list_services",
|
|
231
|
-
description: "List services in a namespace",
|
|
232
|
-
inputSchema: {
|
|
233
|
-
type: "object",
|
|
234
|
-
properties: {
|
|
235
|
-
namespace: { type: "string", default: "default" },
|
|
236
|
-
},
|
|
237
|
-
required: ["namespace"],
|
|
238
|
-
},
|
|
239
|
-
},
|
|
240
|
-
{
|
|
241
|
-
name: "list_namespaces",
|
|
242
|
-
description: "List all namespaces",
|
|
243
|
-
inputSchema: {
|
|
244
|
-
type: "object",
|
|
245
|
-
properties: {},
|
|
246
|
-
},
|
|
247
|
-
},
|
|
248
|
-
{
|
|
249
|
-
// TODO: Add support for custom images and templates (see above in containerTemplates definition)
|
|
250
|
-
name: "create_pod",
|
|
251
|
-
description: "Create a new Kubernetes pod",
|
|
252
|
-
inputSchema: {
|
|
253
|
-
type: "object",
|
|
254
|
-
properties: {
|
|
255
|
-
name: { type: "string" },
|
|
256
|
-
namespace: { type: "string" },
|
|
257
|
-
template: {
|
|
258
|
-
type: "string",
|
|
259
|
-
enum: ["ubuntu", "nginx", "busybox", "alpine"],
|
|
260
|
-
},
|
|
261
|
-
command: {
|
|
262
|
-
type: "array",
|
|
263
|
-
items: { type: "string" },
|
|
264
|
-
optional: true,
|
|
265
|
-
},
|
|
266
|
-
},
|
|
267
|
-
required: ["name", "namespace", "template"],
|
|
268
|
-
},
|
|
269
|
-
},
|
|
270
|
-
{
|
|
271
|
-
// TODO: Support for custom deployments (see above)
|
|
272
|
-
name: "create_deployment",
|
|
273
|
-
description: "Create a new Kubernetes deployment",
|
|
274
|
-
inputSchema: {
|
|
275
|
-
type: "object",
|
|
276
|
-
properties: {
|
|
277
|
-
name: { type: "string" },
|
|
278
|
-
namespace: { type: "string" },
|
|
279
|
-
template: {
|
|
280
|
-
type: "string",
|
|
281
|
-
enum: ["ubuntu", "nginx", "busybox", "alpine"],
|
|
282
|
-
},
|
|
283
|
-
replicas: { type: "number", default: 1 },
|
|
284
|
-
ports: {
|
|
285
|
-
type: "array",
|
|
286
|
-
items: { type: "number" },
|
|
287
|
-
optional: true,
|
|
288
|
-
},
|
|
289
|
-
},
|
|
290
|
-
required: ["name", "namespace", "template"],
|
|
291
|
-
},
|
|
292
|
-
},
|
|
293
|
-
{
|
|
294
|
-
name: "delete_pod",
|
|
295
|
-
description: "Delete a Kubernetes pod",
|
|
296
|
-
inputSchema: {
|
|
297
|
-
type: "object",
|
|
298
|
-
properties: {
|
|
299
|
-
name: { type: "string" },
|
|
300
|
-
namespace: { type: "string" },
|
|
301
|
-
ignoreNotFound: { type: "boolean", default: false },
|
|
302
|
-
},
|
|
303
|
-
required: ["name", "namespace"],
|
|
304
|
-
},
|
|
305
|
-
},
|
|
306
|
-
{
|
|
307
|
-
name: "describe_pod",
|
|
308
|
-
description: "Describe a Kubernetes pod (read details like status, containers, etc.)",
|
|
309
|
-
inputSchema: {
|
|
310
|
-
type: "object",
|
|
311
|
-
properties: {
|
|
312
|
-
name: { type: "string" },
|
|
313
|
-
namespace: { type: "string" },
|
|
314
|
-
},
|
|
315
|
-
required: ["name", "namespace"],
|
|
316
|
-
},
|
|
317
|
-
},
|
|
318
|
-
{
|
|
319
|
-
name: "cleanup",
|
|
320
|
-
description: "Cleanup all managed resources",
|
|
321
|
-
inputSchema: {
|
|
322
|
-
type: "object",
|
|
323
|
-
properties: {},
|
|
324
|
-
},
|
|
325
|
-
},
|
|
326
|
-
{
|
|
327
|
-
name: "list_nodes",
|
|
328
|
-
description: "List all nodes in the cluster",
|
|
329
|
-
inputSchema: {
|
|
330
|
-
type: "object",
|
|
331
|
-
properties: {},
|
|
332
|
-
},
|
|
333
|
-
},
|
|
334
|
-
{
|
|
335
|
-
name: "get_logs",
|
|
336
|
-
description: "Get logs from pods, deployments, jobs, or resources matching a label selector",
|
|
337
|
-
inputSchema: {
|
|
338
|
-
type: "object",
|
|
339
|
-
properties: {
|
|
340
|
-
resourceType: {
|
|
341
|
-
type: "string",
|
|
342
|
-
enum: ["pod", "deployment", "job"],
|
|
343
|
-
description: "Type of resource to get logs from",
|
|
344
|
-
},
|
|
345
|
-
name: {
|
|
346
|
-
type: "string",
|
|
347
|
-
description: "Name of the resource",
|
|
348
|
-
},
|
|
349
|
-
namespace: {
|
|
350
|
-
type: "string",
|
|
351
|
-
description: "Namespace of the resource",
|
|
352
|
-
default: "default",
|
|
353
|
-
},
|
|
354
|
-
labelSelector: {
|
|
355
|
-
type: "string",
|
|
356
|
-
description: "Label selector to filter resources",
|
|
357
|
-
optional: true,
|
|
358
|
-
},
|
|
359
|
-
container: {
|
|
360
|
-
type: "string",
|
|
361
|
-
description: "Container name (required when pod has multiple containers)",
|
|
362
|
-
optional: true,
|
|
363
|
-
},
|
|
364
|
-
tail: {
|
|
365
|
-
type: "number",
|
|
366
|
-
description: "Number of lines to show from end of logs",
|
|
367
|
-
optional: true,
|
|
368
|
-
},
|
|
369
|
-
since: {
|
|
370
|
-
type: "number",
|
|
371
|
-
description: "Get logs since relative time in seconds",
|
|
372
|
-
optional: true,
|
|
373
|
-
},
|
|
374
|
-
timestamps: {
|
|
375
|
-
type: "boolean",
|
|
376
|
-
description: "Include timestamps in logs",
|
|
377
|
-
default: false,
|
|
378
|
-
},
|
|
379
|
-
},
|
|
380
|
-
required: ["resourceType"],
|
|
381
|
-
},
|
|
382
|
-
},
|
|
383
|
-
{
|
|
384
|
-
name: "install_helm_chart",
|
|
385
|
-
description: "Install a Helm chart",
|
|
386
|
-
inputSchema: {
|
|
387
|
-
type: "object",
|
|
388
|
-
properties: {
|
|
389
|
-
name: { type: "string", description: "Release name" },
|
|
390
|
-
chart: { type: "string", description: "Chart name or URL" },
|
|
391
|
-
namespace: {
|
|
392
|
-
type: "string",
|
|
393
|
-
description: "Target namespace",
|
|
394
|
-
optional: true,
|
|
395
|
-
},
|
|
396
|
-
values: {
|
|
397
|
-
type: "object",
|
|
398
|
-
description: "Values to override",
|
|
399
|
-
optional: true,
|
|
400
|
-
},
|
|
401
|
-
version: {
|
|
402
|
-
type: "string",
|
|
403
|
-
description: "Chart version",
|
|
404
|
-
optional: true,
|
|
405
|
-
},
|
|
406
|
-
repo: {
|
|
407
|
-
type: "string",
|
|
408
|
-
description: "Chart repository URL",
|
|
409
|
-
optional: true,
|
|
410
|
-
},
|
|
411
|
-
},
|
|
412
|
-
required: ["name", "chart"],
|
|
413
|
-
},
|
|
414
|
-
},
|
|
415
|
-
{
|
|
416
|
-
name: "uninstall_helm_chart",
|
|
417
|
-
description: "Uninstall a Helm release",
|
|
418
|
-
inputSchema: {
|
|
419
|
-
type: "object",
|
|
420
|
-
properties: {
|
|
421
|
-
name: { type: "string", description: "Release name" },
|
|
422
|
-
namespace: {
|
|
423
|
-
type: "string",
|
|
424
|
-
description: "Release namespace",
|
|
425
|
-
optional: true,
|
|
426
|
-
},
|
|
427
|
-
},
|
|
428
|
-
required: ["name"],
|
|
429
|
-
},
|
|
430
|
-
},
|
|
431
|
-
{
|
|
432
|
-
name: "upgrade_helm_chart",
|
|
433
|
-
description: "Upgrade a Helm release with new values",
|
|
434
|
-
inputSchema: {
|
|
435
|
-
type: "object",
|
|
436
|
-
properties: {
|
|
437
|
-
name: { type: "string", description: "Release name" },
|
|
438
|
-
values: { type: "object", description: "New values to apply" },
|
|
439
|
-
namespace: {
|
|
440
|
-
type: "string",
|
|
441
|
-
description: "Release namespace",
|
|
442
|
-
optional: true,
|
|
443
|
-
},
|
|
444
|
-
},
|
|
445
|
-
required: ["name", "values"],
|
|
446
|
-
},
|
|
447
|
-
},
|
|
29
|
+
listPodsSchema,
|
|
30
|
+
listDeploymentsSchema,
|
|
31
|
+
listServicesSchema,
|
|
32
|
+
listNamespacesSchema,
|
|
33
|
+
createPodSchema,
|
|
34
|
+
createDeploymentSchema,
|
|
35
|
+
deletePodSchema,
|
|
36
|
+
describePodSchema,
|
|
37
|
+
cleanupSchema,
|
|
38
|
+
listNodesSchema,
|
|
39
|
+
getLogsSchema,
|
|
40
|
+
installHelmChartSchema,
|
|
41
|
+
upgradeHelmChartSchema,
|
|
42
|
+
uninstallHelmChartSchema,
|
|
448
43
|
],
|
|
449
44
|
};
|
|
450
45
|
});
|
|
451
46
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
452
47
|
try {
|
|
453
|
-
const { name } = request.params;
|
|
454
|
-
const input = request.params.arguments;
|
|
48
|
+
const { name, arguments: input = {} } = request.params;
|
|
455
49
|
switch (name) {
|
|
456
50
|
case "list_pods": {
|
|
457
|
-
|
|
458
|
-
const namespace = listPodsInput.namespace || "default";
|
|
459
|
-
const { body } = await k8sManager
|
|
460
|
-
.getCoreApi()
|
|
461
|
-
.listNamespacedPod(namespace);
|
|
462
|
-
const pods = body.items.map((pod) => ({
|
|
463
|
-
name: pod.metadata?.name || "",
|
|
464
|
-
namespace: pod.metadata?.namespace || "",
|
|
465
|
-
status: pod.status?.phase,
|
|
466
|
-
createdAt: pod.metadata?.creationTimestamp,
|
|
467
|
-
}));
|
|
468
|
-
return {
|
|
469
|
-
content: [
|
|
470
|
-
{
|
|
471
|
-
type: "text",
|
|
472
|
-
text: JSON.stringify({ pods }, null, 2),
|
|
473
|
-
},
|
|
474
|
-
],
|
|
475
|
-
};
|
|
51
|
+
return await listPods(k8sManager, input);
|
|
476
52
|
}
|
|
477
53
|
case "list_deployments": {
|
|
478
|
-
|
|
479
|
-
const namespace = listDeploymentsInput.namespace || "default";
|
|
480
|
-
const { body } = await k8sManager
|
|
481
|
-
.getAppsApi()
|
|
482
|
-
.listNamespacedDeployment(namespace);
|
|
483
|
-
const deployments = body.items.map((deployment) => ({
|
|
484
|
-
name: deployment.metadata?.name || "",
|
|
485
|
-
namespace: deployment.metadata?.namespace || "",
|
|
486
|
-
replicas: deployment.spec?.replicas || 0,
|
|
487
|
-
availableReplicas: deployment.status?.availableReplicas || 0,
|
|
488
|
-
createdAt: deployment.metadata?.creationTimestamp,
|
|
489
|
-
}));
|
|
490
|
-
return {
|
|
491
|
-
content: [
|
|
492
|
-
{
|
|
493
|
-
type: "text",
|
|
494
|
-
text: JSON.stringify({ deployments }, null, 2),
|
|
495
|
-
},
|
|
496
|
-
],
|
|
497
|
-
};
|
|
54
|
+
return await listDeployments(k8sManager, input);
|
|
498
55
|
}
|
|
499
56
|
case "list_services": {
|
|
500
|
-
|
|
501
|
-
const namespace = listServicesInput.namespace || "default";
|
|
502
|
-
const { body } = await k8sManager
|
|
503
|
-
.getCoreApi()
|
|
504
|
-
.listNamespacedService(namespace);
|
|
505
|
-
const services = body.items.map((service) => ({
|
|
506
|
-
name: service.metadata?.name || "",
|
|
507
|
-
namespace: service.metadata?.namespace || "",
|
|
508
|
-
type: service.spec?.type,
|
|
509
|
-
clusterIP: service.spec?.clusterIP,
|
|
510
|
-
ports: service.spec?.ports || [],
|
|
511
|
-
createdAt: service.metadata?.creationTimestamp,
|
|
512
|
-
}));
|
|
513
|
-
return {
|
|
514
|
-
content: [
|
|
515
|
-
{
|
|
516
|
-
type: "text",
|
|
517
|
-
text: JSON.stringify({ services }, null, 2),
|
|
518
|
-
},
|
|
519
|
-
],
|
|
520
|
-
};
|
|
57
|
+
return await listServices(k8sManager, input);
|
|
521
58
|
}
|
|
522
59
|
case "list_namespaces": {
|
|
523
60
|
const { body } = await k8sManager.getCoreApi().listNamespace();
|
|
@@ -536,161 +73,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
536
73
|
};
|
|
537
74
|
}
|
|
538
75
|
case "create_pod": {
|
|
539
|
-
|
|
540
|
-
const templateConfig = containerTemplates[createPodInput.template];
|
|
541
|
-
const pod = {
|
|
542
|
-
apiVersion: "v1",
|
|
543
|
-
kind: "Pod",
|
|
544
|
-
metadata: {
|
|
545
|
-
name: createPodInput.name,
|
|
546
|
-
namespace: createPodInput.namespace,
|
|
547
|
-
labels: {
|
|
548
|
-
"mcp-managed": "true",
|
|
549
|
-
app: createPodInput.name,
|
|
550
|
-
},
|
|
551
|
-
},
|
|
552
|
-
spec: {
|
|
553
|
-
containers: [
|
|
554
|
-
{
|
|
555
|
-
...templateConfig,
|
|
556
|
-
...(createPodInput.command && {
|
|
557
|
-
command: createPodInput.command,
|
|
558
|
-
args: undefined, // Clear default args when command is overridden
|
|
559
|
-
}),
|
|
560
|
-
},
|
|
561
|
-
],
|
|
562
|
-
},
|
|
563
|
-
};
|
|
564
|
-
const response = await k8sManager
|
|
565
|
-
.getCoreApi()
|
|
566
|
-
.createNamespacedPod(createPodInput.namespace, pod)
|
|
567
|
-
.catch((error) => {
|
|
568
|
-
console.error("Pod creation error:", {
|
|
569
|
-
status: error.response?.statusCode,
|
|
570
|
-
message: error.response?.body?.message || error.message,
|
|
571
|
-
details: error.response?.body,
|
|
572
|
-
});
|
|
573
|
-
throw error;
|
|
574
|
-
});
|
|
575
|
-
k8sManager.trackResource("Pod", createPodInput.name, createPodInput.namespace);
|
|
576
|
-
return {
|
|
577
|
-
content: [
|
|
578
|
-
{
|
|
579
|
-
type: "text",
|
|
580
|
-
text: JSON.stringify({
|
|
581
|
-
podName: response.body.metadata.name,
|
|
582
|
-
status: "created",
|
|
583
|
-
}, null, 2),
|
|
584
|
-
},
|
|
585
|
-
],
|
|
586
|
-
};
|
|
76
|
+
return await createPod(k8sManager, input);
|
|
587
77
|
}
|
|
588
78
|
case "delete_pod": {
|
|
589
|
-
|
|
590
|
-
try {
|
|
591
|
-
await k8sManager
|
|
592
|
-
.getCoreApi()
|
|
593
|
-
.deleteNamespacedPod(deletePodInput.name, deletePodInput.namespace);
|
|
594
|
-
return {
|
|
595
|
-
content: [
|
|
596
|
-
{
|
|
597
|
-
type: "text",
|
|
598
|
-
text: JSON.stringify({
|
|
599
|
-
success: true,
|
|
600
|
-
status: "deleted",
|
|
601
|
-
}, null, 2),
|
|
602
|
-
},
|
|
603
|
-
],
|
|
604
|
-
};
|
|
605
|
-
}
|
|
606
|
-
catch (error) {
|
|
607
|
-
if (deletePodInput.ignoreNotFound &&
|
|
608
|
-
error.response?.statusCode === 404) {
|
|
609
|
-
return {
|
|
610
|
-
content: [
|
|
611
|
-
{
|
|
612
|
-
type: "text",
|
|
613
|
-
text: JSON.stringify({
|
|
614
|
-
success: true,
|
|
615
|
-
status: "not_found",
|
|
616
|
-
}, null, 2),
|
|
617
|
-
},
|
|
618
|
-
],
|
|
619
|
-
};
|
|
620
|
-
}
|
|
621
|
-
throw error;
|
|
622
|
-
}
|
|
79
|
+
return await deletePod(k8sManager, input);
|
|
623
80
|
}
|
|
624
81
|
case "describe_pod": {
|
|
625
|
-
|
|
626
|
-
try {
|
|
627
|
-
const { body } = await k8sManager
|
|
628
|
-
.getCoreApi()
|
|
629
|
-
.readNamespacedPod(describePodInput.name, describePodInput.namespace);
|
|
630
|
-
if (!body) {
|
|
631
|
-
return {
|
|
632
|
-
content: [
|
|
633
|
-
{
|
|
634
|
-
type: "text",
|
|
635
|
-
text: JSON.stringify({
|
|
636
|
-
error: "Pod not found",
|
|
637
|
-
status: "not_found",
|
|
638
|
-
}, null, 2),
|
|
639
|
-
},
|
|
640
|
-
],
|
|
641
|
-
isError: true,
|
|
642
|
-
};
|
|
643
|
-
}
|
|
644
|
-
// Format the pod details for better readability
|
|
645
|
-
const podDetails = {
|
|
646
|
-
kind: body.kind,
|
|
647
|
-
metadata: {
|
|
648
|
-
name: body.metadata?.name,
|
|
649
|
-
namespace: body.metadata?.namespace,
|
|
650
|
-
creationTimestamp: body.metadata?.creationTimestamp,
|
|
651
|
-
labels: body.metadata?.labels,
|
|
652
|
-
},
|
|
653
|
-
spec: {
|
|
654
|
-
containers: body.spec?.containers.map((container) => ({
|
|
655
|
-
name: container.name,
|
|
656
|
-
image: container.image,
|
|
657
|
-
ports: container.ports,
|
|
658
|
-
resources: container.resources,
|
|
659
|
-
})),
|
|
660
|
-
nodeName: body.spec?.nodeName,
|
|
661
|
-
},
|
|
662
|
-
status: {
|
|
663
|
-
phase: body.status?.phase,
|
|
664
|
-
conditions: body.status?.conditions,
|
|
665
|
-
containerStatuses: body.status?.containerStatuses,
|
|
666
|
-
},
|
|
667
|
-
};
|
|
668
|
-
return {
|
|
669
|
-
content: [
|
|
670
|
-
{
|
|
671
|
-
type: "text",
|
|
672
|
-
text: JSON.stringify(podDetails, null, 2),
|
|
673
|
-
},
|
|
674
|
-
],
|
|
675
|
-
};
|
|
676
|
-
}
|
|
677
|
-
catch (error) {
|
|
678
|
-
if (error.response?.statusCode === 404) {
|
|
679
|
-
return {
|
|
680
|
-
content: [
|
|
681
|
-
{
|
|
682
|
-
type: "text",
|
|
683
|
-
text: JSON.stringify({
|
|
684
|
-
error: "Pod not found",
|
|
685
|
-
status: "not_found",
|
|
686
|
-
}, null, 2),
|
|
687
|
-
},
|
|
688
|
-
],
|
|
689
|
-
isError: true,
|
|
690
|
-
};
|
|
691
|
-
}
|
|
692
|
-
throw new McpError(ErrorCode.InternalError, `Failed to describe pod: ${error.response?.body?.message || error.message}`);
|
|
693
|
-
}
|
|
82
|
+
return await describePod(k8sManager, input);
|
|
694
83
|
}
|
|
695
84
|
case "cleanup": {
|
|
696
85
|
await k8sManager.cleanup();
|
|
@@ -706,181 +95,19 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
706
95
|
};
|
|
707
96
|
}
|
|
708
97
|
case "list_nodes": {
|
|
709
|
-
|
|
710
|
-
return {
|
|
711
|
-
content: [
|
|
712
|
-
{
|
|
713
|
-
type: "text",
|
|
714
|
-
text: JSON.stringify({
|
|
715
|
-
nodes: body.items,
|
|
716
|
-
}, null, 2),
|
|
717
|
-
},
|
|
718
|
-
],
|
|
719
|
-
};
|
|
98
|
+
return await listNodes(k8sManager);
|
|
720
99
|
}
|
|
721
100
|
case "get_logs": {
|
|
722
|
-
|
|
723
|
-
async function getPodLogs(podName, podNamespace) {
|
|
724
|
-
try {
|
|
725
|
-
const { body } = await k8sManager.getCoreApi().readNamespacedPodLog(podName, podNamespace, container, follow, undefined, // insecureSkipTLSVerifyBackend
|
|
726
|
-
undefined, // limitBytes
|
|
727
|
-
pretty ? "true" : "false", undefined, // previous
|
|
728
|
-
sinceSeconds, tail, timestamps);
|
|
729
|
-
return body;
|
|
730
|
-
}
|
|
731
|
-
catch (error) {
|
|
732
|
-
if (error.response?.statusCode === 404) {
|
|
733
|
-
throw new McpError(ErrorCode.InvalidRequest, `Pod ${podName} not found in namespace ${podNamespace}`);
|
|
734
|
-
}
|
|
735
|
-
// Log full error details
|
|
736
|
-
console.error("Full error:", {
|
|
737
|
-
statusCode: error.response?.statusCode,
|
|
738
|
-
message: error.response?.body?.message || error.message,
|
|
739
|
-
details: error.response?.body,
|
|
740
|
-
});
|
|
741
|
-
throw new McpError(ErrorCode.InternalError, `Failed to get logs for pod ${podName}: ${error.response?.body?.message || error.message}`);
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
const logs = {};
|
|
745
|
-
try {
|
|
746
|
-
// Get logs based on resource type
|
|
747
|
-
switch (resourceType.toLowerCase()) {
|
|
748
|
-
case "pod": {
|
|
749
|
-
if (!name) {
|
|
750
|
-
throw new McpError(ErrorCode.InvalidRequest, "Pod name is required when resourceType is 'pod'");
|
|
751
|
-
}
|
|
752
|
-
logs[name] = await getPodLogs(name, namespace);
|
|
753
|
-
break;
|
|
754
|
-
}
|
|
755
|
-
case "deployment": {
|
|
756
|
-
if (!name) {
|
|
757
|
-
throw new McpError(ErrorCode.InvalidRequest, "Deployment name is required when resourceType is 'deployment'");
|
|
758
|
-
}
|
|
759
|
-
const { body: deployment } = await k8sManager
|
|
760
|
-
.getAppsApi()
|
|
761
|
-
.readNamespacedDeployment(name, namespace);
|
|
762
|
-
if (!deployment.spec?.selector?.matchLabels) {
|
|
763
|
-
throw new McpError(ErrorCode.InvalidRequest, `Deployment ${name} has no selector`);
|
|
764
|
-
}
|
|
765
|
-
const selector = Object.entries(deployment.spec.selector.matchLabels)
|
|
766
|
-
.map(([key, value]) => `${key}=${value}`)
|
|
767
|
-
.join(",");
|
|
768
|
-
const { body: podList } = await k8sManager
|
|
769
|
-
.getCoreApi()
|
|
770
|
-
.listNamespacedPod(namespace, undefined, undefined, undefined, undefined, selector);
|
|
771
|
-
for (const pod of podList.items) {
|
|
772
|
-
if (pod.metadata?.name) {
|
|
773
|
-
logs[pod.metadata.name] = await getPodLogs(pod.metadata.name, namespace);
|
|
774
|
-
}
|
|
775
|
-
}
|
|
776
|
-
break;
|
|
777
|
-
}
|
|
778
|
-
case "job": {
|
|
779
|
-
if (!name) {
|
|
780
|
-
throw new McpError(ErrorCode.InvalidRequest, "Job name is required when resourceType is 'job'");
|
|
781
|
-
}
|
|
782
|
-
const { body: podList } = await k8sManager
|
|
783
|
-
.getCoreApi()
|
|
784
|
-
.listNamespacedPod(namespace, undefined, undefined, undefined, undefined, `job-name=${name}`);
|
|
785
|
-
for (const pod of podList.items) {
|
|
786
|
-
if (pod.metadata?.name) {
|
|
787
|
-
logs[pod.metadata.name] = await getPodLogs(pod.metadata.name, namespace);
|
|
788
|
-
}
|
|
789
|
-
}
|
|
790
|
-
break;
|
|
791
|
-
}
|
|
792
|
-
default:
|
|
793
|
-
throw new McpError(ErrorCode.InvalidRequest, `Unsupported resource type: ${resourceType}`);
|
|
794
|
-
}
|
|
795
|
-
// If labelSelector is provided, filter or add logs by label
|
|
796
|
-
if (labelSelector) {
|
|
797
|
-
const { body: labeledPods } = await k8sManager
|
|
798
|
-
.getCoreApi()
|
|
799
|
-
.listNamespacedPod(namespace, undefined, undefined, undefined, undefined, labelSelector);
|
|
800
|
-
for (const pod of labeledPods.items) {
|
|
801
|
-
if (pod.metadata?.name) {
|
|
802
|
-
logs[pod.metadata.name] = await getPodLogs(pod.metadata.name, namespace);
|
|
803
|
-
}
|
|
804
|
-
}
|
|
805
|
-
}
|
|
806
|
-
return {
|
|
807
|
-
content: [
|
|
808
|
-
{
|
|
809
|
-
type: "text",
|
|
810
|
-
text: JSON.stringify({ logs }, null, 2),
|
|
811
|
-
},
|
|
812
|
-
],
|
|
813
|
-
};
|
|
814
|
-
}
|
|
815
|
-
catch (error) {
|
|
816
|
-
if (error instanceof McpError)
|
|
817
|
-
throw error;
|
|
818
|
-
throw new McpError(ErrorCode.InternalError, `Failed to get logs: ${error}`);
|
|
819
|
-
}
|
|
101
|
+
return await getLogs(k8sManager, input);
|
|
820
102
|
}
|
|
821
103
|
case "install_helm_chart": {
|
|
822
|
-
|
|
823
|
-
let command = `helm install ${installInput.name} ${installInput.chart}`;
|
|
824
|
-
if (installInput.namespace) {
|
|
825
|
-
command += ` -n ${installInput.namespace}`;
|
|
826
|
-
}
|
|
827
|
-
if (installInput.values) {
|
|
828
|
-
const valuesFile = `${installInput.name}-values.yaml`;
|
|
829
|
-
await fs.writeFile(valuesFile, yaml.dump(installInput.values));
|
|
830
|
-
command += ` -f ${valuesFile}`;
|
|
831
|
-
}
|
|
832
|
-
if (installInput.version) {
|
|
833
|
-
command += ` --version ${installInput.version}`;
|
|
834
|
-
}
|
|
835
|
-
if (installInput.repo) {
|
|
836
|
-
command += ` --repo ${installInput.repo}`;
|
|
837
|
-
}
|
|
838
|
-
const result = await execCommand(command);
|
|
839
|
-
return {
|
|
840
|
-
content: [
|
|
841
|
-
{
|
|
842
|
-
type: "text",
|
|
843
|
-
text: JSON.stringify({ status: "installed", output: result }, null, 2),
|
|
844
|
-
},
|
|
845
|
-
],
|
|
846
|
-
};
|
|
847
|
-
}
|
|
848
|
-
case "uninstall_helm_chart": {
|
|
849
|
-
const uninstallInput = input;
|
|
850
|
-
let command = `helm uninstall ${uninstallInput.name}`;
|
|
851
|
-
if (uninstallInput.namespace) {
|
|
852
|
-
command += ` -n ${uninstallInput.namespace}`;
|
|
853
|
-
}
|
|
854
|
-
const result = await execCommand(command);
|
|
855
|
-
return {
|
|
856
|
-
content: [
|
|
857
|
-
{
|
|
858
|
-
type: "text",
|
|
859
|
-
text: JSON.stringify({ status: "uninstalled", output: result }, null, 2),
|
|
860
|
-
},
|
|
861
|
-
],
|
|
862
|
-
};
|
|
104
|
+
return await installHelmChart(input);
|
|
863
105
|
}
|
|
864
106
|
case "upgrade_helm_chart": {
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
if (upgradeInput.namespace) {
|
|
870
|
-
command += ` -n ${upgradeInput.namespace}`;
|
|
871
|
-
}
|
|
872
|
-
if (upgradeInput.repo) {
|
|
873
|
-
command += ` --repo ${upgradeInput.repo}`;
|
|
874
|
-
}
|
|
875
|
-
const result = await execCommand(command);
|
|
876
|
-
return {
|
|
877
|
-
content: [
|
|
878
|
-
{
|
|
879
|
-
type: "text",
|
|
880
|
-
text: JSON.stringify({ status: "upgraded", output: result }, null, 2),
|
|
881
|
-
},
|
|
882
|
-
],
|
|
883
|
-
};
|
|
107
|
+
return await upgradeHelmChart(input);
|
|
108
|
+
}
|
|
109
|
+
case "uninstall_helm_chart": {
|
|
110
|
+
return await uninstallHelmChart(input);
|
|
884
111
|
}
|
|
885
112
|
default:
|
|
886
113
|
throw new McpError(ErrorCode.InvalidRequest, `Unknown tool: ${name}`);
|
|
@@ -893,115 +120,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
893
120
|
}
|
|
894
121
|
});
|
|
895
122
|
// Resources handlers
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
{
|
|
900
|
-
uri: "k8s://default/pods",
|
|
901
|
-
name: "Kubernetes Pods",
|
|
902
|
-
mimeType: "application/json",
|
|
903
|
-
description: "List of pods in the default namespace",
|
|
904
|
-
},
|
|
905
|
-
{
|
|
906
|
-
uri: "k8s://default/deployments",
|
|
907
|
-
name: "Kubernetes Deployments",
|
|
908
|
-
mimeType: "application/json",
|
|
909
|
-
description: "List of deployments in the default namespace",
|
|
910
|
-
},
|
|
911
|
-
{
|
|
912
|
-
uri: "k8s://default/services",
|
|
913
|
-
name: "Kubernetes Services",
|
|
914
|
-
mimeType: "application/json",
|
|
915
|
-
description: "List of services in the default namespace",
|
|
916
|
-
},
|
|
917
|
-
{
|
|
918
|
-
uri: "k8s://namespaces",
|
|
919
|
-
name: "Kubernetes Namespaces",
|
|
920
|
-
mimeType: "application/json",
|
|
921
|
-
description: "List of all namespaces",
|
|
922
|
-
},
|
|
923
|
-
{
|
|
924
|
-
uri: "k8s://nodes",
|
|
925
|
-
name: "Kubernetes Nodes",
|
|
926
|
-
mimeType: "application/json",
|
|
927
|
-
description: "List of all nodes in the cluster",
|
|
928
|
-
},
|
|
929
|
-
],
|
|
930
|
-
};
|
|
931
|
-
});
|
|
932
|
-
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
933
|
-
try {
|
|
934
|
-
const uri = request.params.uri;
|
|
935
|
-
const parts = uri.replace("k8s://", "").split("/");
|
|
936
|
-
const isNamespaces = parts[0] === "namespaces";
|
|
937
|
-
const isNodes = parts[0] === "nodes";
|
|
938
|
-
if ((isNamespaces || isNodes) && parts.length === 1) {
|
|
939
|
-
const fn = isNodes ? "listNode" : "listNamespace";
|
|
940
|
-
const { body } = await k8sManager.getCoreApi()[fn]();
|
|
941
|
-
return {
|
|
942
|
-
contents: [
|
|
943
|
-
{
|
|
944
|
-
uri: request.params.uri,
|
|
945
|
-
mimeType: "application/json",
|
|
946
|
-
text: JSON.stringify(body.items, null, 2),
|
|
947
|
-
},
|
|
948
|
-
],
|
|
949
|
-
};
|
|
950
|
-
}
|
|
951
|
-
const [namespace, resourceType] = parts;
|
|
952
|
-
switch (resourceType) {
|
|
953
|
-
case "pods": {
|
|
954
|
-
const { body } = await k8sManager
|
|
955
|
-
.getCoreApi()
|
|
956
|
-
.listNamespacedPod(namespace);
|
|
957
|
-
return {
|
|
958
|
-
contents: [
|
|
959
|
-
{
|
|
960
|
-
uri: request.params.uri,
|
|
961
|
-
mimeType: "application/json",
|
|
962
|
-
text: JSON.stringify(body.items, null, 2),
|
|
963
|
-
},
|
|
964
|
-
],
|
|
965
|
-
};
|
|
966
|
-
}
|
|
967
|
-
case "deployments": {
|
|
968
|
-
const { body } = await k8sManager
|
|
969
|
-
.getAppsApi()
|
|
970
|
-
.listNamespacedDeployment(namespace);
|
|
971
|
-
return {
|
|
972
|
-
contents: [
|
|
973
|
-
{
|
|
974
|
-
uri: request.params.uri,
|
|
975
|
-
mimeType: "application/json",
|
|
976
|
-
text: JSON.stringify(body.items, null, 2),
|
|
977
|
-
},
|
|
978
|
-
],
|
|
979
|
-
};
|
|
980
|
-
}
|
|
981
|
-
case "services": {
|
|
982
|
-
const { body } = await k8sManager
|
|
983
|
-
.getCoreApi()
|
|
984
|
-
.listNamespacedService(namespace);
|
|
985
|
-
return {
|
|
986
|
-
contents: [
|
|
987
|
-
{
|
|
988
|
-
uri: request.params.uri,
|
|
989
|
-
mimeType: "application/json",
|
|
990
|
-
text: JSON.stringify(body.items, null, 2),
|
|
991
|
-
},
|
|
992
|
-
],
|
|
993
|
-
};
|
|
994
|
-
}
|
|
995
|
-
default:
|
|
996
|
-
throw new McpError(ErrorCode.InvalidRequest, `Unsupported resource type: ${resourceType}`);
|
|
997
|
-
}
|
|
998
|
-
}
|
|
999
|
-
catch (error) {
|
|
1000
|
-
if (error instanceof McpError)
|
|
1001
|
-
throw error;
|
|
1002
|
-
throw new McpError(ErrorCode.InternalError, `Failed to read resource: ${error}`);
|
|
1003
|
-
}
|
|
1004
|
-
});
|
|
123
|
+
const resourceHandlers = getResourceHandlers(k8sManager);
|
|
124
|
+
server.setRequestHandler(ListResourcesRequestSchema, resourceHandlers.listResources);
|
|
125
|
+
server.setRequestHandler(ReadResourceRequestSchema, resourceHandlers.readResource);
|
|
1005
126
|
const transport = new StdioServerTransport();
|
|
1006
127
|
await server.connect(transport);
|
|
1007
128
|
["SIGINT", "SIGTERM"].forEach((signal) => {
|