mcp-server-kubernetes 0.1.4 → 0.1.5
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 +9 -1
- package/dist/helm.test.d.ts +1 -0
- package/dist/helm.test.js +208 -0
- package/dist/index.js +144 -0
- package/dist/types.d.ts +77 -0
- package/dist/types.js +26 -0
- package/dist/unit.test.js +6 -2
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -24,6 +24,7 @@ The server will automatically connect to your current kubectl context. Make sure
|
|
|
24
24
|
1. kubectl installed and in your PATH
|
|
25
25
|
2. A valid kubeconfig file with contexts configured
|
|
26
26
|
3. Access to a Kubernetes cluster configured for kubectl (e.g. minikube, Rancher Desktop, GKE, etc.)
|
|
27
|
+
4. Helm v3 installed and in your PATH (no Tiller required)
|
|
27
28
|
|
|
28
29
|
You can verify your connection by asking Claude to list your pods or create a test deployment.
|
|
29
30
|
|
|
@@ -41,9 +42,15 @@ If you have errors, open up a standard terminal and run `kubectl get pods` to se
|
|
|
41
42
|
- [x] Describe a pod
|
|
42
43
|
- [x] List all namespaces
|
|
43
44
|
- [x] Get logs from a pod for debugging (supports pods, deployments, jobs, and label selectors)
|
|
45
|
+
- [x] Support Helm v3 for installing charts
|
|
46
|
+
- Install charts with custom values
|
|
47
|
+
- Uninstall releases
|
|
48
|
+
- Upgrade existing releases
|
|
49
|
+
- Support for namespaces
|
|
50
|
+
- Support for version specification
|
|
51
|
+
- Support for custom repositories
|
|
44
52
|
- [ ] Port forward to a pod
|
|
45
53
|
- [ ] Choose namespace for next commands (memory)
|
|
46
|
-
- [ ] Support Helm for installing charts
|
|
47
54
|
|
|
48
55
|
## Local Development
|
|
49
56
|
|
|
@@ -86,6 +93,7 @@ npx @modelcontextprotocol/inspector node build/index.js
|
|
|
86
93
|
src/
|
|
87
94
|
├── index.ts # Main server implementation
|
|
88
95
|
├── types.ts # TypeScript type definitions
|
|
96
|
+
├── helm.test.ts # Helm chart installation tests
|
|
89
97
|
└── unit.test.ts # Unit tests
|
|
90
98
|
```
|
|
91
99
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { expect, test, describe, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
3
|
+
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
4
|
+
import { HelmResponseSchema } from "./types.js";
|
|
5
|
+
import * as fs from "fs";
|
|
6
|
+
async function sleep(ms) {
|
|
7
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
8
|
+
}
|
|
9
|
+
describe("helm operations", () => {
|
|
10
|
+
let transport;
|
|
11
|
+
let client;
|
|
12
|
+
const testReleaseName = "test-nginx";
|
|
13
|
+
const testNamespace = "default";
|
|
14
|
+
beforeEach(async () => {
|
|
15
|
+
try {
|
|
16
|
+
transport = new StdioClientTransport({
|
|
17
|
+
command: "bun",
|
|
18
|
+
args: ["src/index.ts"],
|
|
19
|
+
stderr: "pipe",
|
|
20
|
+
});
|
|
21
|
+
client = new Client({
|
|
22
|
+
name: "test-client",
|
|
23
|
+
version: "1.0.0",
|
|
24
|
+
}, {
|
|
25
|
+
capabilities: {},
|
|
26
|
+
});
|
|
27
|
+
await client.connect(transport);
|
|
28
|
+
await sleep(1000);
|
|
29
|
+
}
|
|
30
|
+
catch (e) {
|
|
31
|
+
console.error("Error in beforeEach:", e);
|
|
32
|
+
throw e;
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
afterEach(async () => {
|
|
36
|
+
try {
|
|
37
|
+
// Cleanup: Uninstall the test release if it exists
|
|
38
|
+
await client
|
|
39
|
+
.request({
|
|
40
|
+
method: "tools/call",
|
|
41
|
+
params: {
|
|
42
|
+
name: "uninstall_helm_chart",
|
|
43
|
+
arguments: {
|
|
44
|
+
name: testReleaseName,
|
|
45
|
+
namespace: testNamespace,
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
}, HelmResponseSchema)
|
|
49
|
+
.catch(() => { }); // Ignore errors if release doesn't exist
|
|
50
|
+
await transport.close();
|
|
51
|
+
await sleep(1000);
|
|
52
|
+
// Cleanup generated values files
|
|
53
|
+
if (fs.existsSync("test-nginx-values.yaml")) {
|
|
54
|
+
fs.unlinkSync("test-nginx-values.yaml");
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch (e) {
|
|
58
|
+
console.error("Error during cleanup:", e);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
test("install helm chart", async () => {
|
|
62
|
+
const installResult = await client.request({
|
|
63
|
+
method: "tools/call",
|
|
64
|
+
params: {
|
|
65
|
+
name: "install_helm_chart",
|
|
66
|
+
arguments: {
|
|
67
|
+
name: testReleaseName,
|
|
68
|
+
chart: "nginx",
|
|
69
|
+
repo: "https://charts.bitnami.com/bitnami",
|
|
70
|
+
namespace: testNamespace,
|
|
71
|
+
values: {
|
|
72
|
+
service: {
|
|
73
|
+
type: "ClusterIP",
|
|
74
|
+
},
|
|
75
|
+
resources: {
|
|
76
|
+
limits: {
|
|
77
|
+
cpu: "100m",
|
|
78
|
+
memory: "128Mi",
|
|
79
|
+
},
|
|
80
|
+
requests: {
|
|
81
|
+
cpu: "50m",
|
|
82
|
+
memory: "64Mi",
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
}, HelmResponseSchema);
|
|
89
|
+
expect(installResult.content[0].type).toBe("text");
|
|
90
|
+
const result = JSON.parse(installResult.content[0].text);
|
|
91
|
+
expect(result.status).toBe("installed");
|
|
92
|
+
// Wait for the deployment to be ready
|
|
93
|
+
await sleep(5000);
|
|
94
|
+
// Verify the deployment exists
|
|
95
|
+
const deploymentResult = await client.request({
|
|
96
|
+
method: "tools/call",
|
|
97
|
+
params: {
|
|
98
|
+
name: "list_deployments",
|
|
99
|
+
arguments: {
|
|
100
|
+
namespace: testNamespace,
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
}, HelmResponseSchema);
|
|
104
|
+
const deployments = JSON.parse(deploymentResult.content[0].text);
|
|
105
|
+
expect(deployments.deployments.some((d) => d.name.startsWith(testReleaseName))).toBe(true);
|
|
106
|
+
}, 30000); // Increase timeout to 30s for chart installation
|
|
107
|
+
test("upgrade helm chart values", async () => {
|
|
108
|
+
// First install the chart
|
|
109
|
+
await client.request({
|
|
110
|
+
method: "tools/call",
|
|
111
|
+
params: {
|
|
112
|
+
name: "install_helm_chart",
|
|
113
|
+
arguments: {
|
|
114
|
+
name: testReleaseName,
|
|
115
|
+
chart: "nginx",
|
|
116
|
+
repo: "https://charts.bitnami.com/bitnami",
|
|
117
|
+
namespace: testNamespace,
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
}, HelmResponseSchema);
|
|
121
|
+
await sleep(5000);
|
|
122
|
+
// Then upgrade it with new values
|
|
123
|
+
const upgradeResult = await client.request({
|
|
124
|
+
method: "tools/call",
|
|
125
|
+
params: {
|
|
126
|
+
name: "upgrade_helm_chart",
|
|
127
|
+
arguments: {
|
|
128
|
+
name: testReleaseName,
|
|
129
|
+
chart: "nginx",
|
|
130
|
+
repo: "https://charts.bitnami.com/bitnami",
|
|
131
|
+
namespace: testNamespace,
|
|
132
|
+
values: {
|
|
133
|
+
replicaCount: 2,
|
|
134
|
+
resources: {
|
|
135
|
+
limits: {
|
|
136
|
+
cpu: "200m",
|
|
137
|
+
memory: "256Mi",
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
}, HelmResponseSchema);
|
|
144
|
+
expect(upgradeResult.content[0].type).toBe("text");
|
|
145
|
+
const result = JSON.parse(upgradeResult.content[0].text);
|
|
146
|
+
expect(result.status).toBe("upgraded");
|
|
147
|
+
// Wait for the upgrade to take effect
|
|
148
|
+
await sleep(5000);
|
|
149
|
+
// Verify the deployment was updated
|
|
150
|
+
const deploymentResult = await client.request({
|
|
151
|
+
method: "tools/call",
|
|
152
|
+
params: {
|
|
153
|
+
name: "list_deployments",
|
|
154
|
+
arguments: {
|
|
155
|
+
namespace: testNamespace,
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
}, HelmResponseSchema);
|
|
159
|
+
const deployments = JSON.parse(deploymentResult.content[0].text);
|
|
160
|
+
const nginxDeployment = deployments.deployments.find((d) => d.name.startsWith(testReleaseName));
|
|
161
|
+
expect(nginxDeployment).toBeDefined();
|
|
162
|
+
expect(nginxDeployment.replicas).toBe(2);
|
|
163
|
+
}, 60000); // Increase timeout to 60s for install + upgrade
|
|
164
|
+
test("uninstall helm chart", async () => {
|
|
165
|
+
// First install the chart
|
|
166
|
+
await client.request({
|
|
167
|
+
method: "tools/call",
|
|
168
|
+
params: {
|
|
169
|
+
name: "install_helm_chart",
|
|
170
|
+
arguments: {
|
|
171
|
+
name: testReleaseName,
|
|
172
|
+
chart: "nginx",
|
|
173
|
+
repo: "https://charts.bitnami.com/bitnami",
|
|
174
|
+
namespace: testNamespace,
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
}, HelmResponseSchema);
|
|
178
|
+
await sleep(5000);
|
|
179
|
+
// Then uninstall it
|
|
180
|
+
const uninstallResult = await client.request({
|
|
181
|
+
method: "tools/call",
|
|
182
|
+
params: {
|
|
183
|
+
name: "uninstall_helm_chart",
|
|
184
|
+
arguments: {
|
|
185
|
+
name: testReleaseName,
|
|
186
|
+
namespace: testNamespace,
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
}, HelmResponseSchema);
|
|
190
|
+
expect(uninstallResult.content[0].type).toBe("text");
|
|
191
|
+
const result = JSON.parse(uninstallResult.content[0].text);
|
|
192
|
+
expect(result.status).toBe("uninstalled");
|
|
193
|
+
// Wait for resources to be cleaned up
|
|
194
|
+
await sleep(5000);
|
|
195
|
+
// Verify the deployment is gone
|
|
196
|
+
const deploymentResult = await client.request({
|
|
197
|
+
method: "tools/call",
|
|
198
|
+
params: {
|
|
199
|
+
name: "list_deployments",
|
|
200
|
+
arguments: {
|
|
201
|
+
namespace: testNamespace,
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
}, HelmResponseSchema);
|
|
205
|
+
const deployments = JSON.parse(deploymentResult.content[0].text);
|
|
206
|
+
expect(deployments.deployments.every((d) => !d.name.startsWith(testReleaseName))).toBe(true);
|
|
207
|
+
}, 60000); // Increase timeout to 60s for install + uninstall
|
|
208
|
+
});
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,9 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import { ListResourcesRequestSchema, ReadResourceRequestSchema, ListToolsRequestSchema, CallToolRequestSchema, ErrorCode, McpError, } from "@modelcontextprotocol/sdk/types.js";
|
|
5
5
|
import * as k8s from "@kubernetes/client-node";
|
|
6
|
+
import * as fs from "fs/promises";
|
|
7
|
+
import * as yaml from "js-yaml";
|
|
8
|
+
import { exec } from "child_process";
|
|
6
9
|
class KubernetesManager {
|
|
7
10
|
resources = [];
|
|
8
11
|
portForwards = [];
|
|
@@ -185,6 +188,18 @@ const server = new Server({
|
|
|
185
188
|
tools: {},
|
|
186
189
|
},
|
|
187
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
|
+
}
|
|
188
203
|
// Tools handlers
|
|
189
204
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
190
205
|
return {
|
|
@@ -365,6 +380,71 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
365
380
|
required: ["resourceType"],
|
|
366
381
|
},
|
|
367
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
|
+
},
|
|
368
448
|
],
|
|
369
449
|
};
|
|
370
450
|
});
|
|
@@ -738,6 +818,70 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
738
818
|
throw new McpError(ErrorCode.InternalError, `Failed to get logs: ${error}`);
|
|
739
819
|
}
|
|
740
820
|
}
|
|
821
|
+
case "install_helm_chart": {
|
|
822
|
+
const installInput = input;
|
|
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
|
+
};
|
|
863
|
+
}
|
|
864
|
+
case "upgrade_helm_chart": {
|
|
865
|
+
const upgradeInput = input;
|
|
866
|
+
const valuesFile = `${upgradeInput.name}-values.yaml`;
|
|
867
|
+
await fs.writeFile(valuesFile, yaml.dump(upgradeInput.values));
|
|
868
|
+
let command = `helm upgrade ${upgradeInput.name} ${upgradeInput.chart} -f ${valuesFile}`;
|
|
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
|
+
};
|
|
884
|
+
}
|
|
741
885
|
default:
|
|
742
886
|
throw new McpError(ErrorCode.InvalidRequest, `Unknown tool: ${name}`);
|
|
743
887
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -354,3 +354,80 @@ export interface WatchTracker {
|
|
|
354
354
|
resourceType: string;
|
|
355
355
|
namespace: string;
|
|
356
356
|
}
|
|
357
|
+
export declare const HelmInstallRequestSchema: z.ZodObject<{
|
|
358
|
+
name: z.ZodString;
|
|
359
|
+
chart: z.ZodString;
|
|
360
|
+
namespace: z.ZodOptional<z.ZodString>;
|
|
361
|
+
values: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
|
|
362
|
+
version: z.ZodOptional<z.ZodString>;
|
|
363
|
+
repo: z.ZodOptional<z.ZodString>;
|
|
364
|
+
}, "strip", z.ZodTypeAny, {
|
|
365
|
+
name: string;
|
|
366
|
+
chart: string;
|
|
367
|
+
values?: Record<string, any> | undefined;
|
|
368
|
+
namespace?: string | undefined;
|
|
369
|
+
version?: string | undefined;
|
|
370
|
+
repo?: string | undefined;
|
|
371
|
+
}, {
|
|
372
|
+
name: string;
|
|
373
|
+
chart: string;
|
|
374
|
+
values?: Record<string, any> | undefined;
|
|
375
|
+
namespace?: string | undefined;
|
|
376
|
+
version?: string | undefined;
|
|
377
|
+
repo?: string | undefined;
|
|
378
|
+
}>;
|
|
379
|
+
export declare const HelmUninstallRequestSchema: z.ZodObject<{
|
|
380
|
+
name: z.ZodString;
|
|
381
|
+
namespace: z.ZodOptional<z.ZodString>;
|
|
382
|
+
}, "strip", z.ZodTypeAny, {
|
|
383
|
+
name: string;
|
|
384
|
+
namespace?: string | undefined;
|
|
385
|
+
}, {
|
|
386
|
+
name: string;
|
|
387
|
+
namespace?: string | undefined;
|
|
388
|
+
}>;
|
|
389
|
+
export declare const HelmUpgradeRequestSchema: z.ZodObject<{
|
|
390
|
+
name: z.ZodString;
|
|
391
|
+
chart: z.ZodString;
|
|
392
|
+
repo: z.ZodOptional<z.ZodString>;
|
|
393
|
+
values: z.ZodRecord<z.ZodString, z.ZodAny>;
|
|
394
|
+
namespace: z.ZodOptional<z.ZodString>;
|
|
395
|
+
}, "strip", z.ZodTypeAny, {
|
|
396
|
+
name: string;
|
|
397
|
+
values: Record<string, any>;
|
|
398
|
+
chart: string;
|
|
399
|
+
namespace?: string | undefined;
|
|
400
|
+
repo?: string | undefined;
|
|
401
|
+
}, {
|
|
402
|
+
name: string;
|
|
403
|
+
values: Record<string, any>;
|
|
404
|
+
chart: string;
|
|
405
|
+
namespace?: string | undefined;
|
|
406
|
+
repo?: string | undefined;
|
|
407
|
+
}>;
|
|
408
|
+
export declare const HelmResponseSchema: z.ZodObject<{
|
|
409
|
+
content: z.ZodArray<z.ZodObject<{
|
|
410
|
+
type: z.ZodLiteral<"text">;
|
|
411
|
+
text: z.ZodString;
|
|
412
|
+
}, "strip", z.ZodTypeAny, {
|
|
413
|
+
type: "text";
|
|
414
|
+
text: string;
|
|
415
|
+
}, {
|
|
416
|
+
type: "text";
|
|
417
|
+
text: string;
|
|
418
|
+
}>, "many">;
|
|
419
|
+
}, "strip", z.ZodTypeAny, {
|
|
420
|
+
content: {
|
|
421
|
+
type: "text";
|
|
422
|
+
text: string;
|
|
423
|
+
}[];
|
|
424
|
+
}, {
|
|
425
|
+
content: {
|
|
426
|
+
type: "text";
|
|
427
|
+
text: string;
|
|
428
|
+
}[];
|
|
429
|
+
}>;
|
|
430
|
+
export type HelmInstallRequest = z.infer<typeof HelmInstallRequestSchema>;
|
|
431
|
+
export type HelmUninstallRequest = z.infer<typeof HelmUninstallRequestSchema>;
|
|
432
|
+
export type HelmUpgradeRequest = z.infer<typeof HelmUpgradeRequestSchema>;
|
|
433
|
+
export type HelmResponse = z.infer<typeof HelmResponseSchema>;
|
package/dist/types.js
CHANGED
|
@@ -92,3 +92,29 @@ export const ReadResourceResponseSchema = z.object({
|
|
|
92
92
|
text: z.string(),
|
|
93
93
|
})),
|
|
94
94
|
});
|
|
95
|
+
// Helm-related types
|
|
96
|
+
export const HelmInstallRequestSchema = z.object({
|
|
97
|
+
name: z.string(),
|
|
98
|
+
chart: z.string(),
|
|
99
|
+
namespace: z.string().optional(),
|
|
100
|
+
values: z.record(z.any()).optional(),
|
|
101
|
+
version: z.string().optional(),
|
|
102
|
+
repo: z.string().optional(),
|
|
103
|
+
});
|
|
104
|
+
export const HelmUninstallRequestSchema = z.object({
|
|
105
|
+
name: z.string(),
|
|
106
|
+
namespace: z.string().optional(),
|
|
107
|
+
});
|
|
108
|
+
export const HelmUpgradeRequestSchema = z.object({
|
|
109
|
+
name: z.string(),
|
|
110
|
+
chart: z.string(),
|
|
111
|
+
repo: z.string().optional(),
|
|
112
|
+
values: z.record(z.any()),
|
|
113
|
+
namespace: z.string().optional(),
|
|
114
|
+
});
|
|
115
|
+
export const HelmResponseSchema = z.object({
|
|
116
|
+
content: z.array(z.object({
|
|
117
|
+
type: z.literal("text"),
|
|
118
|
+
text: z.string(),
|
|
119
|
+
})),
|
|
120
|
+
});
|
package/dist/unit.test.js
CHANGED
|
@@ -184,7 +184,11 @@ describe("kubernetes server operations", () => {
|
|
|
184
184
|
name: podName,
|
|
185
185
|
namespace: "default",
|
|
186
186
|
template: "busybox",
|
|
187
|
-
command: [
|
|
187
|
+
command: [
|
|
188
|
+
"/bin/sh",
|
|
189
|
+
"-c",
|
|
190
|
+
"echo Pod is running && sleep infinity",
|
|
191
|
+
],
|
|
188
192
|
},
|
|
189
193
|
},
|
|
190
194
|
}, CreatePodResponseSchema);
|
|
@@ -285,5 +289,5 @@ describe("kubernetes server operations", () => {
|
|
|
285
289
|
// Ignore any errors during termination check
|
|
286
290
|
console.log(`Error checking pod termination status: ${error}`);
|
|
287
291
|
}
|
|
288
|
-
});
|
|
292
|
+
}, { timeout: 120000 });
|
|
289
293
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-server-kubernetes",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "MCP server for interacting with Kubernetes clusters via kubectl",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -36,9 +36,11 @@
|
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@kubernetes/client-node": "^0.20.0",
|
|
38
38
|
"@modelcontextprotocol/sdk": "1.0.1",
|
|
39
|
+
"js-yaml": "^4.1.0",
|
|
39
40
|
"zod": "^3.22.4"
|
|
40
41
|
},
|
|
41
42
|
"devDependencies": {
|
|
43
|
+
"@types/js-yaml": "^4.0.9",
|
|
42
44
|
"@types/node": "^22.9.3",
|
|
43
45
|
"shx": "^0.3.4",
|
|
44
46
|
"typescript": "^5.6.2",
|