mcp-server-kubernetes 0.3.0 → 0.3.1
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 +31 -0
- package/dist/index.js +49 -44
- package/dist/models/response-schemas.d.ts +22 -0
- package/dist/models/response-schemas.js +3 -0
- package/dist/tools/get_events.d.ts +28 -0
- package/dist/tools/get_events.js +66 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -6,6 +6,8 @@ https://github.com/user-attachments/assets/f25f8f4e-4d04-479b-9ae0-5dac452dd2ed
|
|
|
6
6
|
|
|
7
7
|
<a href="https://glama.ai/mcp/servers/w71ieamqrt"><img width="380" height="200" src="https://glama.ai/mcp/servers/w71ieamqrt/badge" /></a>
|
|
8
8
|
|
|
9
|
+
[](https://smithery.ai/server/mcp-server-kubernetes)
|
|
10
|
+
|
|
9
11
|
## Usage with Claude Desktop
|
|
10
12
|
|
|
11
13
|
```json
|
|
@@ -30,6 +32,28 @@ You can verify your connection by asking Claude to list your pods or create a te
|
|
|
30
32
|
|
|
31
33
|
If you have errors open up a standard terminal and run `kubectl get pods` to see if you can connect to your cluster without credentials issues.
|
|
32
34
|
|
|
35
|
+
## Usage with mcp-chat
|
|
36
|
+
|
|
37
|
+
[mcp-chat](https://github.com/Flux159/mcp-chat) is a CLI chat client for MCP servers. You can use it to interact with the Kubernetes server.
|
|
38
|
+
|
|
39
|
+
```shell
|
|
40
|
+
npx mcp-chat --server "npx mcp-server-kubernetes"
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Alternatively, pass it your existing Claude Desktop configuration file from above (Linux should pass the correct path to config):
|
|
44
|
+
|
|
45
|
+
Mac:
|
|
46
|
+
|
|
47
|
+
```shell
|
|
48
|
+
npx mcp-chat --config "~/Library/Application Support/Claude/claude_desktop_config.json"
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Windows:
|
|
52
|
+
|
|
53
|
+
```shell
|
|
54
|
+
npx mcp-chat --config "%APPDATA%\Claude\claude_desktop_config.json"
|
|
55
|
+
```
|
|
56
|
+
|
|
33
57
|
## Features
|
|
34
58
|
|
|
35
59
|
- [x] Connect to a Kubernetes cluster
|
|
@@ -50,6 +74,7 @@ If you have errors open up a standard terminal and run `kubectl get pods` to see
|
|
|
50
74
|
- Support for version specification
|
|
51
75
|
- Support for custom repositories
|
|
52
76
|
- [x] kubectl explain and kubectl api-resources support
|
|
77
|
+
- [x] Get Kubernetes events from the cluster
|
|
53
78
|
- [ ] Port forward to a pod
|
|
54
79
|
- [ ] Choose namespace for next commands (memory)
|
|
55
80
|
|
|
@@ -88,6 +113,12 @@ npx @modelcontextprotocol/inspector node build/index.js
|
|
|
88
113
|
# Follow further instructions on terminal for Inspector link
|
|
89
114
|
```
|
|
90
115
|
|
|
116
|
+
5. Local testing with [mcp-chat](https://github.com/Flux159/mcp-chat)
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
npm run chat
|
|
120
|
+
```
|
|
121
|
+
|
|
91
122
|
### Project Structure
|
|
92
123
|
|
|
93
124
|
```
|
package/dist/index.js
CHANGED
|
@@ -11,6 +11,7 @@ import { createPod, createPodSchema } from "./tools/create_pod.js";
|
|
|
11
11
|
import { deletePod, deletePodSchema } from "./tools/delete_pod.js";
|
|
12
12
|
import { describePod, describePodSchema } from "./tools/describe_pod.js";
|
|
13
13
|
import { getLogs, getLogsSchema } from "./tools/get_logs.js";
|
|
14
|
+
import { getEvents, getEventsSchema } from "./tools/get_events.js";
|
|
14
15
|
import { getResourceHandlers } from "./resources/handlers.js";
|
|
15
16
|
import { ListResourcesRequestSchema, ReadResourceRequestSchema, ListToolsRequestSchema, CallToolRequestSchema, ErrorCode, McpError, } from "@modelcontextprotocol/sdk/types.js";
|
|
16
17
|
import { KubernetesManager } from "./types.js";
|
|
@@ -27,22 +28,23 @@ const server = new Server({
|
|
|
27
28
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
28
29
|
return {
|
|
29
30
|
tools: [
|
|
30
|
-
|
|
31
|
-
listDeploymentsSchema,
|
|
32
|
-
listServicesSchema,
|
|
33
|
-
listNamespacesSchema,
|
|
34
|
-
createPodSchema,
|
|
31
|
+
cleanupSchema,
|
|
35
32
|
createDeploymentSchema,
|
|
33
|
+
createPodSchema,
|
|
36
34
|
deletePodSchema,
|
|
37
35
|
describePodSchema,
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
explainResourceSchema,
|
|
37
|
+
getEventsSchema,
|
|
40
38
|
getLogsSchema,
|
|
41
39
|
installHelmChartSchema,
|
|
42
|
-
upgradeHelmChartSchema,
|
|
43
|
-
uninstallHelmChartSchema,
|
|
44
|
-
explainResourceSchema,
|
|
45
40
|
listApiResourcesSchema,
|
|
41
|
+
listDeploymentsSchema,
|
|
42
|
+
listNamespacesSchema,
|
|
43
|
+
listNodesSchema,
|
|
44
|
+
listPodsSchema,
|
|
45
|
+
listServicesSchema,
|
|
46
|
+
uninstallHelmChartSchema,
|
|
47
|
+
upgradeHelmChartSchema,
|
|
46
48
|
],
|
|
47
49
|
};
|
|
48
50
|
});
|
|
@@ -50,27 +52,15 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
50
52
|
try {
|
|
51
53
|
const { name, arguments: input = {} } = request.params;
|
|
52
54
|
switch (name) {
|
|
53
|
-
case "
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
case "list_deployments": {
|
|
57
|
-
return await listDeployments(k8sManager, input);
|
|
58
|
-
}
|
|
59
|
-
case "list_services": {
|
|
60
|
-
return await listServices(k8sManager, input);
|
|
61
|
-
}
|
|
62
|
-
case "list_namespaces": {
|
|
63
|
-
const { body } = await k8sManager.getCoreApi().listNamespace();
|
|
64
|
-
const namespaces = body.items.map((ns) => ({
|
|
65
|
-
name: ns.metadata?.name || "",
|
|
66
|
-
status: ns.status?.phase || "",
|
|
67
|
-
createdAt: ns.metadata?.creationTimestamp,
|
|
68
|
-
}));
|
|
55
|
+
case "cleanup": {
|
|
56
|
+
await k8sManager.cleanup();
|
|
69
57
|
return {
|
|
70
58
|
content: [
|
|
71
59
|
{
|
|
72
60
|
type: "text",
|
|
73
|
-
text: JSON.stringify({
|
|
61
|
+
text: JSON.stringify({
|
|
62
|
+
success: true,
|
|
63
|
+
}, null, 2),
|
|
74
64
|
},
|
|
75
65
|
],
|
|
76
66
|
};
|
|
@@ -84,15 +74,36 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
84
74
|
case "describe_pod": {
|
|
85
75
|
return await describePod(k8sManager, input);
|
|
86
76
|
}
|
|
87
|
-
case "
|
|
88
|
-
await
|
|
77
|
+
case "explain_resource": {
|
|
78
|
+
return await explainResource(input);
|
|
79
|
+
}
|
|
80
|
+
case "get_events": {
|
|
81
|
+
return await getEvents(k8sManager, input);
|
|
82
|
+
}
|
|
83
|
+
case "get_logs": {
|
|
84
|
+
return await getLogs(k8sManager, input);
|
|
85
|
+
}
|
|
86
|
+
case "install_helm_chart": {
|
|
87
|
+
return await installHelmChart(input);
|
|
88
|
+
}
|
|
89
|
+
case "list_api_resources": {
|
|
90
|
+
return await listApiResources(input);
|
|
91
|
+
}
|
|
92
|
+
case "list_deployments": {
|
|
93
|
+
return await listDeployments(k8sManager, input);
|
|
94
|
+
}
|
|
95
|
+
case "list_namespaces": {
|
|
96
|
+
const { body } = await k8sManager.getCoreApi().listNamespace();
|
|
97
|
+
const namespaces = body.items.map((ns) => ({
|
|
98
|
+
name: ns.metadata?.name || "",
|
|
99
|
+
status: ns.status?.phase || "",
|
|
100
|
+
createdAt: ns.metadata?.creationTimestamp,
|
|
101
|
+
}));
|
|
89
102
|
return {
|
|
90
103
|
content: [
|
|
91
104
|
{
|
|
92
105
|
type: "text",
|
|
93
|
-
text: JSON.stringify({
|
|
94
|
-
success: true,
|
|
95
|
-
}, null, 2),
|
|
106
|
+
text: JSON.stringify({ namespaces }, null, 2),
|
|
96
107
|
},
|
|
97
108
|
],
|
|
98
109
|
};
|
|
@@ -100,23 +111,17 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
100
111
|
case "list_nodes": {
|
|
101
112
|
return await listNodes(k8sManager);
|
|
102
113
|
}
|
|
103
|
-
case "
|
|
104
|
-
return await
|
|
105
|
-
}
|
|
106
|
-
case "install_helm_chart": {
|
|
107
|
-
return await installHelmChart(input);
|
|
114
|
+
case "list_pods": {
|
|
115
|
+
return await listPods(k8sManager, input);
|
|
108
116
|
}
|
|
109
|
-
case "
|
|
110
|
-
return await
|
|
117
|
+
case "list_services": {
|
|
118
|
+
return await listServices(k8sManager, input);
|
|
111
119
|
}
|
|
112
120
|
case "uninstall_helm_chart": {
|
|
113
121
|
return await uninstallHelmChart(input);
|
|
114
122
|
}
|
|
115
|
-
case "
|
|
116
|
-
return await
|
|
117
|
-
}
|
|
118
|
-
case "list_api_resources": {
|
|
119
|
-
return await listApiResources(input);
|
|
123
|
+
case "upgrade_helm_chart": {
|
|
124
|
+
return await upgradeHelmChart(input);
|
|
120
125
|
}
|
|
121
126
|
default:
|
|
122
127
|
throw new McpError(ErrorCode.InvalidRequest, `Unknown tool: ${name}`);
|
|
@@ -219,3 +219,25 @@ export declare const GetLogsResponseSchema: z.ZodObject<{
|
|
|
219
219
|
text: string;
|
|
220
220
|
}[];
|
|
221
221
|
}>;
|
|
222
|
+
export declare const GetEventsResponseSchema: z.ZodObject<{
|
|
223
|
+
content: z.ZodArray<z.ZodObject<{
|
|
224
|
+
type: z.ZodLiteral<"text">;
|
|
225
|
+
text: z.ZodString;
|
|
226
|
+
}, "strip", z.ZodTypeAny, {
|
|
227
|
+
type: "text";
|
|
228
|
+
text: string;
|
|
229
|
+
}, {
|
|
230
|
+
type: "text";
|
|
231
|
+
text: string;
|
|
232
|
+
}>, "many">;
|
|
233
|
+
}, "strip", z.ZodTypeAny, {
|
|
234
|
+
content: {
|
|
235
|
+
type: "text";
|
|
236
|
+
text: string;
|
|
237
|
+
}[];
|
|
238
|
+
}, {
|
|
239
|
+
content: {
|
|
240
|
+
type: "text";
|
|
241
|
+
text: string;
|
|
242
|
+
}[];
|
|
243
|
+
}>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { KubernetesManager } from "../types.js";
|
|
2
|
+
export declare const getEventsSchema: {
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
inputSchema: {
|
|
6
|
+
type: string;
|
|
7
|
+
properties: {
|
|
8
|
+
namespace: {
|
|
9
|
+
type: string;
|
|
10
|
+
description: string;
|
|
11
|
+
};
|
|
12
|
+
fieldSelector: {
|
|
13
|
+
type: string;
|
|
14
|
+
description: string;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
required: never[];
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
export declare function getEvents(k8sManager: KubernetesManager, params: {
|
|
21
|
+
namespace?: string;
|
|
22
|
+
fieldSelector?: string;
|
|
23
|
+
}): Promise<{
|
|
24
|
+
content: {
|
|
25
|
+
type: string;
|
|
26
|
+
text: string;
|
|
27
|
+
}[];
|
|
28
|
+
}>;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
export const getEventsSchema = {
|
|
2
|
+
name: "get_events",
|
|
3
|
+
description: "Get Kubernetes events from the cluster",
|
|
4
|
+
inputSchema: {
|
|
5
|
+
type: "object",
|
|
6
|
+
properties: {
|
|
7
|
+
namespace: {
|
|
8
|
+
type: "string",
|
|
9
|
+
description: "Namespace to get events from. If not specified, gets events from all namespaces",
|
|
10
|
+
},
|
|
11
|
+
fieldSelector: {
|
|
12
|
+
type: "string",
|
|
13
|
+
description: "Field selector to filter events",
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
required: [],
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
export async function getEvents(k8sManager, params) {
|
|
20
|
+
const { namespace, fieldSelector } = params;
|
|
21
|
+
const api = k8sManager.getCoreApi();
|
|
22
|
+
let events;
|
|
23
|
+
if (namespace) {
|
|
24
|
+
const { body } = await api.listNamespacedEvent(namespace, undefined, // pretty
|
|
25
|
+
undefined, // allowWatchBookmarks
|
|
26
|
+
undefined, // _continue
|
|
27
|
+
undefined, // fieldSelector
|
|
28
|
+
fieldSelector // fieldSelector
|
|
29
|
+
);
|
|
30
|
+
events = body;
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
const { body } = await api.listEventForAllNamespaces(undefined, // allowWatchBookmarks
|
|
34
|
+
undefined, // _continue
|
|
35
|
+
fieldSelector, // fieldSelector
|
|
36
|
+
undefined, // labelSelector
|
|
37
|
+
undefined, // limit
|
|
38
|
+
undefined, // pretty
|
|
39
|
+
undefined, // resourceVersion
|
|
40
|
+
undefined, // resourceVersionMatch
|
|
41
|
+
undefined // timeoutSeconds
|
|
42
|
+
);
|
|
43
|
+
events = body;
|
|
44
|
+
}
|
|
45
|
+
const formattedEvents = events.items.map((event) => ({
|
|
46
|
+
type: event.type || "",
|
|
47
|
+
reason: event.reason || "",
|
|
48
|
+
message: event.message || "",
|
|
49
|
+
involvedObject: {
|
|
50
|
+
kind: event.involvedObject.kind || "",
|
|
51
|
+
name: event.involvedObject.name || "",
|
|
52
|
+
namespace: event.involvedObject.namespace || "",
|
|
53
|
+
},
|
|
54
|
+
firstTimestamp: event.firstTimestamp || "",
|
|
55
|
+
lastTimestamp: event.lastTimestamp || "",
|
|
56
|
+
count: event.count || 0,
|
|
57
|
+
}));
|
|
58
|
+
return {
|
|
59
|
+
content: [
|
|
60
|
+
{
|
|
61
|
+
type: "text",
|
|
62
|
+
text: JSON.stringify({ events: formattedEvents }, null, 2),
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
};
|
|
66
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-server-kubernetes",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "MCP server for interacting with Kubernetes clusters via kubectl",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
"start": "node dist/index.js",
|
|
22
22
|
"test": "vitest run",
|
|
23
23
|
"prepublishOnly": "npm run build",
|
|
24
|
-
"dockerbuild": "docker buildx build -t flux159/mcp-server-kubernetes --platform linux/amd64,linux/arm64 --push ."
|
|
24
|
+
"dockerbuild": "docker buildx build -t flux159/mcp-server-kubernetes --platform linux/amd64,linux/arm64 --push .",
|
|
25
|
+
"chat": "npx mcp-chat --server \"./dist/index.js\""
|
|
25
26
|
},
|
|
26
27
|
"keywords": [
|
|
27
28
|
"mcp",
|