mcp-server-kubernetes 1.6.2 → 2.1.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.
Files changed (98) hide show
  1. package/README.md +46 -22
  2. package/dist/config/container-templates.d.ts +2 -2
  3. package/dist/index.d.ts +314 -736
  4. package/dist/index.js +93 -200
  5. package/dist/models/resource-models.d.ts +6 -6
  6. package/dist/tools/kubectl-apply.d.ts +46 -0
  7. package/dist/tools/kubectl-apply.js +110 -0
  8. package/dist/tools/kubectl-context.d.ts +49 -0
  9. package/dist/tools/kubectl-context.js +233 -0
  10. package/dist/tools/kubectl-create.d.ts +150 -0
  11. package/dist/tools/kubectl-create.js +321 -0
  12. package/dist/tools/kubectl-delete.d.ts +77 -0
  13. package/dist/tools/kubectl-delete.js +177 -0
  14. package/dist/tools/kubectl-describe.d.ts +47 -0
  15. package/dist/tools/kubectl-describe.js +96 -0
  16. package/dist/tools/kubectl-generic.d.ts +71 -0
  17. package/dist/tools/kubectl-generic.js +121 -0
  18. package/dist/tools/kubectl-get.d.ts +72 -0
  19. package/dist/tools/kubectl-get.js +251 -0
  20. package/dist/tools/kubectl-list.d.ts +61 -0
  21. package/dist/tools/kubectl-list.js +189 -0
  22. package/dist/tools/{get_logs.d.ts → kubectl-logs.d.ts} +35 -19
  23. package/dist/tools/kubectl-logs.js +312 -0
  24. package/dist/tools/kubectl-patch.d.ts +57 -0
  25. package/dist/tools/kubectl-patch.js +128 -0
  26. package/dist/tools/kubectl-rollout.d.ts +67 -0
  27. package/dist/tools/kubectl-rollout.js +115 -0
  28. package/dist/tools/{scale_deployment.d.ts → kubectl-scale.d.ts} +13 -3
  29. package/dist/tools/kubectl-scale.js +73 -0
  30. package/dist/utils/kubernetes-manager.d.ts +4 -0
  31. package/dist/utils/kubernetes-manager.js +21 -3
  32. package/package.json +1 -1
  33. package/dist/tools/create_configmap.d.ts +0 -33
  34. package/dist/tools/create_configmap.js +0 -66
  35. package/dist/tools/create_cronjob.d.ts +0 -47
  36. package/dist/tools/create_cronjob.js +0 -93
  37. package/dist/tools/create_deployment.d.ts +0 -135
  38. package/dist/tools/create_deployment.js +0 -162
  39. package/dist/tools/create_namespace.d.ts +0 -22
  40. package/dist/tools/create_namespace.js +0 -48
  41. package/dist/tools/create_pod.d.ts +0 -130
  42. package/dist/tools/create_pod.js +0 -153
  43. package/dist/tools/create_service.d.ts +0 -74
  44. package/dist/tools/create_service.js +0 -102
  45. package/dist/tools/delete_configmap.d.ts +0 -26
  46. package/dist/tools/delete_configmap.js +0 -49
  47. package/dist/tools/delete_cronjob.d.ts +0 -26
  48. package/dist/tools/delete_cronjob.js +0 -48
  49. package/dist/tools/delete_deployment.d.ts +0 -31
  50. package/dist/tools/delete_deployment.js +0 -47
  51. package/dist/tools/delete_namespace.d.ts +0 -27
  52. package/dist/tools/delete_namespace.js +0 -44
  53. package/dist/tools/delete_pod.d.ts +0 -31
  54. package/dist/tools/delete_pod.js +0 -45
  55. package/dist/tools/delete_service.d.ts +0 -32
  56. package/dist/tools/delete_service.js +0 -46
  57. package/dist/tools/describe_cronjob.d.ts +0 -27
  58. package/dist/tools/describe_cronjob.js +0 -83
  59. package/dist/tools/describe_deployment.d.ts +0 -26
  60. package/dist/tools/describe_deployment.js +0 -40
  61. package/dist/tools/describe_node.d.ts +0 -22
  62. package/dist/tools/describe_node.js +0 -84
  63. package/dist/tools/describe_pod.d.ts +0 -33
  64. package/dist/tools/describe_pod.js +0 -81
  65. package/dist/tools/describe_service.d.ts +0 -34
  66. package/dist/tools/describe_service.js +0 -85
  67. package/dist/tools/get_configmap.d.ts +0 -27
  68. package/dist/tools/get_configmap.js +0 -48
  69. package/dist/tools/get_current_context.d.ts +0 -23
  70. package/dist/tools/get_current_context.js +0 -55
  71. package/dist/tools/get_events.d.ts +0 -28
  72. package/dist/tools/get_events.js +0 -66
  73. package/dist/tools/get_job_logs.d.ts +0 -40
  74. package/dist/tools/get_job_logs.js +0 -104
  75. package/dist/tools/get_logs.js +0 -150
  76. package/dist/tools/list_contexts.d.ts +0 -23
  77. package/dist/tools/list_contexts.js +0 -39
  78. package/dist/tools/list_cronjobs.d.ts +0 -23
  79. package/dist/tools/list_cronjobs.js +0 -35
  80. package/dist/tools/list_deployments.d.ts +0 -23
  81. package/dist/tools/list_deployments.js +0 -30
  82. package/dist/tools/list_jobs.d.ts +0 -29
  83. package/dist/tools/list_jobs.js +0 -77
  84. package/dist/tools/list_nodes.d.ts +0 -15
  85. package/dist/tools/list_nodes.js +0 -21
  86. package/dist/tools/list_pods.d.ts +0 -23
  87. package/dist/tools/list_pods.js +0 -29
  88. package/dist/tools/list_services.d.ts +0 -23
  89. package/dist/tools/list_services.js +0 -31
  90. package/dist/tools/scale_deployment.js +0 -50
  91. package/dist/tools/set_current_context.d.ts +0 -23
  92. package/dist/tools/set_current_context.js +0 -35
  93. package/dist/tools/update_configmap.d.ts +0 -33
  94. package/dist/tools/update_configmap.js +0 -71
  95. package/dist/tools/update_deployment.d.ts +0 -113
  96. package/dist/tools/update_deployment.js +0 -155
  97. package/dist/tools/update_service.d.ts +0 -72
  98. package/dist/tools/update_service.js +0 -125
@@ -0,0 +1,189 @@
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
+ optional: true
34
+ },
35
+ fieldSelector: {
36
+ type: "string",
37
+ description: "Filter resources by field selector (e.g. 'metadata.name=my-pod')",
38
+ optional: true
39
+ }
40
+ },
41
+ required: ["resourceType"],
42
+ },
43
+ };
44
+ export async function kubectlList(k8sManager, input) {
45
+ try {
46
+ const resourceType = input.resourceType.toLowerCase();
47
+ const namespace = input.namespace || "default";
48
+ const output = input.output || "formatted";
49
+ const allNamespaces = input.allNamespaces || false;
50
+ const labelSelector = input.labelSelector || "";
51
+ const fieldSelector = input.fieldSelector || "";
52
+ // If not using formatted output, delegate to kubectl_get
53
+ if (output !== "formatted") {
54
+ return await kubectlGet(k8sManager, {
55
+ resourceType: input.resourceType,
56
+ namespace: input.namespace,
57
+ output: input.output,
58
+ allNamespaces: input.allNamespaces,
59
+ labelSelector: input.labelSelector,
60
+ fieldSelector: input.fieldSelector
61
+ });
62
+ }
63
+ // For formatted output, we'll use resource-specific custom columns
64
+ let customColumns = "";
65
+ switch (resourceType) {
66
+ case "pods":
67
+ case "pod":
68
+ case "po":
69
+ customColumns = "NAME:.metadata.name,NAMESPACE:.metadata.namespace,STATUS:.status.phase,NODE:.spec.nodeName,IP:.status.podIP,AGE:.metadata.creationTimestamp";
70
+ break;
71
+ case "deployments":
72
+ case "deployment":
73
+ case "deploy":
74
+ customColumns = "NAME:.metadata.name,NAMESPACE:.metadata.namespace,READY:.status.readyReplicas/.status.replicas,UP-TO-DATE:.status.updatedReplicas,AVAILABLE:.status.availableReplicas,AGE:.metadata.creationTimestamp";
75
+ break;
76
+ case "services":
77
+ case "service":
78
+ case "svc":
79
+ 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";
80
+ break;
81
+ case "nodes":
82
+ case "node":
83
+ case "no":
84
+ 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";
85
+ break;
86
+ case "namespaces":
87
+ case "namespace":
88
+ case "ns":
89
+ customColumns = "NAME:.metadata.name,STATUS:.status.phase,AGE:.metadata.creationTimestamp";
90
+ break;
91
+ case "persistentvolumes":
92
+ case "pv":
93
+ 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";
94
+ break;
95
+ case "persistentvolumeclaims":
96
+ case "pvc":
97
+ 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";
98
+ break;
99
+ case "configmaps":
100
+ case "configmap":
101
+ case "cm":
102
+ customColumns = "NAME:.metadata.name,NAMESPACE:.metadata.namespace,DATA:.data,AGE:.metadata.creationTimestamp";
103
+ break;
104
+ case "secrets":
105
+ case "secret":
106
+ customColumns = "NAME:.metadata.name,NAMESPACE:.metadata.namespace,TYPE:.type,DATA:.data,AGE:.metadata.creationTimestamp";
107
+ break;
108
+ case "jobs":
109
+ case "job":
110
+ customColumns = "NAME:.metadata.name,NAMESPACE:.metadata.namespace,COMPLETIONS:.status.succeeded/.spec.completions,DURATION:.status.completionTime-(.status.startTime),AGE:.metadata.creationTimestamp";
111
+ break;
112
+ case "cronjobs":
113
+ case "cronjob":
114
+ case "cj":
115
+ customColumns = "NAME:.metadata.name,NAMESPACE:.metadata.namespace,SCHEDULE:.spec.schedule,SUSPEND:.spec.suspend,ACTIVE:.status.active,LAST_SCHEDULE:.status.lastScheduleTime,AGE:.metadata.creationTimestamp";
116
+ break;
117
+ default:
118
+ // For unknown resource types, fall back to a generic format
119
+ customColumns = "NAME:.metadata.name,NAMESPACE:.metadata.namespace,KIND:.kind,AGE:.metadata.creationTimestamp";
120
+ break;
121
+ }
122
+ // Build the kubectl command
123
+ let command = "kubectl get ";
124
+ // Add resource type
125
+ command += resourceType;
126
+ // Add namespace flag unless all namespaces is specified
127
+ if (allNamespaces) {
128
+ command += " --all-namespaces";
129
+ }
130
+ else if (namespace && !isNonNamespacedResource(resourceType)) {
131
+ command += ` -n ${namespace}`;
132
+ }
133
+ // Add label selector if provided
134
+ if (labelSelector) {
135
+ command += ` -l ${labelSelector}`;
136
+ }
137
+ // Add field selector if provided
138
+ if (fieldSelector) {
139
+ command += ` --field-selector=${fieldSelector}`;
140
+ }
141
+ // Add custom columns format
142
+ command += ` -o custom-columns="${customColumns}"`;
143
+ // Execute the command
144
+ try {
145
+ const result = execSync(command, { encoding: "utf8" });
146
+ return {
147
+ content: [
148
+ {
149
+ type: "text",
150
+ text: result,
151
+ },
152
+ ],
153
+ };
154
+ }
155
+ catch (error) {
156
+ if (error.status === 404 || error.message.includes("not found")) {
157
+ return {
158
+ content: [
159
+ {
160
+ type: "text",
161
+ text: JSON.stringify({
162
+ error: `Resource type ${resourceType} not found or no resources exist`,
163
+ status: "not_found",
164
+ }, null, 2),
165
+ },
166
+ ],
167
+ isError: true,
168
+ };
169
+ }
170
+ throw new McpError(ErrorCode.InternalError, `Failed to list resources: ${error.message}`);
171
+ }
172
+ }
173
+ catch (error) {
174
+ throw new McpError(ErrorCode.InternalError, `Failed to execute kubectl list command: ${error.message}`);
175
+ }
176
+ }
177
+ // Helper function to determine if a resource is non-namespaced
178
+ function isNonNamespacedResource(resourceType) {
179
+ const nonNamespacedResources = [
180
+ "nodes", "node", "no",
181
+ "namespaces", "namespace", "ns",
182
+ "persistentvolumes", "pv",
183
+ "storageclasses", "sc",
184
+ "clusterroles",
185
+ "clusterrolebindings",
186
+ "customresourcedefinitions", "crd", "crds"
187
+ ];
188
+ return nonNamespacedResources.includes(resourceType.toLowerCase());
189
+ }
@@ -1,13 +1,13 @@
1
1
  import { KubernetesManager } from "../types.js";
2
- export declare const getLogsSchema: {
3
- readonly name: "get_logs";
4
- readonly description: "Get logs from pods, deployments, jobs, or resources matching a label selector";
2
+ export declare const kubectlLogsSchema: {
3
+ readonly name: "kubectl_logs";
4
+ readonly description: "Get logs from Kubernetes resources like pods, deployments, or jobs";
5
5
  readonly inputSchema: {
6
6
  readonly type: "object";
7
7
  readonly properties: {
8
8
  readonly resourceType: {
9
9
  readonly type: "string";
10
- readonly enum: readonly ["pod", "deployment", "job"];
10
+ readonly enum: readonly ["pod", "deployment", "job", "cronjob"];
11
11
  readonly description: "Type of resource to get logs from";
12
12
  };
13
13
  readonly name: {
@@ -19,11 +19,6 @@ export declare const getLogsSchema: {
19
19
  readonly description: "Namespace of the resource";
20
20
  readonly default: "default";
21
21
  };
22
- readonly labelSelector: {
23
- readonly type: "string";
24
- readonly description: "Label selector to filter resources";
25
- readonly optional: true;
26
- };
27
22
  readonly container: {
28
23
  readonly type: "string";
29
24
  readonly description: "Container name (required when pod has multiple containers)";
@@ -35,8 +30,13 @@ export declare const getLogsSchema: {
35
30
  readonly optional: true;
36
31
  };
37
32
  readonly since: {
38
- readonly type: "number";
39
- readonly description: "Get logs since relative time in seconds";
33
+ readonly type: "string";
34
+ readonly description: "Show logs since relative time (e.g. '5s', '2m', '3h')";
35
+ readonly optional: true;
36
+ };
37
+ readonly sinceTime: {
38
+ readonly type: "string";
39
+ readonly description: "Show logs since absolute time (RFC3339)";
40
40
  readonly optional: true;
41
41
  };
42
42
  readonly timestamps: {
@@ -44,21 +44,37 @@ export declare const getLogsSchema: {
44
44
  readonly description: "Include timestamps in logs";
45
45
  readonly default: false;
46
46
  };
47
+ readonly previous: {
48
+ readonly type: "boolean";
49
+ readonly description: "Include logs from previously terminated containers";
50
+ readonly default: false;
51
+ };
52
+ readonly follow: {
53
+ readonly type: "boolean";
54
+ readonly description: "Follow logs output (not recommended, may cause timeouts)";
55
+ readonly default: false;
56
+ };
57
+ readonly labelSelector: {
58
+ readonly type: "string";
59
+ readonly description: "Filter resources by label selector";
60
+ readonly optional: true;
61
+ };
47
62
  };
48
- readonly required: readonly ["resourceType"];
63
+ readonly required: readonly ["resourceType", "name", "namespace"];
49
64
  };
50
65
  };
51
- export declare function getLogs(k8sManager: KubernetesManager, input: {
66
+ export declare function kubectlLogs(k8sManager: KubernetesManager, input: {
52
67
  resourceType: string;
53
- name?: string;
54
- namespace?: string;
55
- labelSelector?: string;
68
+ name: string;
69
+ namespace: string;
56
70
  container?: string;
57
71
  tail?: number;
58
- sinceSeconds?: number;
72
+ since?: string;
73
+ sinceTime?: string;
59
74
  timestamps?: boolean;
60
- pretty?: boolean;
61
- follow?: false;
75
+ previous?: boolean;
76
+ follow?: boolean;
77
+ labelSelector?: string;
62
78
  }): Promise<{
63
79
  content: {
64
80
  type: string;
@@ -0,0 +1,312 @@
1
+ import { execSync } from "child_process";
2
+ import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
3
+ export const kubectlLogsSchema = {
4
+ name: "kubectl_logs",
5
+ description: "Get logs from Kubernetes resources like pods, deployments, or jobs",
6
+ inputSchema: {
7
+ type: "object",
8
+ properties: {
9
+ resourceType: {
10
+ type: "string",
11
+ enum: ["pod", "deployment", "job", "cronjob"],
12
+ description: "Type of resource to get logs from",
13
+ },
14
+ name: {
15
+ type: "string",
16
+ description: "Name of the resource",
17
+ },
18
+ namespace: {
19
+ type: "string",
20
+ description: "Namespace of the resource",
21
+ default: "default",
22
+ },
23
+ container: {
24
+ type: "string",
25
+ description: "Container name (required when pod has multiple containers)",
26
+ optional: true,
27
+ },
28
+ tail: {
29
+ type: "number",
30
+ description: "Number of lines to show from end of logs",
31
+ optional: true,
32
+ },
33
+ since: {
34
+ type: "string",
35
+ description: "Show logs since relative time (e.g. '5s', '2m', '3h')",
36
+ optional: true,
37
+ },
38
+ sinceTime: {
39
+ type: "string",
40
+ description: "Show logs since absolute time (RFC3339)",
41
+ optional: true,
42
+ },
43
+ timestamps: {
44
+ type: "boolean",
45
+ description: "Include timestamps in logs",
46
+ default: false,
47
+ },
48
+ previous: {
49
+ type: "boolean",
50
+ description: "Include logs from previously terminated containers",
51
+ default: false,
52
+ },
53
+ follow: {
54
+ type: "boolean",
55
+ description: "Follow logs output (not recommended, may cause timeouts)",
56
+ default: false,
57
+ },
58
+ labelSelector: {
59
+ type: "string",
60
+ description: "Filter resources by label selector",
61
+ optional: true,
62
+ }
63
+ },
64
+ required: ["resourceType", "name", "namespace"],
65
+ },
66
+ };
67
+ export async function kubectlLogs(k8sManager, input) {
68
+ try {
69
+ const resourceType = input.resourceType.toLowerCase();
70
+ const name = input.name;
71
+ const namespace = input.namespace || "default";
72
+ // Build the kubectl command base
73
+ let baseCommand = `kubectl -n ${namespace}`;
74
+ // Handle different resource types
75
+ if (resourceType === "pod") {
76
+ // Direct pod logs
77
+ baseCommand += ` logs ${name}`;
78
+ // If container is specified, add it
79
+ if (input.container) {
80
+ baseCommand += ` -c ${input.container}`;
81
+ }
82
+ // Add options
83
+ baseCommand = addLogOptions(baseCommand, input);
84
+ // Execute the command
85
+ try {
86
+ const result = execSync(baseCommand, { encoding: "utf8" });
87
+ return formatLogOutput(name, result);
88
+ }
89
+ catch (error) {
90
+ return handleCommandError(error, `pod ${name}`);
91
+ }
92
+ }
93
+ else if (resourceType === "deployment" || resourceType === "job" || resourceType === "cronjob") {
94
+ // For deployments, jobs and cronjobs we need to find the pods first
95
+ let selectorCommand;
96
+ if (resourceType === "deployment") {
97
+ selectorCommand = `kubectl -n ${namespace} get deployment ${name} -o jsonpath='{.spec.selector.matchLabels}'`;
98
+ }
99
+ else if (resourceType === "job") {
100
+ // For jobs, we use the job-name label
101
+ return getLabelSelectorLogs(`job-name=${name}`, namespace, input);
102
+ }
103
+ else if (resourceType === "cronjob") {
104
+ // For cronjobs, it's more complex - need to find the job first
105
+ const jobsCommand = `kubectl -n ${namespace} get jobs --selector=job-name=${name} -o jsonpath='{.items[*].metadata.name}'`;
106
+ try {
107
+ const jobs = execSync(jobsCommand, { encoding: "utf8" }).trim().split(' ');
108
+ if (jobs.length === 0 || (jobs.length === 1 && jobs[0] === '')) {
109
+ return {
110
+ content: [
111
+ {
112
+ type: "text",
113
+ text: JSON.stringify({
114
+ message: `No jobs found for cronjob ${name} in namespace ${namespace}`,
115
+ }, null, 2),
116
+ },
117
+ ],
118
+ };
119
+ }
120
+ // Get logs for all jobs
121
+ const allJobLogs = {};
122
+ for (const job of jobs) {
123
+ // Get logs for pods from this job
124
+ const result = await getLabelSelectorLogs(`job-name=${job}`, namespace, input);
125
+ const jobLog = JSON.parse(result.content[0].text);
126
+ allJobLogs[job] = jobLog.logs;
127
+ }
128
+ return {
129
+ content: [
130
+ {
131
+ type: "text",
132
+ text: JSON.stringify({
133
+ cronjob: name,
134
+ namespace: namespace,
135
+ jobs: allJobLogs,
136
+ }, null, 2),
137
+ },
138
+ ],
139
+ };
140
+ }
141
+ catch (error) {
142
+ return handleCommandError(error, `cronjob ${name}`);
143
+ }
144
+ }
145
+ try {
146
+ if (resourceType === "deployment") {
147
+ // Get the deployment's selector
148
+ if (!selectorCommand) {
149
+ throw new Error("Selector command is undefined");
150
+ }
151
+ const selectorJson = execSync(selectorCommand, { encoding: "utf8" }).trim();
152
+ const selector = JSON.parse(selectorJson.replace(/'/g, '"'));
153
+ // Convert to label selector format
154
+ const labelSelector = Object.entries(selector)
155
+ .map(([key, value]) => `${key}=${value}`)
156
+ .join(',');
157
+ return getLabelSelectorLogs(labelSelector, namespace, input);
158
+ }
159
+ // For jobs and cronjobs, the logic is handled above
160
+ return {
161
+ content: [
162
+ {
163
+ type: "text",
164
+ text: JSON.stringify({
165
+ error: `Unexpected resource type: ${resourceType}`,
166
+ }, null, 2),
167
+ },
168
+ ],
169
+ isError: true,
170
+ };
171
+ }
172
+ catch (error) {
173
+ return handleCommandError(error, `${resourceType} ${name}`);
174
+ }
175
+ }
176
+ else if (input.labelSelector) {
177
+ // Handle logs by label selector
178
+ return getLabelSelectorLogs(input.labelSelector, namespace, input);
179
+ }
180
+ else {
181
+ throw new McpError(ErrorCode.InvalidRequest, `Unsupported resource type: ${resourceType}`);
182
+ }
183
+ }
184
+ catch (error) {
185
+ if (error instanceof McpError)
186
+ throw error;
187
+ throw new McpError(ErrorCode.InternalError, `Failed to get logs: ${error.message}`);
188
+ }
189
+ }
190
+ // Helper function to add log options to the kubectl command
191
+ function addLogOptions(baseCommand, input) {
192
+ let command = baseCommand;
193
+ // Add options based on inputs
194
+ if (input.tail !== undefined) {
195
+ command += ` --tail=${input.tail}`;
196
+ }
197
+ if (input.since) {
198
+ command += ` --since=${input.since}`;
199
+ }
200
+ if (input.sinceTime) {
201
+ command += ` --since-time=${input.sinceTime}`;
202
+ }
203
+ if (input.timestamps) {
204
+ command += ` --timestamps`;
205
+ }
206
+ if (input.previous) {
207
+ command += ` --previous`;
208
+ }
209
+ if (input.follow) {
210
+ command += ` --follow`;
211
+ }
212
+ return command;
213
+ }
214
+ // Helper function to get logs for resources selected by labels
215
+ async function getLabelSelectorLogs(labelSelector, namespace, input) {
216
+ try {
217
+ // First, find all pods matching the label selector
218
+ const podsCommand = `kubectl -n ${namespace} get pods --selector=${labelSelector} -o jsonpath='{.items[*].metadata.name}'`;
219
+ const pods = execSync(podsCommand, { encoding: "utf8" }).trim().split(' ');
220
+ if (pods.length === 0 || (pods.length === 1 && pods[0] === '')) {
221
+ return {
222
+ content: [
223
+ {
224
+ type: "text",
225
+ text: JSON.stringify({
226
+ message: `No pods found with label selector "${labelSelector}" in namespace ${namespace}`,
227
+ }, null, 2),
228
+ },
229
+ ],
230
+ };
231
+ }
232
+ // Get logs for each pod
233
+ const logsMap = {};
234
+ for (const pod of pods) {
235
+ // Skip empty pod names
236
+ if (!pod)
237
+ continue;
238
+ let podCommand = `kubectl -n ${namespace} logs ${pod}`;
239
+ // Add container if specified
240
+ if (input.container) {
241
+ podCommand += ` -c ${input.container}`;
242
+ }
243
+ // Add other options
244
+ podCommand = addLogOptions(podCommand, input);
245
+ try {
246
+ const logs = execSync(podCommand, { encoding: "utf8" });
247
+ logsMap[pod] = logs;
248
+ }
249
+ catch (error) {
250
+ logsMap[pod] = `Error: ${error.message}`;
251
+ }
252
+ }
253
+ return {
254
+ content: [
255
+ {
256
+ type: "text",
257
+ text: JSON.stringify({
258
+ selector: labelSelector,
259
+ namespace: namespace,
260
+ logs: logsMap,
261
+ }, null, 2),
262
+ },
263
+ ],
264
+ };
265
+ }
266
+ catch (error) {
267
+ return handleCommandError(error, `pods with selector "${labelSelector}"`);
268
+ }
269
+ }
270
+ // Helper function to format log output
271
+ function formatLogOutput(resourceName, logOutput) {
272
+ return {
273
+ content: [
274
+ {
275
+ type: "text",
276
+ text: JSON.stringify({
277
+ name: resourceName,
278
+ logs: logOutput,
279
+ }, null, 2),
280
+ },
281
+ ],
282
+ };
283
+ }
284
+ // Helper function to handle command errors
285
+ function handleCommandError(error, resourceDescription) {
286
+ console.error(`Error getting logs for ${resourceDescription}:`, error);
287
+ if (error.status === 404 || error.message.includes("not found")) {
288
+ return {
289
+ content: [
290
+ {
291
+ type: "text",
292
+ text: JSON.stringify({
293
+ error: `Resource ${resourceDescription} not found`,
294
+ status: "not_found",
295
+ }, null, 2),
296
+ },
297
+ ],
298
+ isError: true,
299
+ };
300
+ }
301
+ return {
302
+ content: [
303
+ {
304
+ type: "text",
305
+ text: JSON.stringify({
306
+ error: `Failed to get logs for ${resourceDescription}: ${error.message}`,
307
+ }, null, 2),
308
+ },
309
+ ],
310
+ isError: true,
311
+ };
312
+ }
@@ -0,0 +1,57 @@
1
+ import { KubernetesManager } from "../types.js";
2
+ export declare const kubectlPatchSchema: {
3
+ name: string;
4
+ description: string;
5
+ inputSchema: {
6
+ type: string;
7
+ properties: {
8
+ resourceType: {
9
+ type: string;
10
+ description: string;
11
+ };
12
+ name: {
13
+ type: string;
14
+ description: string;
15
+ };
16
+ namespace: {
17
+ type: string;
18
+ description: string;
19
+ default: string;
20
+ };
21
+ patchType: {
22
+ type: string;
23
+ description: string;
24
+ enum: string[];
25
+ default: string;
26
+ };
27
+ patchData: {
28
+ type: string;
29
+ description: string;
30
+ };
31
+ patchFile: {
32
+ type: string;
33
+ description: string;
34
+ };
35
+ dryRun: {
36
+ type: string;
37
+ description: string;
38
+ default: boolean;
39
+ };
40
+ };
41
+ required: string[];
42
+ };
43
+ };
44
+ export declare function kubectlPatch(k8sManager: KubernetesManager, input: {
45
+ resourceType: string;
46
+ name: string;
47
+ namespace?: string;
48
+ patchType?: "strategic" | "merge" | "json";
49
+ patchData?: object;
50
+ patchFile?: string;
51
+ dryRun?: boolean;
52
+ }): Promise<{
53
+ content: {
54
+ type: string;
55
+ text: string;
56
+ }[];
57
+ }>;