mcp-server-kubernetes 2.4.1 → 2.4.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 +14 -3
- package/dist/config/container-templates.d.ts +2 -2
- package/dist/index.d.ts +8 -37
- package/dist/index.js +21 -6
- package/dist/models/response-schemas.d.ts +22 -0
- package/dist/models/response-schemas.js +3 -0
- package/dist/models/tool-models.d.ts +1 -0
- package/dist/models/tool-models.js +1 -0
- package/dist/prompts/index.d.ts +3 -0
- package/dist/prompts/index.js +108 -0
- package/dist/tools/exec_in_pod.d.ts +78 -0
- package/dist/tools/exec_in_pod.js +161 -0
- package/dist/tools/kubectl-logs.js +30 -0
- package/dist/tools/ping.d.ts +10 -0
- package/dist/tools/ping.js +12 -0
- package/package.json +1 -1
- package/dist/tools/kubectl-list.d.ts +0 -59
- package/dist/tools/kubectl-list.js +0 -187
package/README.md
CHANGED
|
@@ -17,7 +17,6 @@ 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
|
-
|
|
21
20
|
## Usage with Claude Desktop
|
|
22
21
|
|
|
23
22
|
```json
|
|
@@ -72,7 +71,7 @@ npx mcp-chat --config "%APPDATA%\Claude\claude_desktop_config.json"
|
|
|
72
71
|
- [x] Unified kubectl API for managing resources
|
|
73
72
|
- Get or list resources with `kubectl_get`
|
|
74
73
|
- Describe resources with `kubectl_describe`
|
|
75
|
-
- List resources with `
|
|
74
|
+
- List resources with `kubectl_get`
|
|
76
75
|
- Create resources with `kubectl_create`
|
|
77
76
|
- Apply YAML manifests with `kubectl_apply`
|
|
78
77
|
- Delete resources with `kubectl_delete`
|
|
@@ -84,14 +83,26 @@ npx mcp-chat --config "%APPDATA%\Claude\claude_desktop_config.json"
|
|
|
84
83
|
- Update field(s) of a resource with `kubectl_patch`
|
|
85
84
|
- Manage deployment rollouts with `kubectl_rollout`
|
|
86
85
|
- Execute any kubectl command with `kubectl_generic`
|
|
86
|
+
- Verify connection with `ping`
|
|
87
87
|
- [x] Advanced operations
|
|
88
88
|
- Scale deployments with `kubectl_scale` (replaces legacy `scale_deployment`)
|
|
89
89
|
- Port forward to pods and services with `port_forward`
|
|
90
90
|
- Run Helm operations
|
|
91
91
|
- Install, upgrade, and uninstall charts
|
|
92
92
|
- Support for custom values, repositories, and versions
|
|
93
|
+
- [x] Troubleshooting Prompt (`k8s-diagnose`)
|
|
94
|
+
- Guides through a systematic Kubernetes troubleshooting flow for pods based on a keyword and optional namespace.
|
|
93
95
|
- [x] Non-destructive mode for read and create/update-only access to clusters
|
|
94
96
|
|
|
97
|
+
## Prompts
|
|
98
|
+
|
|
99
|
+
The MCP Kubernetes server includes specialized prompts to assist with common diagnostic operations.
|
|
100
|
+
|
|
101
|
+
### k8s-diagnose Prompt
|
|
102
|
+
|
|
103
|
+
This prompt provides a systematic troubleshooting flow for Kubernetes pods. It accepts a `keyword` to identify relevant pods and an optional `namespace` to narrow the search.
|
|
104
|
+
The prompt's output will guide you through an autonomous troubleshooting flow, providing instructions for identifying issues, collecting evidence, and suggesting remediation steps.
|
|
105
|
+
|
|
95
106
|
## Local Development
|
|
96
107
|
|
|
97
108
|
Make sure that you have [bun installed](https://bun.sh/docs/installation). Clone the repo & install dependencies:
|
|
@@ -182,7 +193,7 @@ For Claude Desktop configuration with non-destructive mode:
|
|
|
182
193
|
|
|
183
194
|
All read-only and resource creation/update operations remain available:
|
|
184
195
|
|
|
185
|
-
- Resource Information: `kubectl_get`, `kubectl_describe`, `
|
|
196
|
+
- Resource Information: `kubectl_get`, `kubectl_describe`, `kubectl_logs`, `explain_resource`, `list_api_resources`
|
|
186
197
|
- Resource Creation/Modification: `kubectl_apply`, `kubectl_create`, `kubectl_scale`, `kubectl_patch`, `kubectl_rollout`
|
|
187
198
|
- Helm Operations: `install_helm_chart`, `upgrade_helm_chart`
|
|
188
199
|
- Connectivity: `port_forward`, `stop_port_forward`
|
|
@@ -61,8 +61,8 @@ export declare const CustomContainerConfig: z.ZodObject<{
|
|
|
61
61
|
limits?: Record<string, string> | undefined;
|
|
62
62
|
requests?: Record<string, string> | undefined;
|
|
63
63
|
} | undefined;
|
|
64
|
-
args?: string[] | undefined;
|
|
65
64
|
command?: string[] | undefined;
|
|
65
|
+
args?: string[] | undefined;
|
|
66
66
|
ports?: {
|
|
67
67
|
containerPort: number;
|
|
68
68
|
name?: string | undefined;
|
|
@@ -84,8 +84,8 @@ export declare const CustomContainerConfig: z.ZodObject<{
|
|
|
84
84
|
limits?: Record<string, string> | undefined;
|
|
85
85
|
requests?: Record<string, string> | undefined;
|
|
86
86
|
} | undefined;
|
|
87
|
-
args?: string[] | undefined;
|
|
88
87
|
command?: string[] | undefined;
|
|
88
|
+
args?: string[] | undefined;
|
|
89
89
|
ports?: {
|
|
90
90
|
containerPort: number;
|
|
91
91
|
name?: string | undefined;
|
package/dist/index.d.ts
CHANGED
|
@@ -296,43 +296,6 @@ declare const allTools: ({
|
|
|
296
296
|
};
|
|
297
297
|
readonly required: readonly ["resourceType", "name"];
|
|
298
298
|
};
|
|
299
|
-
} | {
|
|
300
|
-
readonly name: "kubectl_list";
|
|
301
|
-
readonly description: "List Kubernetes resources by resource type and optionally namespace";
|
|
302
|
-
readonly inputSchema: {
|
|
303
|
-
readonly type: "object";
|
|
304
|
-
readonly properties: {
|
|
305
|
-
readonly resourceType: {
|
|
306
|
-
readonly type: "string";
|
|
307
|
-
readonly description: "Type of resource to list (e.g., pods, deployments, services, configmaps, etc.)";
|
|
308
|
-
};
|
|
309
|
-
readonly namespace: {
|
|
310
|
-
readonly type: "string";
|
|
311
|
-
readonly description: "Namespace of the resources (optional - defaults to 'default' for namespaced resources)";
|
|
312
|
-
readonly default: "default";
|
|
313
|
-
};
|
|
314
|
-
readonly output: {
|
|
315
|
-
readonly type: "string";
|
|
316
|
-
readonly enum: readonly ["json", "yaml", "wide", "name", "custom", "formatted"];
|
|
317
|
-
readonly description: "Output format - 'formatted' uses a resource-specific format with key information";
|
|
318
|
-
readonly default: "formatted";
|
|
319
|
-
};
|
|
320
|
-
readonly allNamespaces: {
|
|
321
|
-
readonly type: "boolean";
|
|
322
|
-
readonly description: "If true, list resources across all namespaces";
|
|
323
|
-
readonly default: false;
|
|
324
|
-
};
|
|
325
|
-
readonly labelSelector: {
|
|
326
|
-
readonly type: "string";
|
|
327
|
-
readonly description: "Filter resources by label selector (e.g. 'app=nginx')";
|
|
328
|
-
};
|
|
329
|
-
readonly fieldSelector: {
|
|
330
|
-
readonly type: "string";
|
|
331
|
-
readonly description: "Filter resources by field selector (e.g. 'metadata.name=my-pod')";
|
|
332
|
-
};
|
|
333
|
-
};
|
|
334
|
-
readonly required: readonly ["resourceType", "namespace"];
|
|
335
|
-
};
|
|
336
299
|
} | {
|
|
337
300
|
readonly name: "kubectl_apply";
|
|
338
301
|
readonly description: "Apply a Kubernetes YAML manifest from a string or file";
|
|
@@ -591,5 +554,13 @@ declare const allTools: ({
|
|
|
591
554
|
};
|
|
592
555
|
readonly required: readonly ["resourceType", "name", "namespace"];
|
|
593
556
|
};
|
|
557
|
+
} | {
|
|
558
|
+
name: string;
|
|
559
|
+
description: string;
|
|
560
|
+
inputSchema: {
|
|
561
|
+
type: string;
|
|
562
|
+
properties: {};
|
|
563
|
+
required: never[];
|
|
564
|
+
};
|
|
594
565
|
})[];
|
|
595
566
|
export { allTools, destructiveTools };
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import { installHelmChart, installHelmChartSchema, upgradeHelmChart, upgradeHelmChartSchema, uninstallHelmChart, uninstallHelmChartSchema, } from "./tools/helm-operations.js";
|
|
5
5
|
import { explainResource, explainResourceSchema, listApiResources, listApiResourcesSchema, } from "./tools/kubectl-operations.js";
|
|
6
|
+
import { execInPod, execInPodSchema } from "./tools/exec_in_pod.js";
|
|
6
7
|
import { getResourceHandlers } from "./resources/handlers.js";
|
|
7
8
|
import { ListResourcesRequestSchema, ReadResourceRequestSchema, ListToolsRequestSchema, CallToolRequestSchema, ErrorCode, McpError, } from "@modelcontextprotocol/sdk/types.js";
|
|
8
9
|
import { KubernetesManager } from "./types.js";
|
|
@@ -14,7 +15,6 @@ import { kubectlScale, kubectlScaleSchema } from "./tools/kubectl-scale.js";
|
|
|
14
15
|
import { kubectlContext, kubectlContextSchema, } from "./tools/kubectl-context.js";
|
|
15
16
|
import { kubectlGet, kubectlGetSchema } from "./tools/kubectl-get.js";
|
|
16
17
|
import { kubectlDescribe, kubectlDescribeSchema, } from "./tools/kubectl-describe.js";
|
|
17
|
-
import { kubectlList, kubectlListSchema } from "./tools/kubectl-list.js";
|
|
18
18
|
import { kubectlApply, kubectlApplySchema } from "./tools/kubectl-apply.js";
|
|
19
19
|
import { kubectlDelete, kubectlDeleteSchema } from "./tools/kubectl-delete.js";
|
|
20
20
|
import { kubectlCreate, kubectlCreateSchema } from "./tools/kubectl-create.js";
|
|
@@ -22,6 +22,8 @@ import { kubectlLogs, kubectlLogsSchema } from "./tools/kubectl-logs.js";
|
|
|
22
22
|
import { kubectlGeneric, kubectlGenericSchema, } from "./tools/kubectl-generic.js";
|
|
23
23
|
import { kubectlPatch, kubectlPatchSchema } from "./tools/kubectl-patch.js";
|
|
24
24
|
import { kubectlRollout, kubectlRolloutSchema, } from "./tools/kubectl-rollout.js";
|
|
25
|
+
import { registerPromptHandlers } from "./prompts/index.js";
|
|
26
|
+
import { ping, pingSchema } from "./tools/ping.js";
|
|
25
27
|
// Check if non-destructive tools only mode is enabled
|
|
26
28
|
const nonDestructiveTools = process.env.ALLOW_ONLY_NON_DESTRUCTIVE_TOOLS === "true";
|
|
27
29
|
// Define destructive tools (delete and uninstall operations)
|
|
@@ -38,7 +40,6 @@ const allTools = [
|
|
|
38
40
|
// Unified kubectl-style tools - these replace many specific tools
|
|
39
41
|
kubectlGetSchema,
|
|
40
42
|
kubectlDescribeSchema,
|
|
41
|
-
kubectlListSchema,
|
|
42
43
|
kubectlApplySchema,
|
|
43
44
|
kubectlDeleteSchema,
|
|
44
45
|
kubectlCreateSchema,
|
|
@@ -57,20 +58,31 @@ const allTools = [
|
|
|
57
58
|
// Port forwarding
|
|
58
59
|
PortForwardSchema,
|
|
59
60
|
StopPortForwardSchema,
|
|
61
|
+
execInPodSchema,
|
|
60
62
|
// API resource operations
|
|
61
63
|
listApiResourcesSchema,
|
|
62
64
|
// Generic kubectl command
|
|
63
65
|
kubectlGenericSchema,
|
|
66
|
+
// Ping utility
|
|
67
|
+
pingSchema,
|
|
64
68
|
];
|
|
65
69
|
const k8sManager = new KubernetesManager();
|
|
66
70
|
const server = new Server({
|
|
67
71
|
name: serverConfig.name,
|
|
68
72
|
version: serverConfig.version,
|
|
69
|
-
},
|
|
73
|
+
}, {
|
|
74
|
+
...serverConfig,
|
|
75
|
+
capabilities: {
|
|
76
|
+
prompts: {},
|
|
77
|
+
...serverConfig.capabilities,
|
|
78
|
+
},
|
|
79
|
+
});
|
|
70
80
|
// Resources handlers
|
|
71
81
|
const resourceHandlers = getResourceHandlers(k8sManager);
|
|
72
82
|
server.setRequestHandler(ListResourcesRequestSchema, resourceHandlers.listResources);
|
|
73
83
|
server.setRequestHandler(ReadResourceRequestSchema, resourceHandlers.readResource);
|
|
84
|
+
// Register prompt handlers
|
|
85
|
+
registerPromptHandlers(server, k8sManager);
|
|
74
86
|
// Tools handlers
|
|
75
87
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
76
88
|
// Filter out destructive tools if ALLOW_ONLY_NON_DESTRUCTIVE_TOOLS is set to 'true'
|
|
@@ -92,9 +104,6 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
92
104
|
if (name === "kubectl_describe") {
|
|
93
105
|
return await kubectlDescribe(k8sManager, input);
|
|
94
106
|
}
|
|
95
|
-
if (name === "kubectl_list") {
|
|
96
|
-
return await kubectlList(k8sManager, input);
|
|
97
|
-
}
|
|
98
107
|
if (name === "kubectl_apply") {
|
|
99
108
|
return await kubectlApply(k8sManager, input);
|
|
100
109
|
}
|
|
@@ -165,6 +174,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
165
174
|
case "kubectl_scale": {
|
|
166
175
|
return await kubectlScale(k8sManager, input);
|
|
167
176
|
}
|
|
177
|
+
case "ping": {
|
|
178
|
+
return await ping();
|
|
179
|
+
}
|
|
180
|
+
case "exec_in_pod": {
|
|
181
|
+
return await execInPod(k8sManager, input);
|
|
182
|
+
}
|
|
168
183
|
default:
|
|
169
184
|
throw new McpError(ErrorCode.InvalidRequest, `Unknown tool: ${name}`);
|
|
170
185
|
}
|
|
@@ -664,3 +664,25 @@ export declare const DescribeNodeResponseSchema: z.ZodObject<{
|
|
|
664
664
|
text: string;
|
|
665
665
|
}[];
|
|
666
666
|
}>;
|
|
667
|
+
export declare const ExecInPodResponseSchema: z.ZodObject<{
|
|
668
|
+
content: z.ZodArray<z.ZodObject<{
|
|
669
|
+
type: z.ZodLiteral<"text">;
|
|
670
|
+
text: z.ZodString;
|
|
671
|
+
}, "strip", z.ZodTypeAny, {
|
|
672
|
+
type: "text";
|
|
673
|
+
text: string;
|
|
674
|
+
}, {
|
|
675
|
+
type: "text";
|
|
676
|
+
text: string;
|
|
677
|
+
}>, "many">;
|
|
678
|
+
}, "strip", z.ZodTypeAny, {
|
|
679
|
+
content: {
|
|
680
|
+
type: "text";
|
|
681
|
+
text: string;
|
|
682
|
+
}[];
|
|
683
|
+
}, {
|
|
684
|
+
content: {
|
|
685
|
+
type: "text";
|
|
686
|
+
text: string;
|
|
687
|
+
}[];
|
|
688
|
+
}>;
|
|
@@ -116,3 +116,6 @@ export const SetCurrentContextResponseSchema = z.object({
|
|
|
116
116
|
export const DescribeNodeResponseSchema = z.object({
|
|
117
117
|
content: z.array(ToolResponseContent),
|
|
118
118
|
});
|
|
119
|
+
export const ExecInPodResponseSchema = z.object({
|
|
120
|
+
content: z.array(ToolResponseContent),
|
|
121
|
+
});
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { ListPromptsRequestSchema, GetPromptRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
+
export function registerPromptHandlers(server, k8sManager) {
|
|
3
|
+
// Register prompts list handler
|
|
4
|
+
server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
5
|
+
return {
|
|
6
|
+
prompts: [
|
|
7
|
+
{
|
|
8
|
+
name: "k8s-diagnose",
|
|
9
|
+
description: "Diagnose Kubernetes Resources.",
|
|
10
|
+
arguments: [
|
|
11
|
+
{
|
|
12
|
+
name: "keyword",
|
|
13
|
+
description: "A keyword to search pod/node names.",
|
|
14
|
+
required: true,
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
name: "namespace",
|
|
18
|
+
description: "Optional: Specify a namespace to narrow down the search.",
|
|
19
|
+
required: false,
|
|
20
|
+
default: "all"
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
};
|
|
26
|
+
});
|
|
27
|
+
// Register prompt handler
|
|
28
|
+
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
29
|
+
const { name, arguments: args } = request.params;
|
|
30
|
+
if (name === "k8s-diagnose") {
|
|
31
|
+
const keyword = args?.keyword;
|
|
32
|
+
const namespace = args?.namespace;
|
|
33
|
+
if (!keyword) {
|
|
34
|
+
throw new Error("Keyword parameter is required for k8s-diagnose prompt");
|
|
35
|
+
}
|
|
36
|
+
const actualNamespace = namespace || "all";
|
|
37
|
+
const message = `Diagnose Kubernetes resources (pods, nodes, etc.) containing keyword "${keyword}" in their names within namespace "${actualNamespace}" (or across all namespaces if specified) for this investigation:
|
|
38
|
+
|
|
39
|
+
**Autonomous Kubernetes Diagnosis Flow**
|
|
40
|
+
|
|
41
|
+
0. **Perform Quick Health Checks / Golden Signals Analysis**
|
|
42
|
+
- Assess latency, errors, and resource utilization. If a clear issue is identified (e.g., node not ready, network partition), streamline or deprioritize subsequent detailed steps.
|
|
43
|
+
|
|
44
|
+
1. **Identify Resource Type and Scope**
|
|
45
|
+
- Determine the specific resource type (e.g., Pod, Node, Deployment, Service, Customresourcedefination) by analyzing labels, controller relationships, and initial observations.
|
|
46
|
+
- If needed we can use kubectl_explain tool to get list of resource type
|
|
47
|
+
- Note when you need customresourcedefinitions please use kubectl_explain
|
|
48
|
+
|
|
49
|
+
2. **Assess Current State**
|
|
50
|
+
- Check resource status (e.g., ready state, desired vs. current replicas for deployments).
|
|
51
|
+
- Identify any non-running or unhealthy states (e.g., CrashLoopBackOff, NotReady, Pending, Evicted).
|
|
52
|
+
- Review placement and distribution patterns across nodes.
|
|
53
|
+
|
|
54
|
+
3. **Analyze Operational History**
|
|
55
|
+
- Review recent events and warnings related to the resource.
|
|
56
|
+
- Check rollout history and update strategies for controllers (e.g., Deployments).
|
|
57
|
+
- Examine recent configuration changes or applied manifests.
|
|
58
|
+
|
|
59
|
+
4. **Inspect Runtime Behavior**
|
|
60
|
+
- Collect logs from current and previous instances for errors or anomalies (e.g., container logs for pods, system logs for nodes).
|
|
61
|
+
- Test intra-cluster networking and DNS resolution.
|
|
62
|
+
- Verify storage mounts, secret accessibility, and configuration usage.
|
|
63
|
+
|
|
64
|
+
5. **Evaluate Dependencies**
|
|
65
|
+
- Validate references to ConfigMaps, Secrets, and other dependent resources.
|
|
66
|
+
- Check associated service account permissions and RBAC rules.
|
|
67
|
+
- Confirm initContainers and sidecar containers have completed successfully or are running as expected.
|
|
68
|
+
|
|
69
|
+
6. **Audit Resource Constraints**
|
|
70
|
+
- Analyze CPU, memory, and storage usage trends against defined requests and limits.
|
|
71
|
+
- Check node allocatable resources and capacity.
|
|
72
|
+
- Review pod disruption budgets and quotas affecting the resource.
|
|
73
|
+
|
|
74
|
+
7. **Validate Cluster Context & Environment**
|
|
75
|
+
- Inspect node readiness, taints, and tolerations.
|
|
76
|
+
- Verify the current Kubernetes context and namespace.
|
|
77
|
+
- Confirm API server availability and connectivity.
|
|
78
|
+
- Check Kubernetes version compatibility (if applicable).
|
|
79
|
+
|
|
80
|
+
8. **Compare Against Patterns**
|
|
81
|
+
- Benchmark against workload-specific best practices and known healthy configurations.
|
|
82
|
+
- Verify liveness, readiness, and startup probe configurations.
|
|
83
|
+
- Audit security context settings and network policies.
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
**Instructions:**
|
|
88
|
+
- For each finding, clearly state the observation, its severity (e.g., \`CRITICAL\`, \`WARNING\`, \`INFO\`), and the evidence (e.g., \`kubectl output\`, error message in POD_NAME, timestamp). Also, print which object they found symptoms, e.g., error message in POD_NAME.
|
|
89
|
+
- If there are more than 4 relevant resources (e.g., pods, nodes), pick up to 3 resources which are exhibiting the most severe or illustrative symptoms.
|
|
90
|
+
- If there's a typo in user input and a closest matching object name exists, consider an auto-correction or suggest the correct name.
|
|
91
|
+
- Summarize the root cause clearly and concisely at the end of the investigation, along with clear, actionable steps for remediation, including specific \`kubectl\` commands or configuration changes required.
|
|
92
|
+
- If there is a node-level issue, thoroughly analyze it and explicitly post the findings.
|
|
93
|
+
- **Keep the output crisp, to the point, professional, direct, and systematic, avoiding verbose descriptions. Focus on actionable insights for engineers.**`;
|
|
94
|
+
return {
|
|
95
|
+
messages: [
|
|
96
|
+
{
|
|
97
|
+
role: "user",
|
|
98
|
+
content: {
|
|
99
|
+
type: "text",
|
|
100
|
+
text: message,
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
throw new Error(`Unknown prompt: ${name}`);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool: exec_in_pod
|
|
3
|
+
* Execute a command in a Kubernetes pod or container and return the output.
|
|
4
|
+
* Uses the official Kubernetes client-node Exec API for native execution.
|
|
5
|
+
* Supports both string and array command formats, and optional container targeting.
|
|
6
|
+
*/
|
|
7
|
+
import { KubernetesManager } from "../types.js";
|
|
8
|
+
/**
|
|
9
|
+
* Schema for exec_in_pod tool.
|
|
10
|
+
* - name: Pod name
|
|
11
|
+
* - namespace: Namespace (default: "default")
|
|
12
|
+
* - command: Command to execute (string or array of args)
|
|
13
|
+
* - container: (Optional) Container name
|
|
14
|
+
*/
|
|
15
|
+
export declare const execInPodSchema: {
|
|
16
|
+
name: string;
|
|
17
|
+
description: string;
|
|
18
|
+
inputSchema: {
|
|
19
|
+
type: string;
|
|
20
|
+
properties: {
|
|
21
|
+
name: {
|
|
22
|
+
type: string;
|
|
23
|
+
description: string;
|
|
24
|
+
};
|
|
25
|
+
namespace: {
|
|
26
|
+
type: string;
|
|
27
|
+
description: string;
|
|
28
|
+
default: string;
|
|
29
|
+
};
|
|
30
|
+
command: {
|
|
31
|
+
anyOf: ({
|
|
32
|
+
type: string;
|
|
33
|
+
items?: undefined;
|
|
34
|
+
} | {
|
|
35
|
+
type: string;
|
|
36
|
+
items: {
|
|
37
|
+
type: string;
|
|
38
|
+
};
|
|
39
|
+
})[];
|
|
40
|
+
description: string;
|
|
41
|
+
};
|
|
42
|
+
container: {
|
|
43
|
+
type: string;
|
|
44
|
+
description: string;
|
|
45
|
+
optional: boolean;
|
|
46
|
+
};
|
|
47
|
+
shell: {
|
|
48
|
+
type: string;
|
|
49
|
+
description: string;
|
|
50
|
+
optional: boolean;
|
|
51
|
+
};
|
|
52
|
+
timeout: {
|
|
53
|
+
type: string;
|
|
54
|
+
description: string;
|
|
55
|
+
optional: boolean;
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
required: string[];
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Execute a command in a Kubernetes pod or container using the Kubernetes client-node Exec API.
|
|
63
|
+
* Returns the stdout output as a text response.
|
|
64
|
+
* Throws McpError on failure.
|
|
65
|
+
*/
|
|
66
|
+
export declare function execInPod(k8sManager: KubernetesManager, input: {
|
|
67
|
+
name: string;
|
|
68
|
+
namespace?: string;
|
|
69
|
+
command: string | string[];
|
|
70
|
+
container?: string;
|
|
71
|
+
shell?: string;
|
|
72
|
+
timeout?: number;
|
|
73
|
+
}): Promise<{
|
|
74
|
+
content: {
|
|
75
|
+
type: string;
|
|
76
|
+
text: string;
|
|
77
|
+
}[];
|
|
78
|
+
}>;
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool: exec_in_pod
|
|
3
|
+
* Execute a command in a Kubernetes pod or container and return the output.
|
|
4
|
+
* Uses the official Kubernetes client-node Exec API for native execution.
|
|
5
|
+
* Supports both string and array command formats, and optional container targeting.
|
|
6
|
+
*/
|
|
7
|
+
import * as k8s from "@kubernetes/client-node";
|
|
8
|
+
import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
|
|
9
|
+
import { Writable } from "stream";
|
|
10
|
+
/**
|
|
11
|
+
* Schema for exec_in_pod tool.
|
|
12
|
+
* - name: Pod name
|
|
13
|
+
* - namespace: Namespace (default: "default")
|
|
14
|
+
* - command: Command to execute (string or array of args)
|
|
15
|
+
* - container: (Optional) Container name
|
|
16
|
+
*/
|
|
17
|
+
export const execInPodSchema = {
|
|
18
|
+
name: "exec_in_pod",
|
|
19
|
+
description: "Execute a command in a Kubernetes pod or container and return the output",
|
|
20
|
+
inputSchema: {
|
|
21
|
+
type: "object",
|
|
22
|
+
properties: {
|
|
23
|
+
name: {
|
|
24
|
+
type: "string",
|
|
25
|
+
description: "Name of the pod to execute the command in",
|
|
26
|
+
},
|
|
27
|
+
namespace: {
|
|
28
|
+
type: "string",
|
|
29
|
+
description: "Kubernetes namespace where the pod is located",
|
|
30
|
+
default: "default",
|
|
31
|
+
},
|
|
32
|
+
command: {
|
|
33
|
+
anyOf: [
|
|
34
|
+
{ type: "string" },
|
|
35
|
+
{ type: "array", items: { type: "string" } }
|
|
36
|
+
],
|
|
37
|
+
description: "Command to execute in the pod (string or array of args)",
|
|
38
|
+
},
|
|
39
|
+
container: {
|
|
40
|
+
type: "string",
|
|
41
|
+
description: "Container name (required when pod has multiple containers)",
|
|
42
|
+
optional: true,
|
|
43
|
+
},
|
|
44
|
+
shell: {
|
|
45
|
+
type: "string",
|
|
46
|
+
description: "Shell to use for command execution (e.g. '/bin/sh', '/bin/bash'). If not provided, will use command as-is.",
|
|
47
|
+
optional: true,
|
|
48
|
+
},
|
|
49
|
+
timeout: {
|
|
50
|
+
type: "number",
|
|
51
|
+
description: "Timeout for command - 60000 milliseconds if not specified",
|
|
52
|
+
optional: true,
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
required: ["name", "command"],
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Execute a command in a Kubernetes pod or container using the Kubernetes client-node Exec API.
|
|
60
|
+
* Returns the stdout output as a text response.
|
|
61
|
+
* Throws McpError on failure.
|
|
62
|
+
*/
|
|
63
|
+
export async function execInPod(k8sManager, input) {
|
|
64
|
+
const namespace = input.namespace || "default";
|
|
65
|
+
// Convert command to array of strings for the Exec API
|
|
66
|
+
let commandArr;
|
|
67
|
+
if (Array.isArray(input.command)) {
|
|
68
|
+
commandArr = input.command;
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
// Always wrap string commands in a shell for correct parsing
|
|
72
|
+
const shell = input.shell || "/bin/sh";
|
|
73
|
+
commandArr = [shell, "-c", input.command];
|
|
74
|
+
console.log("[exec_in_pod] Using shell:", shell, "Command array:", commandArr);
|
|
75
|
+
}
|
|
76
|
+
// Prepare buffers to capture stdout and stderr
|
|
77
|
+
let stdout = "";
|
|
78
|
+
let stderr = "";
|
|
79
|
+
// Use Node.js Writable streams to collect output
|
|
80
|
+
const stdoutStream = new Writable({
|
|
81
|
+
write(chunk, _encoding, callback) {
|
|
82
|
+
stdout += chunk.toString();
|
|
83
|
+
callback();
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
const stderrStream = new Writable({
|
|
87
|
+
write(chunk, _encoding, callback) {
|
|
88
|
+
stderr += chunk.toString();
|
|
89
|
+
callback();
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
// Add a dummy stdin stream
|
|
93
|
+
const stdinStream = new Writable({
|
|
94
|
+
write(_chunk, _encoding, callback) {
|
|
95
|
+
callback();
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
try {
|
|
99
|
+
// Use the Kubernetes client-node Exec API for native exec
|
|
100
|
+
const kc = k8sManager.getKubeConfig();
|
|
101
|
+
const exec = new k8s.Exec(kc);
|
|
102
|
+
// Add a timeout to avoid hanging forever if exec never returns
|
|
103
|
+
await new Promise((resolve, reject) => {
|
|
104
|
+
let finished = false;
|
|
105
|
+
const timeoutMs = input.timeout || 60000;
|
|
106
|
+
const timeout = setTimeout(() => {
|
|
107
|
+
if (!finished) {
|
|
108
|
+
finished = true;
|
|
109
|
+
reject(new McpError(ErrorCode.InternalError, "Exec operation timed out (possible networking, RBAC, or cluster issue)"));
|
|
110
|
+
}
|
|
111
|
+
}, timeoutMs);
|
|
112
|
+
console.log("[exec_in_pod] Calling exec.exec with params:", {
|
|
113
|
+
namespace,
|
|
114
|
+
pod: input.name,
|
|
115
|
+
container: input.container ?? "",
|
|
116
|
+
commandArr,
|
|
117
|
+
stdoutStreamType: typeof stdoutStream,
|
|
118
|
+
stderrStreamType: typeof stderrStream,
|
|
119
|
+
});
|
|
120
|
+
exec.exec(namespace, input.name, input.container ?? "", commandArr, stdoutStream, stderrStream, stdinStream, // use dummy stdin
|
|
121
|
+
true, // set tty to true
|
|
122
|
+
(status) => {
|
|
123
|
+
console.log("[exec_in_pod] exec.exec callback called. Status:", status);
|
|
124
|
+
if (finished)
|
|
125
|
+
return;
|
|
126
|
+
finished = true;
|
|
127
|
+
clearTimeout(timeout);
|
|
128
|
+
// Always resolve; handle errors based on stderr or thrown errors
|
|
129
|
+
resolve();
|
|
130
|
+
}).catch((err) => {
|
|
131
|
+
console.log("[exec_in_pod] exec.exec threw error:", err);
|
|
132
|
+
if (!finished) {
|
|
133
|
+
finished = true;
|
|
134
|
+
clearTimeout(timeout);
|
|
135
|
+
reject(new McpError(ErrorCode.InternalError, `Exec threw error: ${err?.message || err}`));
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
// Return the collected stdout as the result
|
|
140
|
+
// If there is stderr output or no output at all, treat as error
|
|
141
|
+
if (stderr || (!stdout && !stderr)) {
|
|
142
|
+
throw new McpError(ErrorCode.InternalError, `Failed to execute command in pod: ${stderr || "No output"}`);
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
content: [
|
|
146
|
+
{
|
|
147
|
+
type: "text",
|
|
148
|
+
text: stdout,
|
|
149
|
+
},
|
|
150
|
+
],
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
// Collect error message and stderr output if available
|
|
155
|
+
let message = error.message || "Unknown error";
|
|
156
|
+
if (stderr) {
|
|
157
|
+
message += "\n" + stderr;
|
|
158
|
+
}
|
|
159
|
+
throw new McpError(ErrorCode.InternalError, `Failed to execute command in pod: ${message}`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
@@ -293,12 +293,42 @@ function handleCommandError(error, resourceDescription) {
|
|
|
293
293
|
isError: true,
|
|
294
294
|
};
|
|
295
295
|
}
|
|
296
|
+
// Check for multi-container pod error
|
|
297
|
+
if (error.message.includes("a container name must be specified")) {
|
|
298
|
+
// Extract pod name and available containers from error message
|
|
299
|
+
const podNameMatch = error.message.match(/for pod ([^,]+)/);
|
|
300
|
+
const containersMatch = error.message.match(/choose one of: \[([^\]]+)\]/);
|
|
301
|
+
const initContainersMatch = error.message.match(/or one of the init containers: \[([^\]]+)\]/);
|
|
302
|
+
const podName = podNameMatch ? podNameMatch[1] : 'unknown';
|
|
303
|
+
const containers = containersMatch ? containersMatch[1].split(' ').map((c) => c.trim()) : [];
|
|
304
|
+
const initContainers = initContainersMatch ? initContainersMatch[1].split(' ').map((c) => c.trim()) : [];
|
|
305
|
+
// Generate structured context for the MCP client to make decisions
|
|
306
|
+
const context = {
|
|
307
|
+
error: "Multi-container pod requires container specification",
|
|
308
|
+
status: "multi_container_error",
|
|
309
|
+
pod_name: podName,
|
|
310
|
+
available_containers: containers,
|
|
311
|
+
init_containers: initContainers,
|
|
312
|
+
suggestion: `Please specify a container name using the 'container' parameter. Available containers: ${containers.join(', ')}${initContainers.length > 0 ? `. Init containers: ${initContainers.join(', ')}` : ''}`
|
|
313
|
+
};
|
|
314
|
+
return {
|
|
315
|
+
content: [
|
|
316
|
+
{
|
|
317
|
+
type: "text",
|
|
318
|
+
text: JSON.stringify(context, null, 2),
|
|
319
|
+
},
|
|
320
|
+
],
|
|
321
|
+
isError: true,
|
|
322
|
+
};
|
|
323
|
+
}
|
|
296
324
|
return {
|
|
297
325
|
content: [
|
|
298
326
|
{
|
|
299
327
|
type: "text",
|
|
300
328
|
text: JSON.stringify({
|
|
301
329
|
error: `Failed to get logs for ${resourceDescription}: ${error.message}`,
|
|
330
|
+
status: "general_error",
|
|
331
|
+
original_error: error.message
|
|
302
332
|
}, null, 2),
|
|
303
333
|
},
|
|
304
334
|
],
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export const pingSchema = {
|
|
2
|
+
name: "ping",
|
|
3
|
+
description: "Verify that the counterpart is still responsive and the connection is alive.",
|
|
4
|
+
inputSchema: {
|
|
5
|
+
type: "object",
|
|
6
|
+
properties: {},
|
|
7
|
+
required: [],
|
|
8
|
+
},
|
|
9
|
+
};
|
|
10
|
+
export async function ping() {
|
|
11
|
+
return {};
|
|
12
|
+
}
|
package/package.json
CHANGED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { KubernetesManager } from "../types.js";
|
|
2
|
-
export declare const kubectlListSchema: {
|
|
3
|
-
readonly name: "kubectl_list";
|
|
4
|
-
readonly description: "List Kubernetes resources by resource type and optionally namespace";
|
|
5
|
-
readonly inputSchema: {
|
|
6
|
-
readonly type: "object";
|
|
7
|
-
readonly properties: {
|
|
8
|
-
readonly resourceType: {
|
|
9
|
-
readonly type: "string";
|
|
10
|
-
readonly description: "Type of resource to list (e.g., pods, deployments, services, configmaps, etc.)";
|
|
11
|
-
};
|
|
12
|
-
readonly namespace: {
|
|
13
|
-
readonly type: "string";
|
|
14
|
-
readonly description: "Namespace of the resources (optional - defaults to 'default' for namespaced resources)";
|
|
15
|
-
readonly default: "default";
|
|
16
|
-
};
|
|
17
|
-
readonly output: {
|
|
18
|
-
readonly type: "string";
|
|
19
|
-
readonly enum: readonly ["json", "yaml", "wide", "name", "custom", "formatted"];
|
|
20
|
-
readonly description: "Output format - 'formatted' uses a resource-specific format with key information";
|
|
21
|
-
readonly default: "formatted";
|
|
22
|
-
};
|
|
23
|
-
readonly allNamespaces: {
|
|
24
|
-
readonly type: "boolean";
|
|
25
|
-
readonly description: "If true, list resources across all namespaces";
|
|
26
|
-
readonly default: false;
|
|
27
|
-
};
|
|
28
|
-
readonly labelSelector: {
|
|
29
|
-
readonly type: "string";
|
|
30
|
-
readonly description: "Filter resources by label selector (e.g. 'app=nginx')";
|
|
31
|
-
};
|
|
32
|
-
readonly fieldSelector: {
|
|
33
|
-
readonly type: "string";
|
|
34
|
-
readonly description: "Filter resources by field selector (e.g. 'metadata.name=my-pod')";
|
|
35
|
-
};
|
|
36
|
-
};
|
|
37
|
-
readonly required: readonly ["resourceType", "namespace"];
|
|
38
|
-
};
|
|
39
|
-
};
|
|
40
|
-
export declare function kubectlList(k8sManager: KubernetesManager, input: {
|
|
41
|
-
resourceType: string;
|
|
42
|
-
namespace?: string;
|
|
43
|
-
output?: string;
|
|
44
|
-
allNamespaces?: boolean;
|
|
45
|
-
labelSelector?: string;
|
|
46
|
-
fieldSelector?: string;
|
|
47
|
-
}): Promise<{
|
|
48
|
-
content: {
|
|
49
|
-
type: string;
|
|
50
|
-
text: string;
|
|
51
|
-
}[];
|
|
52
|
-
isError?: undefined;
|
|
53
|
-
} | {
|
|
54
|
-
content: {
|
|
55
|
-
type: string;
|
|
56
|
-
text: string;
|
|
57
|
-
}[];
|
|
58
|
-
isError: boolean;
|
|
59
|
-
}>;
|
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
import { kubectlGet } from "./kubectl-get.js";
|
|
2
|
-
import { execSync } from "child_process";
|
|
3
|
-
import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
|
|
4
|
-
export const kubectlListSchema = {
|
|
5
|
-
name: "kubectl_list",
|
|
6
|
-
description: "List Kubernetes resources by resource type and optionally namespace",
|
|
7
|
-
inputSchema: {
|
|
8
|
-
type: "object",
|
|
9
|
-
properties: {
|
|
10
|
-
resourceType: {
|
|
11
|
-
type: "string",
|
|
12
|
-
description: "Type of resource to list (e.g., pods, deployments, services, configmaps, etc.)"
|
|
13
|
-
},
|
|
14
|
-
namespace: {
|
|
15
|
-
type: "string",
|
|
16
|
-
description: "Namespace of the resources (optional - defaults to 'default' for namespaced resources)",
|
|
17
|
-
default: "default"
|
|
18
|
-
},
|
|
19
|
-
output: {
|
|
20
|
-
type: "string",
|
|
21
|
-
enum: ["json", "yaml", "wide", "name", "custom", "formatted"],
|
|
22
|
-
description: "Output format - 'formatted' uses a resource-specific format with key information",
|
|
23
|
-
default: "formatted"
|
|
24
|
-
},
|
|
25
|
-
allNamespaces: {
|
|
26
|
-
type: "boolean",
|
|
27
|
-
description: "If true, list resources across all namespaces",
|
|
28
|
-
default: false
|
|
29
|
-
},
|
|
30
|
-
labelSelector: {
|
|
31
|
-
type: "string",
|
|
32
|
-
description: "Filter resources by label selector (e.g. 'app=nginx')"
|
|
33
|
-
},
|
|
34
|
-
fieldSelector: {
|
|
35
|
-
type: "string",
|
|
36
|
-
description: "Filter resources by field selector (e.g. 'metadata.name=my-pod')"
|
|
37
|
-
}
|
|
38
|
-
},
|
|
39
|
-
required: ["resourceType", "namespace"],
|
|
40
|
-
},
|
|
41
|
-
};
|
|
42
|
-
export async function kubectlList(k8sManager, input) {
|
|
43
|
-
try {
|
|
44
|
-
const resourceType = input.resourceType.toLowerCase();
|
|
45
|
-
const namespace = input.namespace || "default";
|
|
46
|
-
const output = input.output || "formatted";
|
|
47
|
-
const allNamespaces = input.allNamespaces || false;
|
|
48
|
-
const labelSelector = input.labelSelector || "";
|
|
49
|
-
const fieldSelector = input.fieldSelector || "";
|
|
50
|
-
// If not using formatted output, delegate to kubectl_get
|
|
51
|
-
if (output !== "formatted") {
|
|
52
|
-
return await kubectlGet(k8sManager, {
|
|
53
|
-
resourceType: input.resourceType,
|
|
54
|
-
namespace: input.namespace,
|
|
55
|
-
output: input.output,
|
|
56
|
-
allNamespaces: input.allNamespaces,
|
|
57
|
-
labelSelector: input.labelSelector,
|
|
58
|
-
fieldSelector: input.fieldSelector
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
// For formatted output, we'll use resource-specific custom columns
|
|
62
|
-
let customColumns = "";
|
|
63
|
-
switch (resourceType) {
|
|
64
|
-
case "pods":
|
|
65
|
-
case "pod":
|
|
66
|
-
case "po":
|
|
67
|
-
customColumns = "NAME:.metadata.name,NAMESPACE:.metadata.namespace,STATUS:.status.phase,NODE:.spec.nodeName,IP:.status.podIP,AGE:.metadata.creationTimestamp";
|
|
68
|
-
break;
|
|
69
|
-
case "deployments":
|
|
70
|
-
case "deployment":
|
|
71
|
-
case "deploy":
|
|
72
|
-
customColumns = "NAME:.metadata.name,NAMESPACE:.metadata.namespace,READY:.status.readyReplicas/.status.replicas,UP-TO-DATE:.status.updatedReplicas,AVAILABLE:.status.availableReplicas,AGE:.metadata.creationTimestamp";
|
|
73
|
-
break;
|
|
74
|
-
case "services":
|
|
75
|
-
case "service":
|
|
76
|
-
case "svc":
|
|
77
|
-
customColumns = "NAME:.metadata.name,NAMESPACE:.metadata.namespace,TYPE:.spec.type,CLUSTER-IP:.spec.clusterIP,EXTERNAL-IP:.status.loadBalancer.ingress[0].ip,PORTS:.spec.ports[*].port,AGE:.metadata.creationTimestamp";
|
|
78
|
-
break;
|
|
79
|
-
case "nodes":
|
|
80
|
-
case "node":
|
|
81
|
-
case "no":
|
|
82
|
-
customColumns = "NAME:.metadata.name,STATUS:.status.conditions[?(@.type==\"Ready\")].status,ROLES:.metadata.labels.kubernetes\\.io/role,VERSION:.status.nodeInfo.kubeletVersion,INTERNAL-IP:.status.addresses[?(@.type==\"InternalIP\")].address,OS-IMAGE:.status.nodeInfo.osImage,KERNEL-VERSION:.status.nodeInfo.kernelVersion,CONTAINER-RUNTIME:.status.nodeInfo.containerRuntimeVersion";
|
|
83
|
-
break;
|
|
84
|
-
case "namespaces":
|
|
85
|
-
case "namespace":
|
|
86
|
-
case "ns":
|
|
87
|
-
customColumns = "NAME:.metadata.name,STATUS:.status.phase,AGE:.metadata.creationTimestamp";
|
|
88
|
-
break;
|
|
89
|
-
case "persistentvolumes":
|
|
90
|
-
case "pv":
|
|
91
|
-
customColumns = "NAME:.metadata.name,CAPACITY:.spec.capacity.storage,ACCESS_MODES:.spec.accessModes,RECLAIM_POLICY:.spec.persistentVolumeReclaimPolicy,STATUS:.status.phase,CLAIM:.spec.claimRef.name,STORAGECLASS:.spec.storageClassName,AGE:.metadata.creationTimestamp";
|
|
92
|
-
break;
|
|
93
|
-
case "persistentvolumeclaims":
|
|
94
|
-
case "pvc":
|
|
95
|
-
customColumns = "NAME:.metadata.name,NAMESPACE:.metadata.namespace,STATUS:.status.phase,VOLUME:.spec.volumeName,CAPACITY:.status.capacity.storage,ACCESS_MODES:.spec.accessModes,STORAGECLASS:.spec.storageClassName,AGE:.metadata.creationTimestamp";
|
|
96
|
-
break;
|
|
97
|
-
case "configmaps":
|
|
98
|
-
case "configmap":
|
|
99
|
-
case "cm":
|
|
100
|
-
customColumns = "NAME:.metadata.name,NAMESPACE:.metadata.namespace,DATA:.data,AGE:.metadata.creationTimestamp";
|
|
101
|
-
break;
|
|
102
|
-
case "secrets":
|
|
103
|
-
case "secret":
|
|
104
|
-
customColumns = "NAME:.metadata.name,NAMESPACE:.metadata.namespace,TYPE:.type,DATA:.data,AGE:.metadata.creationTimestamp";
|
|
105
|
-
break;
|
|
106
|
-
case "jobs":
|
|
107
|
-
case "job":
|
|
108
|
-
customColumns = "NAME:.metadata.name,NAMESPACE:.metadata.namespace,COMPLETIONS:.status.succeeded/.spec.completions,DURATION:.status.completionTime-(.status.startTime),AGE:.metadata.creationTimestamp";
|
|
109
|
-
break;
|
|
110
|
-
case "cronjobs":
|
|
111
|
-
case "cronjob":
|
|
112
|
-
case "cj":
|
|
113
|
-
customColumns = "NAME:.metadata.name,NAMESPACE:.metadata.namespace,SCHEDULE:.spec.schedule,SUSPEND:.spec.suspend,ACTIVE:.status.active,LAST_SCHEDULE:.status.lastScheduleTime,AGE:.metadata.creationTimestamp";
|
|
114
|
-
break;
|
|
115
|
-
default:
|
|
116
|
-
// For unknown resource types, fall back to a generic format
|
|
117
|
-
customColumns = "NAME:.metadata.name,NAMESPACE:.metadata.namespace,KIND:.kind,AGE:.metadata.creationTimestamp";
|
|
118
|
-
break;
|
|
119
|
-
}
|
|
120
|
-
// Build the kubectl command
|
|
121
|
-
let command = "kubectl get ";
|
|
122
|
-
// Add resource type
|
|
123
|
-
command += resourceType;
|
|
124
|
-
// Add namespace flag unless all namespaces is specified
|
|
125
|
-
if (allNamespaces) {
|
|
126
|
-
command += " --all-namespaces";
|
|
127
|
-
}
|
|
128
|
-
else if (namespace && !isNonNamespacedResource(resourceType)) {
|
|
129
|
-
command += ` -n ${namespace}`;
|
|
130
|
-
}
|
|
131
|
-
// Add label selector if provided
|
|
132
|
-
if (labelSelector) {
|
|
133
|
-
command += ` -l ${labelSelector}`;
|
|
134
|
-
}
|
|
135
|
-
// Add field selector if provided
|
|
136
|
-
if (fieldSelector) {
|
|
137
|
-
command += ` --field-selector=${fieldSelector}`;
|
|
138
|
-
}
|
|
139
|
-
// Add custom columns format
|
|
140
|
-
command += ` -o custom-columns="${customColumns}"`;
|
|
141
|
-
// Execute the command
|
|
142
|
-
try {
|
|
143
|
-
const result = execSync(command, { encoding: "utf8", env: { ...process.env, KUBECONFIG: process.env.KUBECONFIG } });
|
|
144
|
-
return {
|
|
145
|
-
content: [
|
|
146
|
-
{
|
|
147
|
-
type: "text",
|
|
148
|
-
text: result,
|
|
149
|
-
},
|
|
150
|
-
],
|
|
151
|
-
};
|
|
152
|
-
}
|
|
153
|
-
catch (error) {
|
|
154
|
-
if (error.status === 404 || error.message.includes("not found")) {
|
|
155
|
-
return {
|
|
156
|
-
content: [
|
|
157
|
-
{
|
|
158
|
-
type: "text",
|
|
159
|
-
text: JSON.stringify({
|
|
160
|
-
error: `Resource type ${resourceType} not found or no resources exist`,
|
|
161
|
-
status: "not_found",
|
|
162
|
-
}, null, 2),
|
|
163
|
-
},
|
|
164
|
-
],
|
|
165
|
-
isError: true,
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
throw new McpError(ErrorCode.InternalError, `Failed to list resources: ${error.message}`);
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
catch (error) {
|
|
172
|
-
throw new McpError(ErrorCode.InternalError, `Failed to execute kubectl list command: ${error.message}`);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
// Helper function to determine if a resource is non-namespaced
|
|
176
|
-
function isNonNamespacedResource(resourceType) {
|
|
177
|
-
const nonNamespacedResources = [
|
|
178
|
-
"nodes", "node", "no",
|
|
179
|
-
"namespaces", "namespace", "ns",
|
|
180
|
-
"persistentvolumes", "pv",
|
|
181
|
-
"storageclasses", "sc",
|
|
182
|
-
"clusterroles",
|
|
183
|
-
"clusterrolebindings",
|
|
184
|
-
"customresourcedefinitions", "crd", "crds"
|
|
185
|
-
];
|
|
186
|
-
return nonNamespacedResources.includes(resourceType.toLowerCase());
|
|
187
|
-
}
|