mcp-server-kubernetes 2.1.0 → 2.2.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/README.md +46 -7
- package/dist/index.js +2 -1
- package/dist/tools/kubectl-generic.js +1 -1
- package/dist/utils/kubernetes-manager.d.ts +37 -0
- package/dist/utils/kubernetes-manager.js +129 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -11,12 +11,13 @@
|
|
|
11
11
|
[](https://github.com/Flux159/mcp-server-kubernetes/commits/main)
|
|
12
12
|
[](https://smithery.ai/protocol/mcp-server-kubernetes)
|
|
13
13
|
|
|
14
|
-
MCP Server that can connect to a Kubernetes cluster and manage it.
|
|
14
|
+
MCP Server that can connect to a Kubernetes cluster and manage it. Supports loading kubeconfig from multiple sources in priority order.
|
|
15
15
|
|
|
16
16
|
https://github.com/user-attachments/assets/f25f8f4e-4d04-479b-9ae0-5dac452dd2ed
|
|
17
17
|
|
|
18
18
|
<a href="https://glama.ai/mcp/servers/w71ieamqrt"><img width="380" height="200" src="https://glama.ai/mcp/servers/w71ieamqrt/badge" /></a>
|
|
19
19
|
|
|
20
|
+
|
|
20
21
|
## Usage with Claude Desktop
|
|
21
22
|
|
|
22
23
|
```json
|
|
@@ -30,6 +31,8 @@ https://github.com/user-attachments/assets/f25f8f4e-4d04-479b-9ae0-5dac452dd2ed
|
|
|
30
31
|
}
|
|
31
32
|
```
|
|
32
33
|
|
|
34
|
+
By default, the server loads kubeconfig from `~/.kube/config`. For additional authentication options (environment variables, custom paths, etc.), see [ADVANCED_README.md](ADVANCED_README.md).
|
|
35
|
+
|
|
33
36
|
The server will automatically connect to your current kubectl context. Make sure you have:
|
|
34
37
|
|
|
35
38
|
1. kubectl installed and in your PATH
|
|
@@ -175,6 +178,25 @@ For Claude Desktop configuration with non-destructive mode:
|
|
|
175
178
|
}
|
|
176
179
|
```
|
|
177
180
|
|
|
181
|
+
### Commands Available in Non-Destructive Mode
|
|
182
|
+
|
|
183
|
+
All read-only and resource creation/update operations remain available:
|
|
184
|
+
|
|
185
|
+
- Resource Information: `kubectl_get`, `kubectl_describe`, `kubectl_list`, `kubectl_logs`, `explain_resource`, `list_api_resources`
|
|
186
|
+
- Resource Creation/Modification: `kubectl_apply`, `kubectl_create`, `kubectl_scale`, `kubectl_patch`, `kubectl_rollout`
|
|
187
|
+
- Helm Operations: `install_helm_chart`, `upgrade_helm_chart`
|
|
188
|
+
- Connectivity: `port_forward`, `stop_port_forward`
|
|
189
|
+
- Context Management: `kubectl_context`
|
|
190
|
+
|
|
191
|
+
### Commands Disabled in Non-Destructive Mode
|
|
192
|
+
|
|
193
|
+
The following destructive operations are disabled:
|
|
194
|
+
|
|
195
|
+
- `kubectl_delete`: Deleting any Kubernetes resources
|
|
196
|
+
- `uninstall_helm_chart`: Uninstalling Helm charts
|
|
197
|
+
- `cleanup`: Cleanup of managed resources
|
|
198
|
+
- `kubectl_generic`: General kubectl command access (may include destructive operations)
|
|
199
|
+
|
|
178
200
|
For additional advanced features, see the [ADVANCED_README.md](ADVANCED_README.md).
|
|
179
201
|
|
|
180
202
|
## Architecture
|
|
@@ -190,19 +212,34 @@ The sequence diagram below illustrates how requests flow through the system:
|
|
|
190
212
|
```mermaid
|
|
191
213
|
sequenceDiagram
|
|
192
214
|
participant Client
|
|
193
|
-
participant Transport as
|
|
215
|
+
participant Transport as Transport Layer
|
|
194
216
|
participant Server as MCP Server
|
|
217
|
+
participant Filter as Tool Filter
|
|
195
218
|
participant Handler as Request Handler
|
|
196
219
|
participant K8sManager as KubernetesManager
|
|
197
220
|
participant K8s as Kubernetes API
|
|
198
221
|
|
|
199
|
-
|
|
222
|
+
Note over Transport: StdioTransport or<br>SSE Transport
|
|
223
|
+
|
|
224
|
+
Client->>Transport: Send Request
|
|
200
225
|
Transport->>Server: Forward Request
|
|
201
226
|
|
|
202
227
|
alt Tools Request
|
|
203
|
-
Server->>
|
|
204
|
-
|
|
205
|
-
|
|
228
|
+
Server->>Filter: Filter available tools
|
|
229
|
+
Note over Filter: Remove destructive tools<br>if in non-destructive mode
|
|
230
|
+
Filter->>Handler: Route to tools handler
|
|
231
|
+
|
|
232
|
+
alt kubectl operations
|
|
233
|
+
Handler->>K8sManager: Execute kubectl operation
|
|
234
|
+
K8sManager->>K8s: Make API call
|
|
235
|
+
else Helm operations
|
|
236
|
+
Handler->>K8sManager: Execute Helm operation
|
|
237
|
+
K8sManager->>K8s: Make API call
|
|
238
|
+
else Port Forward operations
|
|
239
|
+
Handler->>K8sManager: Set up port forwarding
|
|
240
|
+
K8sManager->>K8s: Make API call
|
|
241
|
+
end
|
|
242
|
+
|
|
206
243
|
K8s-->>K8sManager: Return result
|
|
207
244
|
K8sManager-->>Handler: Process response
|
|
208
245
|
Handler-->>Server: Return tool result
|
|
@@ -219,6 +256,8 @@ sequenceDiagram
|
|
|
219
256
|
Transport-->>Client: Return Final Response
|
|
220
257
|
```
|
|
221
258
|
|
|
259
|
+
See this [DeepWiki link](https://deepwiki.com/Flux159/mcp-server-kubernetes) for a more indepth architecture overview created by Devin.
|
|
260
|
+
|
|
222
261
|
## Publishing new release
|
|
223
262
|
|
|
224
263
|
Go to the [releases page](https://github.com/Flux159/mcp-server-kubernetes/releases), click on "Draft New Release", click "Choose a tag" and create a new tag by typing out a new version number using "v{major}.{minor}.{patch}" semver format. Then, write a release title "Release v{major}.{minor}.{patch}" and description / changelog if necessary and click "Publish Release".
|
|
@@ -227,4 +266,4 @@ This will create a new tag which will trigger a new release build via the cd.yml
|
|
|
227
266
|
|
|
228
267
|
## Not planned
|
|
229
268
|
|
|
230
|
-
|
|
269
|
+
Adding clusters to kubectx.
|
package/dist/index.js
CHANGED
|
@@ -29,6 +29,7 @@ const destructiveTools = [
|
|
|
29
29
|
kubectlDeleteSchema, // This replaces all individual delete operations
|
|
30
30
|
uninstallHelmChartSchema,
|
|
31
31
|
cleanupSchema, // Cleanup is also destructive as it deletes resources
|
|
32
|
+
kubectlGenericSchema, // Generic kubectl command can perform destructive operations
|
|
32
33
|
];
|
|
33
34
|
// Get all available tools
|
|
34
35
|
const allTools = [
|
|
@@ -181,7 +182,7 @@ if (process.env.ENABLE_UNSAFE_SSE_TRANSPORT) {
|
|
|
181
182
|
}
|
|
182
183
|
else {
|
|
183
184
|
const transport = new StdioServerTransport();
|
|
184
|
-
console.
|
|
185
|
+
console.error(`Starting Kubernetes MCP server v${serverConfig.version}, handling commands...`);
|
|
185
186
|
server.connect(transport);
|
|
186
187
|
}
|
|
187
188
|
["SIGINT", "SIGTERM"].forEach((signal) => {
|
|
@@ -97,7 +97,7 @@ export async function kubectlGeneric(k8sManager, input) {
|
|
|
97
97
|
// Execute the command (join all args except the first "kubectl" which is used in execSync)
|
|
98
98
|
const command = cmdArgs.slice(1).join(' ');
|
|
99
99
|
try {
|
|
100
|
-
console.
|
|
100
|
+
console.error(`Executing: kubectl ${command}`);
|
|
101
101
|
const result = execSync(`kubectl ${command}`, { encoding: "utf8" });
|
|
102
102
|
return {
|
|
103
103
|
content: [
|
|
@@ -13,6 +13,38 @@ export declare class KubernetesManager {
|
|
|
13
13
|
* A very simple test to check if the application is running inside a Kubernetes cluster
|
|
14
14
|
*/
|
|
15
15
|
private isRunningInCluster;
|
|
16
|
+
/**
|
|
17
|
+
* Check if KUBECONFIG_YAML environment variable is available
|
|
18
|
+
*/
|
|
19
|
+
private hasEnvKubeconfigYaml;
|
|
20
|
+
/**
|
|
21
|
+
* Check if KUBECONFIG_JSON environment variable is available
|
|
22
|
+
*/
|
|
23
|
+
private hasEnvKubeconfigJson;
|
|
24
|
+
/**
|
|
25
|
+
* Check if minimal K8S_SERVER and K8S_TOKEN environment variables are available
|
|
26
|
+
*/
|
|
27
|
+
private hasEnvMinimalKubeconfig;
|
|
28
|
+
/**
|
|
29
|
+
* Load kubeconfig from KUBECONFIG_PATH environment variable (file path)
|
|
30
|
+
*/
|
|
31
|
+
private loadEnvKubeconfigPath;
|
|
32
|
+
/**
|
|
33
|
+
* Load kubeconfig from KUBECONFIG_YAML environment variable (YAML format)
|
|
34
|
+
*/
|
|
35
|
+
private loadEnvKubeconfigYaml;
|
|
36
|
+
/**
|
|
37
|
+
* Load kubeconfig from KUBECONFIG_JSON environment variable (JSON format)
|
|
38
|
+
*/
|
|
39
|
+
private loadEnvKubeconfigJson;
|
|
40
|
+
/**
|
|
41
|
+
* Load kubeconfig from minimal K8S_SERVER and K8S_TOKEN environment variables
|
|
42
|
+
*/
|
|
43
|
+
private loadEnvMinimalKubeconfig;
|
|
44
|
+
/**
|
|
45
|
+
* Check if KUBECONFIG_PATH environment variable is available
|
|
46
|
+
*/
|
|
47
|
+
private hasEnvKubeconfigPath;
|
|
16
48
|
/**
|
|
17
49
|
* Set the current context to the desired context name.
|
|
18
50
|
*
|
|
@@ -30,4 +62,9 @@ export declare class KubernetesManager {
|
|
|
30
62
|
getCoreApi(): k8s.CoreV1Api;
|
|
31
63
|
getAppsApi(): k8s.AppsV1Api;
|
|
32
64
|
getBatchApi(): k8s.BatchV1Api;
|
|
65
|
+
/**
|
|
66
|
+
* Get the default namespace for operations
|
|
67
|
+
* Uses K8S_NAMESPACE environment variable if set, otherwise defaults to "default"
|
|
68
|
+
*/
|
|
69
|
+
getDefaultNamespace(): string;
|
|
33
70
|
}
|
|
@@ -11,11 +11,59 @@ export class KubernetesManager {
|
|
|
11
11
|
constructor() {
|
|
12
12
|
this.kc = new k8s.KubeConfig();
|
|
13
13
|
if (this.isRunningInCluster()) {
|
|
14
|
+
// Priority 1: In-cluster configuration (existing)
|
|
14
15
|
this.kc.loadFromCluster();
|
|
15
16
|
}
|
|
17
|
+
else if (this.hasEnvKubeconfigYaml()) {
|
|
18
|
+
// Priority 2: Full kubeconfig as YAML string
|
|
19
|
+
try {
|
|
20
|
+
this.loadEnvKubeconfigYaml();
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
throw new Error(`Failed to parse KUBECONFIG_YAML: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
else if (this.hasEnvKubeconfigJson()) {
|
|
27
|
+
// Priority 3: Full kubeconfig as JSON string
|
|
28
|
+
try {
|
|
29
|
+
this.loadEnvKubeconfigJson();
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
throw new Error(`Failed to parse KUBECONFIG_JSON: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
else if (this.hasEnvMinimalKubeconfig()) {
|
|
36
|
+
// Priority 4: Minimal config with individual environment variables
|
|
37
|
+
try {
|
|
38
|
+
this.loadEnvMinimalKubeconfig();
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
throw new Error(`Failed to create kubeconfig from K8S_SERVER and K8S_TOKEN: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
else if (this.hasEnvKubeconfigPath()) {
|
|
45
|
+
// Priority 5: Custom kubeconfig file path
|
|
46
|
+
try {
|
|
47
|
+
this.loadEnvKubeconfigPath();
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
throw new Error(`Failed to load kubeconfig from KUBECONFIG_PATH: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
16
53
|
else {
|
|
54
|
+
// Priority 6: Default file-based configuration (existing fallback)
|
|
17
55
|
this.kc.loadFromDefault();
|
|
18
56
|
}
|
|
57
|
+
// Apply context override if specified
|
|
58
|
+
if (process.env.K8S_CONTEXT) {
|
|
59
|
+
try {
|
|
60
|
+
this.setCurrentContext(process.env.K8S_CONTEXT);
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
console.warn(`Warning: Could not set context to ${process.env.K8S_CONTEXT}: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// Initialize API clients
|
|
19
67
|
this.k8sApi = this.kc.makeApiClient(k8s.CoreV1Api);
|
|
20
68
|
this.k8sAppsApi = this.kc.makeApiClient(k8s.AppsV1Api);
|
|
21
69
|
this.k8sBatchApi = this.kc.makeApiClient(k8s.BatchV1Api);
|
|
@@ -32,6 +80,80 @@ export class KubernetesManager {
|
|
|
32
80
|
return false;
|
|
33
81
|
}
|
|
34
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* Check if KUBECONFIG_YAML environment variable is available
|
|
85
|
+
*/
|
|
86
|
+
hasEnvKubeconfigYaml() {
|
|
87
|
+
return !!(process.env.KUBECONFIG_YAML && process.env.KUBECONFIG_YAML.trim());
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Check if KUBECONFIG_JSON environment variable is available
|
|
91
|
+
*/
|
|
92
|
+
hasEnvKubeconfigJson() {
|
|
93
|
+
return !!(process.env.KUBECONFIG_JSON && process.env.KUBECONFIG_JSON.trim());
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Check if minimal K8S_SERVER and K8S_TOKEN environment variables are available
|
|
97
|
+
*/
|
|
98
|
+
hasEnvMinimalKubeconfig() {
|
|
99
|
+
return !!(process.env.K8S_SERVER &&
|
|
100
|
+
process.env.K8S_SERVER.trim() &&
|
|
101
|
+
process.env.K8S_TOKEN &&
|
|
102
|
+
process.env.K8S_TOKEN.trim());
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Load kubeconfig from KUBECONFIG_PATH environment variable (file path)
|
|
106
|
+
*/
|
|
107
|
+
loadEnvKubeconfigPath() {
|
|
108
|
+
this.kc.loadFromFile(process.env.KUBECONFIG_PATH);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Load kubeconfig from KUBECONFIG_YAML environment variable (YAML format)
|
|
112
|
+
*/
|
|
113
|
+
loadEnvKubeconfigYaml() {
|
|
114
|
+
this.kc.loadFromString(process.env.KUBECONFIG_YAML);
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Load kubeconfig from KUBECONFIG_JSON environment variable (JSON format)
|
|
118
|
+
*/
|
|
119
|
+
loadEnvKubeconfigJson() {
|
|
120
|
+
const configObj = JSON.parse(process.env.KUBECONFIG_JSON);
|
|
121
|
+
this.kc.loadFromOptions(configObj);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Load kubeconfig from minimal K8S_SERVER and K8S_TOKEN environment variables
|
|
125
|
+
*/
|
|
126
|
+
loadEnvMinimalKubeconfig() {
|
|
127
|
+
if (!process.env.K8S_SERVER || !process.env.K8S_TOKEN) {
|
|
128
|
+
throw new Error('K8S_SERVER and K8S_TOKEN environment variables are required');
|
|
129
|
+
}
|
|
130
|
+
const cluster = {
|
|
131
|
+
name: 'env-cluster',
|
|
132
|
+
server: process.env.K8S_SERVER,
|
|
133
|
+
skipTLSVerify: process.env.K8S_SKIP_TLS_VERIFY === 'true'
|
|
134
|
+
};
|
|
135
|
+
const user = {
|
|
136
|
+
name: 'env-user',
|
|
137
|
+
token: process.env.K8S_TOKEN
|
|
138
|
+
};
|
|
139
|
+
const context = {
|
|
140
|
+
name: 'env-context',
|
|
141
|
+
user: user.name,
|
|
142
|
+
cluster: cluster.name
|
|
143
|
+
};
|
|
144
|
+
this.kc.loadFromOptions({
|
|
145
|
+
clusters: [cluster],
|
|
146
|
+
users: [user],
|
|
147
|
+
contexts: [context],
|
|
148
|
+
currentContext: context.name
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Check if KUBECONFIG_PATH environment variable is available
|
|
153
|
+
*/
|
|
154
|
+
hasEnvKubeconfigPath() {
|
|
155
|
+
return !!(process.env.KUBECONFIG_PATH && process.env.KUBECONFIG_PATH.trim());
|
|
156
|
+
}
|
|
35
157
|
/**
|
|
36
158
|
* Set the current context to the desired context name.
|
|
37
159
|
*
|
|
@@ -110,4 +232,11 @@ export class KubernetesManager {
|
|
|
110
232
|
getBatchApi() {
|
|
111
233
|
return this.k8sBatchApi;
|
|
112
234
|
}
|
|
235
|
+
/**
|
|
236
|
+
* Get the default namespace for operations
|
|
237
|
+
* Uses K8S_NAMESPACE environment variable if set, otherwise defaults to "default"
|
|
238
|
+
*/
|
|
239
|
+
getDefaultNamespace() {
|
|
240
|
+
return process.env.K8S_NAMESPACE || 'default';
|
|
241
|
+
}
|
|
113
242
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-server-kubernetes",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "MCP server for interacting with Kubernetes clusters via kubectl",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
"@types/express": "5.0.1",
|
|
47
47
|
"@types/js-yaml": "4.0.9",
|
|
48
48
|
"@types/node": "22.9.3",
|
|
49
|
+
"esbuild": "0.21.5",
|
|
49
50
|
"shx": "0.3.4",
|
|
50
51
|
"typescript": "5.6.2",
|
|
51
52
|
"vitest": "2.1.9"
|