mcp-server-kubernetes 1.3.2 → 1.5.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.
@@ -0,0 +1,72 @@
1
+ import { KubernetesManager } from "../types.js";
2
+ export declare const updateServiceSchema: {
3
+ name: string;
4
+ description: string;
5
+ inputSchema: {
6
+ type: string;
7
+ required: string[];
8
+ properties: {
9
+ name: {
10
+ type: string;
11
+ };
12
+ namespace: {
13
+ type: string;
14
+ default: string;
15
+ };
16
+ type: {
17
+ type: string;
18
+ enum: string[];
19
+ };
20
+ selector: {
21
+ type: string;
22
+ additionalProperties: {
23
+ type: string;
24
+ };
25
+ };
26
+ ports: {
27
+ type: string;
28
+ items: {
29
+ type: string;
30
+ properties: {
31
+ port: {
32
+ type: string;
33
+ };
34
+ targetPort: {
35
+ type: string;
36
+ };
37
+ protocol: {
38
+ type: string;
39
+ enum: string[];
40
+ default: string;
41
+ };
42
+ name: {
43
+ type: string;
44
+ };
45
+ nodePort: {
46
+ type: string;
47
+ };
48
+ };
49
+ required: string[];
50
+ };
51
+ };
52
+ };
53
+ };
54
+ };
55
+ export declare function updateService(k8sManager: KubernetesManager, params: {
56
+ name: string;
57
+ namespace: string;
58
+ type?: "ClusterIP" | "NodePort" | "LoadBalancer";
59
+ selector?: Record<string, string>;
60
+ ports?: Array<{
61
+ port: number;
62
+ targetPort?: number;
63
+ protocol?: string;
64
+ name?: string;
65
+ nodePort?: number;
66
+ }>;
67
+ }): Promise<{
68
+ content: {
69
+ type: string;
70
+ text: string;
71
+ }[];
72
+ }>;
@@ -0,0 +1,125 @@
1
+ import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
2
+ export const updateServiceSchema = {
3
+ name: "update_service",
4
+ description: "Update an existing kubernetes service in cluster",
5
+ inputSchema: {
6
+ type: "object",
7
+ required: ["name", "namespace"],
8
+ properties: {
9
+ name: { type: "string" },
10
+ namespace: { type: "string", default: "default" },
11
+ type: {
12
+ type: "string",
13
+ enum: ["ClusterIP", "NodePort", "LoadBalancer"],
14
+ },
15
+ selector: {
16
+ type: "object",
17
+ additionalProperties: { type: "string" },
18
+ },
19
+ ports: {
20
+ type: "array",
21
+ items: {
22
+ type: "object",
23
+ properties: {
24
+ port: { type: "number" },
25
+ targetPort: { type: "number" },
26
+ protocol: {
27
+ type: "string",
28
+ enum: ["TCP", "UDP"],
29
+ default: "TCP"
30
+ },
31
+ name: { type: "string" },
32
+ nodePort: { type: "number" }
33
+ },
34
+ required: ["port"]
35
+ }
36
+ }
37
+ },
38
+ },
39
+ };
40
+ export async function updateService(k8sManager, params) {
41
+ // Get existing service
42
+ const { body: existingService } = await k8sManager
43
+ .getCoreApi()
44
+ .readNamespacedService(params.name, params.namespace)
45
+ .catch((error) => {
46
+ console.error("Service read error:", {
47
+ status: error.response?.statusCode,
48
+ message: error.response?.body?.message || error.message,
49
+ details: error.response?.body,
50
+ });
51
+ if (error.response?.statusCode === 404) {
52
+ throw new McpError(ErrorCode.InvalidRequest, `Service '${params.name}' not found in namespace '${params.namespace}'`);
53
+ }
54
+ throw new McpError(ErrorCode.InternalError, `Failed to retrieve service: ${error.response?.body?.message || error.message}`);
55
+ });
56
+ // Process ports if provided
57
+ let servicePorts;
58
+ if (params.ports) {
59
+ servicePorts = params.ports.map((portConfig, index) => {
60
+ const existingPort = existingService.spec?.ports?.[index];
61
+ const name = portConfig.name || (existingPort?.name || `port-${index}`);
62
+ return {
63
+ port: portConfig.port,
64
+ targetPort: portConfig.targetPort !== undefined
65
+ ? portConfig.targetPort
66
+ : portConfig.port,
67
+ protocol: portConfig.protocol || "TCP",
68
+ name: name,
69
+ ...(existingService.spec?.type === "NodePort" || params.type === "NodePort" ?
70
+ { nodePort: portConfig.nodePort !== undefined ? portConfig.nodePort : existingPort?.nodePort } : {})
71
+ };
72
+ });
73
+ }
74
+ const updatedService = {
75
+ ...existingService,
76
+ spec: {
77
+ ...existingService.spec,
78
+ type: params.type || existingService.spec.type,
79
+ selector: params.selector || existingService.spec.selector,
80
+ ports: servicePorts || existingService.spec.ports,
81
+ clusterIP: existingService.spec.clusterIP,
82
+ },
83
+ };
84
+ try {
85
+ const { body } = await k8sManager
86
+ .getCoreApi()
87
+ .replaceNamespacedService(params.name, params.namespace, updatedService);
88
+ return {
89
+ content: [
90
+ {
91
+ type: "text",
92
+ text: JSON.stringify({
93
+ message: "Service updated successfully",
94
+ service: {
95
+ name: body.metadata?.name,
96
+ namespace: body.metadata?.namespace,
97
+ type: body.spec?.type,
98
+ clusterIP: body.spec?.clusterIP,
99
+ ports: body.spec?.ports,
100
+ },
101
+ }, null, 2),
102
+ },
103
+ ],
104
+ };
105
+ }
106
+ catch (error) {
107
+ console.error("Service update error:", {
108
+ status: error.response?.statusCode,
109
+ message: error.response?.body?.message || error.message,
110
+ details: error.response?.body,
111
+ });
112
+ if (error instanceof McpError)
113
+ throw error;
114
+ // Handle specific Kubernetes API errors
115
+ if (error.response?.body?.message) {
116
+ if (error.response.body.message.includes("field is immutable")) {
117
+ throw new McpError(ErrorCode.InvalidRequest, `Update failed: Attempted to modify immutable field. ${error.response.body.message}`);
118
+ }
119
+ if (error.response.statusCode === 422) {
120
+ throw new McpError(ErrorCode.InvalidRequest, `Invalid service configuration: ${error.response.body.message}`);
121
+ }
122
+ }
123
+ throw new McpError(ErrorCode.InternalError, `Failed to update service: ${error.response?.body?.message || error.message}`);
124
+ }
125
+ }
@@ -9,6 +9,12 @@ export declare class KubernetesManager {
9
9
  private k8sAppsApi;
10
10
  private k8sBatchApi;
11
11
  constructor();
12
+ /**
13
+ * Set the current context to the desired context name.
14
+ *
15
+ * @param contextName
16
+ */
17
+ setCurrentContext(contextName: string): void;
12
18
  cleanup(): Promise<void>;
13
19
  trackResource(kind: string, name: string, namespace: string): void;
14
20
  deleteResource(kind: string, name: string, namespace: string): Promise<void>;
@@ -14,6 +14,25 @@ export class KubernetesManager {
14
14
  this.k8sAppsApi = this.kc.makeApiClient(k8s.AppsV1Api);
15
15
  this.k8sBatchApi = this.kc.makeApiClient(k8s.BatchV1Api);
16
16
  }
17
+ /**
18
+ * Set the current context to the desired context name.
19
+ *
20
+ * @param contextName
21
+ */
22
+ setCurrentContext(contextName) {
23
+ // Get all available contexts
24
+ const contexts = this.kc.getContexts();
25
+ const contextNames = contexts.map(context => context.name);
26
+ // Check if the requested context exists
27
+ if (!contextNames.includes(contextName)) {
28
+ throw new Error(`Context '${contextName}' not found. Available contexts: ${contextNames.join(', ')}`);
29
+ }
30
+ // Set the current context
31
+ this.kc.setCurrentContext(contextName);
32
+ this.k8sApi = this.kc.makeApiClient(k8s.CoreV1Api);
33
+ this.k8sAppsApi = this.kc.makeApiClient(k8s.AppsV1Api);
34
+ this.k8sBatchApi = this.kc.makeApiClient(k8s.BatchV1Api);
35
+ }
17
36
  async cleanup() {
18
37
  // Stop watches
19
38
  for (const watch of this.watches) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-server-kubernetes",
3
- "version": "1.3.2",
3
+ "version": "1.5.0",
4
4
  "description": "MCP server for interacting with Kubernetes clusters via kubectl",
5
5
  "license": "MIT",
6
6
  "type": "module",