Skip to content

Commit f35654f

Browse files
authored
Merge branch 'main' into feat/update-deployment
2 parents 6bad564 + 89c0880 commit f35654f

22 files changed

+1459
-72
lines changed

ADVANCED_README.md

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,49 @@
11
# Advanced README for mcp-server-kubernetes
22

3+
### Non-Destructive Mode
4+
5+
You can run the server in a non-destructive mode that disables all destructive operations (delete pods, delete deployments, delete namespaces, etc.) by setting the `ALLOW_ONLY_NON_DESTRUCTIVE_TOOLS` environment variable to `true`:
6+
7+
```shell
8+
ALLOW_ONLY_NON_DESTRUCTIVE_TOOLS=true npx mcp-server-kubernetes
9+
```
10+
11+
This feature is particularly useful for:
12+
13+
- **Production environments**: Prevent accidental deletion or modification of critical resources
14+
- **Shared clusters**: Allow multiple users to safely explore the cluster without risk of disruption
15+
- **Educational settings**: Provide a safe environment for learning Kubernetes operations
16+
- **Demonstration purposes**: Show cluster state and resources without modification risk
17+
18+
When enabled, the following destructive operations are disabled:
19+
20+
- `delete_pod`: Deleting pods
21+
- `delete_deployment`: Deleting deployments
22+
- `delete_namespace`: Deleting namespaces
23+
- `uninstall_helm_chart`: Uninstalling Helm charts
24+
- `delete_cronjob`: Deleting cronjobs
25+
- `cleanup`: Cleaning up resources
26+
27+
All read-only operations like listing resources, describing pods, getting logs, etc. remain fully functional.
28+
29+
For Non destructive mode in Claude Desktop, you can specify the env var like this:
30+
31+
```json
32+
{
33+
"mcpServers": {
34+
"kubernetes-readonly": {
35+
"command": "npx",
36+
"args": ["mcp-server-kubernetes"],
37+
"env": {
38+
"ALLOW_ONLY_NON_DESTRUCTIVE_TOOLS": "true"
39+
}
40+
}
41+
}
42+
}
43+
```
44+
45+
### SSE Transport
46+
347
To enable [SSE transport](https://modelcontextprotocol.io/docs/concepts/transports#server-sent-events-sse) for mcp-server-kubernetes, use the ENABLE_UNSAFE_SSE_TRANSPORT environment variable.
448

549
```shell
@@ -35,6 +79,6 @@ If there's no error, you will receive an `event: message` response in the localh
3579

3680
Note that normally a client would handle this for you. This is just a demonstration of how to use the SSE transport.
3781

38-
## Why is it Unsafe?
82+
### Why is SSE Transport Unsafe?
3983

4084
SSE transport exposes an http endpoint that can be accessed by anyone with the URL. This can be a security risk if the server is not properly secured. It is recommended to use a secure proxy server to proxy to the SSE endpoint. In addition, anyone with access to the URL will be able to utilize the authentication of your kubeconfig to make requests to your Kubernetes cluster. You should add logging to your proxy in order to monitor user requests to the SSE endpoint.

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ RUN chmod 644 /etc/apt/sources.list.d/kubernetes.list
1414
RUN curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | gpg --dearmor -o /usr/share/keyrings/cloud.google.gpg
1515
RUN echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list
1616
RUN apt-get update
17-
RUN apt-get install -y kubectl google-cloud-cli google-cloud-cli-gke-gcloud-auth-plugin
17+
RUN apt-get install -y kubectl google-cloud-cli google-cloud-cli-gke-gcloud-auth-plugin awscli
1818

1919
# Build the typescript code
2020
FROM base AS dependencies

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ npx mcp-chat --config "%APPDATA%\Claude\claude_desktop_config.json"
8282
- [x] Get Kubernetes events from the cluster
8383
- [x] Port forward to a pod or service
8484
- [x] Create, list, and decribe cronjobs
85+
- [x] Non-destructive mode for read-only access to clusters
8586

8687
## Local Development
8788

@@ -143,7 +144,9 @@ See the [CONTRIBUTING.md](CONTRIBUTING.md) file for details.
143144

144145
## Advanced
145146

146-
For more advanced information like using SSE transport, see the [ADVANCED_README.md](ADVANCED_README.md).
147+
### Additional Advanced Features
148+
149+
For more advanced information like using SSE transport, Non-destructive mode with `ALLOW_ONLY_NON_DESTRUCTIVE_TOOLS`, see the [ADVANCED_README.md](ADVANCED_README.md).
147150

148151
## Architecture
149152

bun.lockb

935 Bytes
Binary file not shown.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "mcp-server-kubernetes",
3-
"version": "1.3.0",
3+
"version": "1.3.2",
44
"description": "MCP server for interacting with Kubernetes clusters via kubectl",
55
"license": "MIT",
66
"type": "module",

src/index.ts

Lines changed: 118 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import {
3232
} from "./tools/create_namespace.js";
3333
import { createPod, createPodSchema } from "./tools/create_pod.js";
3434
import { createCronJob, createCronJobSchema } from "./tools/create_cronjob.js";
35-
import { DeleteCronJob,DeleteCronJobSchema} from "./tools/delete_cronjob.js";
35+
import { DeleteCronJob, DeleteCronJobSchema } from "./tools/delete_cronjob.js";
3636
import { deletePod, deletePodSchema } from "./tools/delete_pod.js";
3737
import { describePod, describePodSchema } from "./tools/describe_pod.js";
3838
import { getLogs, getLogsSchema } from "./tools/get_logs.js";
@@ -62,13 +62,20 @@ import {
6262
} from "./tools/port_forward.js";
6363
import { deleteDeployment, deleteDeploymentSchema } from "./tools/delete_deployment.js";
6464
import { createDeployment } from "./tools/create_deployment.js";
65-
import {scaleDeployment,scaleDeploymentSchema} from "./tools/scale_deployment.js"
65+
import { scaleDeployment, scaleDeploymentSchema } from "./tools/scale_deployment.js"
6666
import {
6767
describeDeployment,
6868
describeDeploymentSchema,
6969
} from "./tools/describe_deployment.js";
70-
import {createConfigMap, CreateConfigMapSchema } from "./tools/create_configmap.js";
7170
import { updateDeployment, updateDeploymentSchema } from "./tools/update_deployment.js";
71+
import { createConfigMap, CreateConfigMapSchema } from "./tools/create_configmap.js";
72+
import { createService, createServiceSchema } from "./tools/create_service.js";
73+
import { listContexts, listContextsSchema } from "./tools/list_contexts.js";
74+
import { getCurrentContext, getCurrentContextSchema } from "./tools/get_current_context.js";
75+
import { setCurrentContext, setCurrentContextSchema } from "./tools/set_current_context.js";
76+
77+
// Check if non-destructive tools only mode is enabled
78+
const nonDestructiveTools = process.env.ALLOW_ONLY_NON_DESTRUCTIVE_TOOLS === 'true';
7279

7380
const k8sManager = new KubernetesManager();
7481

@@ -80,44 +87,64 @@ const server = new Server(
8087
serverConfig
8188
);
8289

90+
// Define destructive tools (delete and uninstall operations)
91+
const destructiveTools = [
92+
deletePodSchema,
93+
deleteDeploymentSchema,
94+
deleteNamespaceSchema,
95+
uninstallHelmChartSchema,
96+
DeleteCronJobSchema,
97+
cleanupSchema, // Cleanup is also destructive as it deletes resources
98+
];
99+
83100
// Tools handlers
84101
server.setRequestHandler(ListToolsRequestSchema, async () => {
85-
return {
86-
tools: [
87-
cleanupSchema,
88-
createDeploymentSchema,
89-
createNamespaceSchema,
90-
createPodSchema,
91-
createCronJobSchema,
92-
deletePodSchema,
93-
deleteDeploymentSchema,
94-
deleteNamespaceSchema,
95-
describeCronJobSchema,
96-
describePodSchema,
97-
describeDeploymentSchema,
98-
explainResourceSchema,
99-
getEventsSchema,
100-
getJobLogsSchema,
101-
getLogsSchema,
102-
installHelmChartSchema,
103-
listApiResourcesSchema,
104-
listCronJobsSchema,
105-
listDeploymentsSchema,
106-
listJobsSchema,
107-
listNamespacesSchema,
108-
listNodesSchema,
109-
listPodsSchema,
110-
listServicesSchema,
111-
updateDeploymentSchema,
112-
uninstallHelmChartSchema,
113-
upgradeHelmChartSchema,
114-
PortForwardSchema,
115-
StopPortForwardSchema,
116-
scaleDeploymentSchema,
117-
DeleteCronJobSchema,
118-
CreateConfigMapSchema,
119-
],
120-
};
102+
// Get all available tools
103+
const allTools = [
104+
cleanupSchema,
105+
createDeploymentSchema,
106+
createNamespaceSchema,
107+
createPodSchema,
108+
createCronJobSchema,
109+
createServiceSchema,
110+
deletePodSchema,
111+
deleteDeploymentSchema,
112+
deleteNamespaceSchema,
113+
describeCronJobSchema,
114+
describePodSchema,
115+
describeDeploymentSchema,
116+
explainResourceSchema,
117+
getEventsSchema,
118+
getJobLogsSchema,
119+
getLogsSchema,
120+
installHelmChartSchema,
121+
listApiResourcesSchema,
122+
listCronJobsSchema,
123+
listContextsSchema,
124+
getCurrentContextSchema,
125+
setCurrentContextSchema,
126+
listDeploymentsSchema,
127+
listJobsSchema,
128+
listNamespacesSchema,
129+
listNodesSchema,
130+
listPodsSchema,
131+
listServicesSchema,
132+
uninstallHelmChartSchema,
133+
updateDeploymentSchema,
134+
upgradeHelmChartSchema,
135+
PortForwardSchema,
136+
StopPortForwardSchema,
137+
scaleDeploymentSchema,
138+
DeleteCronJobSchema,
139+
CreateConfigMapSchema,
140+
];
141+
142+
// Filter out destructive tools if ALLOW_ONLY_NON_DESTRUCTIVE_TOOLS is set to 'true'
143+
const tools = nonDestructiveTools
144+
? allTools.filter(tool => !destructiveTools.some(dt => dt.name === tool.name))
145+
: allTools;
146+
147+
return { tools };
121148
});
122149

123150
server.setRequestHandler(
@@ -183,12 +210,12 @@ server.setRequestHandler(
183210
);
184211
}
185212

186-
case "delete_cronjob" : {
213+
case "delete_cronjob": {
187214
return await DeleteCronJob(
188215
k8sManager,
189216
input as {
190-
name : string;
191-
namespace : string
217+
name: string;
218+
namespace: string
192219
}
193220
);
194221
}
@@ -323,6 +350,27 @@ server.setRequestHandler(
323350
);
324351
}
325352

353+
case "list_contexts": {
354+
return await listContexts(
355+
k8sManager,
356+
input as { showCurrent?: boolean }
357+
);
358+
}
359+
360+
case "get_current_context": {
361+
return await getCurrentContext(
362+
k8sManager,
363+
input as { detailed?: boolean }
364+
);
365+
}
366+
367+
case "set_current_context": {
368+
return await setCurrentContext(
369+
k8sManager,
370+
input as { name: string }
371+
);
372+
}
373+
326374
case "describe_cronjob": {
327375
return await describeCronJob(
328376
k8sManager,
@@ -453,25 +501,44 @@ server.setRequestHandler(
453501
}
454502
);
455503
}
456-
457-
case "scale_deployment" : {
504+
505+
case "scale_deployment": {
458506
return await scaleDeployment(
459507
k8sManager,
460508
input as {
461-
name : string,
462-
namespace : string,
463-
replicas : number
509+
name: string,
510+
namespace: string,
511+
replicas: number
464512
}
465513
);
466514
}
467515

468-
case "create_configmap" : {
516+
case "create_configmap": {
469517
return await createConfigMap(
470518
k8sManager,
471519
input as {
472-
name : string,
473-
namespace : string,
474-
data : Record<string, string>
520+
name: string,
521+
namespace: string,
522+
data: Record<string, string>
523+
}
524+
);
525+
}
526+
527+
case "create_service": {
528+
return await createService(
529+
k8sManager,
530+
input as {
531+
name: string;
532+
namespace?: string;
533+
type?: "ClusterIP" | "NodePort" | "LoadBalancer";
534+
selector?: Record<string, string>;
535+
ports: Array<{
536+
port: number;
537+
targetPort?: number;
538+
protocol?: string;
539+
name?: string;
540+
nodePort?: number;
541+
}>;
475542
}
476543
);
477544
}

src/models/response-schemas.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,4 +116,15 @@ export const CreateConfigMapResponseSchema = z.object({
116116
message: z.string(),
117117
})
118118
),
119-
});
119+
});
120+
121+
export const ListContextsResponseSchema = z.object({
122+
content: z.array(ToolResponseContent),
123+
});
124+
125+
export const GetCurrentContextResponseSchema = z.object({
126+
content: z.array(ToolResponseContent),
127+
});
128+
export const SetCurrentContextResponseSchema = z.object({
129+
content: z.array(ToolResponseContent),
130+
});

src/tools/create_namespace.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export async function createNamespace(
4343
type: "text",
4444
text: JSON.stringify(
4545
{
46-
podName: response.body.metadata!.name!,
46+
namespaceName: response.body.metadata!.name!,
4747
status: "created",
4848
},
4949
null,

0 commit comments

Comments
 (0)