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.
- package/README.md +9 -2
- package/dist/index.js +101 -40
- package/dist/models/response-schemas.d.ts +88 -0
- package/dist/models/response-schemas.js +12 -0
- package/dist/tools/create_service.d.ts +74 -0
- package/dist/tools/create_service.js +102 -0
- package/dist/tools/delete_service.d.ts +32 -0
- package/dist/tools/delete_service.js +46 -0
- package/dist/tools/describe_node.d.ts +22 -0
- package/dist/tools/describe_node.js +84 -0
- package/dist/tools/describe_service.d.ts +34 -0
- package/dist/tools/describe_service.js +85 -0
- package/dist/tools/get_current_context.d.ts +23 -0
- package/dist/tools/get_current_context.js +55 -0
- package/dist/tools/helm-operations.js +5 -1
- package/dist/tools/list_contexts.d.ts +23 -0
- package/dist/tools/list_contexts.js +39 -0
- package/dist/tools/set_current_context.d.ts +23 -0
- package/dist/tools/set_current_context.js +35 -0
- package/dist/tools/update_deployment.d.ts +113 -0
- package/dist/tools/update_deployment.js +155 -0
- package/dist/tools/update_service.d.ts +72 -0
- package/dist/tools/update_service.js +125 -0
- package/dist/utils/kubernetes-manager.d.ts +6 -0
- package/dist/utils/kubernetes-manager.js +19 -0
- package/package.json +1 -1
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
+
export const describeNodeSchema = {
|
|
3
|
+
name: "describe_node",
|
|
4
|
+
description: "Describe a Kubernetes node (read details like status, capacity, conditions, etc.)",
|
|
5
|
+
inputSchema: {
|
|
6
|
+
type: "object",
|
|
7
|
+
properties: {
|
|
8
|
+
name: { type: "string" },
|
|
9
|
+
},
|
|
10
|
+
required: ["name"],
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
export async function describeNode(k8sManager, input) {
|
|
14
|
+
try {
|
|
15
|
+
const { body } = await k8sManager.getCoreApi().readNode(input.name);
|
|
16
|
+
if (!body) {
|
|
17
|
+
return {
|
|
18
|
+
content: [
|
|
19
|
+
{
|
|
20
|
+
type: "text",
|
|
21
|
+
text: JSON.stringify({
|
|
22
|
+
error: "Node not found",
|
|
23
|
+
status: "not_found",
|
|
24
|
+
}, null, 2),
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
// Format the node details for better readability
|
|
30
|
+
const nodeDetails = {
|
|
31
|
+
kind: body.kind,
|
|
32
|
+
metadata: {
|
|
33
|
+
name: body.metadata?.name,
|
|
34
|
+
creationTimestamp: body.metadata?.creationTimestamp,
|
|
35
|
+
labels: body.metadata?.labels,
|
|
36
|
+
annotations: body.metadata?.annotations,
|
|
37
|
+
},
|
|
38
|
+
spec: {
|
|
39
|
+
podCIDR: body.spec?.podCIDR,
|
|
40
|
+
podCIDRs: body.spec?.podCIDRs,
|
|
41
|
+
taints: body.spec?.taints,
|
|
42
|
+
unschedulable: body.spec?.unschedulable,
|
|
43
|
+
},
|
|
44
|
+
status: {
|
|
45
|
+
capacity: body.status?.capacity,
|
|
46
|
+
allocatable: body.status?.allocatable,
|
|
47
|
+
conditions: body.status?.conditions,
|
|
48
|
+
nodeInfo: {
|
|
49
|
+
architecture: body.status?.nodeInfo?.architecture,
|
|
50
|
+
containerRuntimeVersion: body.status?.nodeInfo?.containerRuntimeVersion,
|
|
51
|
+
kernelVersion: body.status?.nodeInfo?.kernelVersion,
|
|
52
|
+
kubeletVersion: body.status?.nodeInfo?.kubeletVersion,
|
|
53
|
+
operatingSystem: body.status?.nodeInfo?.operatingSystem,
|
|
54
|
+
osImage: body.status?.nodeInfo?.osImage,
|
|
55
|
+
},
|
|
56
|
+
addresses: body.status?.addresses,
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
return {
|
|
60
|
+
content: [
|
|
61
|
+
{
|
|
62
|
+
type: "text",
|
|
63
|
+
text: JSON.stringify(nodeDetails, null, 2),
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
if (error.response?.statusCode === 404) {
|
|
70
|
+
return {
|
|
71
|
+
content: [
|
|
72
|
+
{
|
|
73
|
+
type: "text",
|
|
74
|
+
text: JSON.stringify({
|
|
75
|
+
error: "Node not found",
|
|
76
|
+
status: "not_found",
|
|
77
|
+
}, null, 2),
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
throw new McpError(ErrorCode.InternalError, `Failed to describe node: ${error.response?.body?.message || error.message}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { KubernetesManager } from "../types.js";
|
|
2
|
+
export declare const describeServiceSchema: {
|
|
3
|
+
readonly name: "describe_service";
|
|
4
|
+
readonly description: "Describe a Kubernetes service (read details like status, ports, selectors, etc.)";
|
|
5
|
+
readonly inputSchema: {
|
|
6
|
+
readonly type: "object";
|
|
7
|
+
readonly properties: {
|
|
8
|
+
readonly name: {
|
|
9
|
+
readonly type: "string";
|
|
10
|
+
};
|
|
11
|
+
readonly namespace: {
|
|
12
|
+
readonly type: "string";
|
|
13
|
+
readonly default: "default";
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
readonly required: readonly ["name"];
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
export declare function describeService(k8sManager: KubernetesManager, input: {
|
|
20
|
+
name: string;
|
|
21
|
+
namespace?: string;
|
|
22
|
+
}): Promise<{
|
|
23
|
+
content: {
|
|
24
|
+
type: string;
|
|
25
|
+
text: string;
|
|
26
|
+
}[];
|
|
27
|
+
isError: boolean;
|
|
28
|
+
} | {
|
|
29
|
+
content: {
|
|
30
|
+
type: string;
|
|
31
|
+
text: string;
|
|
32
|
+
}[];
|
|
33
|
+
isError?: undefined;
|
|
34
|
+
}>;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
+
export const describeServiceSchema = {
|
|
3
|
+
name: "describe_service",
|
|
4
|
+
description: "Describe a Kubernetes service (read details like status, ports, selectors, etc.)",
|
|
5
|
+
inputSchema: {
|
|
6
|
+
type: "object",
|
|
7
|
+
properties: {
|
|
8
|
+
name: { type: "string" },
|
|
9
|
+
namespace: { type: "string", default: "default" },
|
|
10
|
+
},
|
|
11
|
+
required: ["name"],
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
export async function describeService(k8sManager, input) {
|
|
15
|
+
const namespace = input.namespace || "default";
|
|
16
|
+
try {
|
|
17
|
+
const { body } = await k8sManager.getCoreApi().readNamespacedService(input.name, namespace);
|
|
18
|
+
if (!body) {
|
|
19
|
+
return {
|
|
20
|
+
content: [
|
|
21
|
+
{
|
|
22
|
+
type: "text",
|
|
23
|
+
text: JSON.stringify({
|
|
24
|
+
error: "Service not found",
|
|
25
|
+
status: "not_found",
|
|
26
|
+
}, null, 2),
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
isError: true,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
// Format service details for better readability
|
|
33
|
+
const serviceDetails = {
|
|
34
|
+
kind: body.kind,
|
|
35
|
+
metadata: {
|
|
36
|
+
name: body.metadata?.name,
|
|
37
|
+
namespace: body.metadata?.namespace,
|
|
38
|
+
creationTimestamp: body.metadata?.creationTimestamp,
|
|
39
|
+
labels: body.metadata?.labels,
|
|
40
|
+
},
|
|
41
|
+
spec: {
|
|
42
|
+
type: body.spec?.type,
|
|
43
|
+
selector: body.spec?.selector,
|
|
44
|
+
ports: body.spec?.ports?.map((port) => ({
|
|
45
|
+
name: port.name,
|
|
46
|
+
protocol: port.protocol,
|
|
47
|
+
port: port.port,
|
|
48
|
+
targetPort: port.targetPort,
|
|
49
|
+
nodePort: port.nodePort,
|
|
50
|
+
})),
|
|
51
|
+
clusterIP: body.spec?.clusterIP,
|
|
52
|
+
externalIPs: body.spec?.externalIPs,
|
|
53
|
+
loadBalancerIP: body.spec?.loadBalancerIP,
|
|
54
|
+
},
|
|
55
|
+
status: {
|
|
56
|
+
loadBalancer: body.status?.loadBalancer,
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
return {
|
|
60
|
+
content: [
|
|
61
|
+
{
|
|
62
|
+
type: "text",
|
|
63
|
+
text: JSON.stringify(serviceDetails, null, 2),
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
if (error.response?.statusCode === 404) {
|
|
70
|
+
return {
|
|
71
|
+
content: [
|
|
72
|
+
{
|
|
73
|
+
type: "text",
|
|
74
|
+
text: JSON.stringify({
|
|
75
|
+
error: "Service not found",
|
|
76
|
+
status: "not_found",
|
|
77
|
+
}, null, 2),
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
isError: true,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
throw new McpError(ErrorCode.InternalError, `Failed to describe service: ${error.response?.body?.message || error.message}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { KubernetesManager } from "../types.js";
|
|
2
|
+
export declare const getCurrentContextSchema: {
|
|
3
|
+
readonly name: "get_current_context";
|
|
4
|
+
readonly description: "Get the current Kubernetes context";
|
|
5
|
+
readonly inputSchema: {
|
|
6
|
+
readonly type: "object";
|
|
7
|
+
readonly properties: {
|
|
8
|
+
readonly detailed: {
|
|
9
|
+
readonly type: "boolean";
|
|
10
|
+
readonly description: "Include detailed information about the current context";
|
|
11
|
+
readonly default: false;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
export declare function getCurrentContext(k8sManager: KubernetesManager, input: {
|
|
17
|
+
detailed?: boolean;
|
|
18
|
+
}): Promise<{
|
|
19
|
+
content: {
|
|
20
|
+
type: string;
|
|
21
|
+
text: string;
|
|
22
|
+
}[];
|
|
23
|
+
}>;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export const getCurrentContextSchema = {
|
|
2
|
+
name: "get_current_context",
|
|
3
|
+
description: "Get the current Kubernetes context",
|
|
4
|
+
inputSchema: {
|
|
5
|
+
type: "object",
|
|
6
|
+
properties: {
|
|
7
|
+
detailed: {
|
|
8
|
+
type: "boolean",
|
|
9
|
+
description: "Include detailed information about the current context",
|
|
10
|
+
default: false
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
export async function getCurrentContext(k8sManager, input) {
|
|
16
|
+
try {
|
|
17
|
+
// Get the KubeConfig from the KubernetesManager
|
|
18
|
+
const kc = k8sManager.getKubeConfig();
|
|
19
|
+
// Get the current context name
|
|
20
|
+
const currentContextName = kc.getCurrentContext();
|
|
21
|
+
// If detailed is true, get more information about the context
|
|
22
|
+
if (input.detailed) {
|
|
23
|
+
const contexts = kc.getContexts();
|
|
24
|
+
const currentContext = contexts.find(context => context.name === currentContextName);
|
|
25
|
+
if (!currentContext) {
|
|
26
|
+
throw new Error(`Current context '${currentContextName}' not found in available contexts`);
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
content: [
|
|
30
|
+
{
|
|
31
|
+
type: "text",
|
|
32
|
+
text: JSON.stringify({
|
|
33
|
+
name: currentContextName,
|
|
34
|
+
cluster: currentContext.cluster,
|
|
35
|
+
user: currentContext.user,
|
|
36
|
+
namespace: currentContext.namespace || "default"
|
|
37
|
+
}, null, 2),
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
// Simple response with just the context name
|
|
43
|
+
return {
|
|
44
|
+
content: [
|
|
45
|
+
{
|
|
46
|
+
type: "text",
|
|
47
|
+
text: JSON.stringify({ currentContext: currentContextName }, null, 2),
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
throw new Error(`Failed to get current context: ${error.message}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -85,7 +85,11 @@ export const uninstallHelmChartSchema = {
|
|
|
85
85
|
};
|
|
86
86
|
const executeHelmCommand = (command) => {
|
|
87
87
|
try {
|
|
88
|
-
|
|
88
|
+
// Add a generous timeout of 60 seconds for Helm operations
|
|
89
|
+
return execSync(command, {
|
|
90
|
+
encoding: "utf8",
|
|
91
|
+
timeout: 60000 // 60 seconds timeout
|
|
92
|
+
});
|
|
89
93
|
}
|
|
90
94
|
catch (error) {
|
|
91
95
|
throw new Error(`Helm command failed: ${error.message}`);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { KubernetesManager } from "../types.js";
|
|
2
|
+
export declare const listContextsSchema: {
|
|
3
|
+
readonly name: "list_contexts";
|
|
4
|
+
readonly description: "List all available Kubernetes contexts";
|
|
5
|
+
readonly inputSchema: {
|
|
6
|
+
readonly type: "object";
|
|
7
|
+
readonly properties: {
|
|
8
|
+
readonly showCurrent: {
|
|
9
|
+
readonly type: "boolean";
|
|
10
|
+
readonly description: "Show which context is currently active";
|
|
11
|
+
readonly default: true;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
export declare function listContexts(k8sManager: KubernetesManager, input: {
|
|
17
|
+
showCurrent?: boolean;
|
|
18
|
+
}): Promise<{
|
|
19
|
+
content: {
|
|
20
|
+
type: string;
|
|
21
|
+
text: string;
|
|
22
|
+
}[];
|
|
23
|
+
}>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export const listContextsSchema = {
|
|
2
|
+
name: "list_contexts",
|
|
3
|
+
description: "List all available Kubernetes contexts",
|
|
4
|
+
inputSchema: {
|
|
5
|
+
type: "object",
|
|
6
|
+
properties: {
|
|
7
|
+
showCurrent: {
|
|
8
|
+
type: "boolean",
|
|
9
|
+
description: "Show which context is currently active",
|
|
10
|
+
default: true
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
export async function listContexts(k8sManager, input) {
|
|
16
|
+
try {
|
|
17
|
+
// Get the KubeConfig from the KubernetesManager
|
|
18
|
+
const kc = k8sManager.getKubeConfig();
|
|
19
|
+
const contexts = kc.getContexts();
|
|
20
|
+
const currentContext = input.showCurrent ? kc.getCurrentContext() : undefined;
|
|
21
|
+
const contextList = contexts.map(context => ({
|
|
22
|
+
name: context.name,
|
|
23
|
+
cluster: context.cluster,
|
|
24
|
+
user: context.user,
|
|
25
|
+
isCurrent: context.name === currentContext
|
|
26
|
+
}));
|
|
27
|
+
return {
|
|
28
|
+
content: [
|
|
29
|
+
{
|
|
30
|
+
type: "text",
|
|
31
|
+
text: JSON.stringify({ contexts: contextList }, null, 2),
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
throw new Error(`Failed to list contexts: ${error.message}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { KubernetesManager } from "../types.js";
|
|
2
|
+
export declare const setCurrentContextSchema: {
|
|
3
|
+
readonly name: "set_current_context";
|
|
4
|
+
readonly description: "Set the current Kubernetes context";
|
|
5
|
+
readonly inputSchema: {
|
|
6
|
+
readonly type: "object";
|
|
7
|
+
readonly properties: {
|
|
8
|
+
readonly name: {
|
|
9
|
+
readonly type: "string";
|
|
10
|
+
readonly description: "Name of the context to set as current";
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
readonly required: readonly ["name"];
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
export declare function setCurrentContext(k8sManager: KubernetesManager, input: {
|
|
17
|
+
name: string;
|
|
18
|
+
}): Promise<{
|
|
19
|
+
content: {
|
|
20
|
+
type: string;
|
|
21
|
+
text: string;
|
|
22
|
+
}[];
|
|
23
|
+
}>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export const setCurrentContextSchema = {
|
|
2
|
+
name: "set_current_context",
|
|
3
|
+
description: "Set the current Kubernetes context",
|
|
4
|
+
inputSchema: {
|
|
5
|
+
type: "object",
|
|
6
|
+
properties: {
|
|
7
|
+
name: {
|
|
8
|
+
type: "string",
|
|
9
|
+
description: "Name of the context to set as current"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
required: ["name"],
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
export async function setCurrentContext(k8sManager, input) {
|
|
16
|
+
try {
|
|
17
|
+
// Set the current context
|
|
18
|
+
k8sManager.setCurrentContext(input.name);
|
|
19
|
+
return {
|
|
20
|
+
content: [
|
|
21
|
+
{
|
|
22
|
+
type: "text",
|
|
23
|
+
text: JSON.stringify({
|
|
24
|
+
success: true,
|
|
25
|
+
message: `Current context set to '${input.name}'`,
|
|
26
|
+
context: input.name
|
|
27
|
+
}, null, 2),
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
throw new Error(`Failed to set current context: ${error.message}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { KubernetesManager } from "../types.js";
|
|
2
|
+
import { CustomContainerConfigType } from "../config/container-templates.js";
|
|
3
|
+
export declare const updateDeploymentSchema: {
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
inputSchema: {
|
|
7
|
+
type: string;
|
|
8
|
+
required: string[];
|
|
9
|
+
properties: {
|
|
10
|
+
name: {
|
|
11
|
+
type: string;
|
|
12
|
+
};
|
|
13
|
+
namespace: {
|
|
14
|
+
type: string;
|
|
15
|
+
};
|
|
16
|
+
template: {
|
|
17
|
+
type: string;
|
|
18
|
+
enum: ["ubuntu", "nginx", "busybox", "alpine", "custom"];
|
|
19
|
+
};
|
|
20
|
+
containerName: {
|
|
21
|
+
type: string;
|
|
22
|
+
description: string;
|
|
23
|
+
};
|
|
24
|
+
replicas: {
|
|
25
|
+
type: string;
|
|
26
|
+
};
|
|
27
|
+
customConfig: {
|
|
28
|
+
type: string;
|
|
29
|
+
properties: {
|
|
30
|
+
image: {
|
|
31
|
+
type: string;
|
|
32
|
+
};
|
|
33
|
+
command: {
|
|
34
|
+
type: string;
|
|
35
|
+
items: {
|
|
36
|
+
type: string;
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
args: {
|
|
40
|
+
type: string;
|
|
41
|
+
items: {
|
|
42
|
+
type: string;
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
ports: {
|
|
46
|
+
type: string;
|
|
47
|
+
items: {
|
|
48
|
+
type: string;
|
|
49
|
+
properties: {
|
|
50
|
+
containerPort: {
|
|
51
|
+
type: string;
|
|
52
|
+
};
|
|
53
|
+
name: {
|
|
54
|
+
type: string;
|
|
55
|
+
};
|
|
56
|
+
protocol: {
|
|
57
|
+
type: string;
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
resources: {
|
|
63
|
+
type: string;
|
|
64
|
+
properties: {
|
|
65
|
+
limits: {
|
|
66
|
+
type: string;
|
|
67
|
+
additionalProperties: {
|
|
68
|
+
type: string;
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
requests: {
|
|
72
|
+
type: string;
|
|
73
|
+
additionalProperties: {
|
|
74
|
+
type: string;
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
env: {
|
|
80
|
+
type: string;
|
|
81
|
+
items: {
|
|
82
|
+
type: string;
|
|
83
|
+
properties: {
|
|
84
|
+
name: {
|
|
85
|
+
type: string;
|
|
86
|
+
};
|
|
87
|
+
value: {
|
|
88
|
+
type: string;
|
|
89
|
+
};
|
|
90
|
+
valueFrom: {
|
|
91
|
+
type: string;
|
|
92
|
+
};
|
|
93
|
+
};
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
};
|
|
100
|
+
};
|
|
101
|
+
export declare function updateDeployment(k8sManager: KubernetesManager, params: {
|
|
102
|
+
name: string;
|
|
103
|
+
namespace: string;
|
|
104
|
+
template: string;
|
|
105
|
+
containerName?: string;
|
|
106
|
+
replicas?: number;
|
|
107
|
+
customConfig?: CustomContainerConfigType;
|
|
108
|
+
}): Promise<{
|
|
109
|
+
content: {
|
|
110
|
+
type: string;
|
|
111
|
+
text: string;
|
|
112
|
+
}[];
|
|
113
|
+
}>;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
+
import { ContainerTemplate, containerTemplates, CustomContainerConfig, } from "../config/container-templates.js";
|
|
3
|
+
export const updateDeploymentSchema = {
|
|
4
|
+
name: "update_deployment",
|
|
5
|
+
description: "Update an existing kubernetes deployment in cluster",
|
|
6
|
+
inputSchema: {
|
|
7
|
+
type: "object",
|
|
8
|
+
required: ["name", "namespace", "template"],
|
|
9
|
+
properties: {
|
|
10
|
+
name: { type: "string" },
|
|
11
|
+
namespace: { type: "string" },
|
|
12
|
+
template: {
|
|
13
|
+
type: "string",
|
|
14
|
+
enum: ContainerTemplate.options,
|
|
15
|
+
},
|
|
16
|
+
containerName: {
|
|
17
|
+
type: "string",
|
|
18
|
+
description: "Name of the container to update",
|
|
19
|
+
},
|
|
20
|
+
replicas: { type: "number" },
|
|
21
|
+
customConfig: {
|
|
22
|
+
type: "object",
|
|
23
|
+
properties: {
|
|
24
|
+
image: { type: "string" },
|
|
25
|
+
command: { type: "array", items: { type: "string" } },
|
|
26
|
+
args: { type: "array", items: { type: "string" } },
|
|
27
|
+
ports: {
|
|
28
|
+
type: "array",
|
|
29
|
+
items: {
|
|
30
|
+
type: "object",
|
|
31
|
+
properties: {
|
|
32
|
+
containerPort: { type: "number" },
|
|
33
|
+
name: { type: "string" },
|
|
34
|
+
protocol: { type: "string" },
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
resources: {
|
|
39
|
+
type: "object",
|
|
40
|
+
properties: {
|
|
41
|
+
limits: {
|
|
42
|
+
type: "object",
|
|
43
|
+
additionalProperties: { type: "string" },
|
|
44
|
+
},
|
|
45
|
+
requests: {
|
|
46
|
+
type: "object",
|
|
47
|
+
additionalProperties: { type: "string" },
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
env: {
|
|
52
|
+
type: "array",
|
|
53
|
+
items: {
|
|
54
|
+
type: "object",
|
|
55
|
+
properties: {
|
|
56
|
+
name: { type: "string" },
|
|
57
|
+
value: { type: "string" },
|
|
58
|
+
valueFrom: { type: "object" },
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
export async function updateDeployment(k8sManager, params) {
|
|
68
|
+
// Get existing deployment
|
|
69
|
+
const { body: existingDeployment } = await k8sManager
|
|
70
|
+
.getAppsApi()
|
|
71
|
+
.readNamespacedDeployment(params.name, params.namespace)
|
|
72
|
+
.catch((error) => {
|
|
73
|
+
console.error("Deployment read error:", {
|
|
74
|
+
status: error.response?.statusCode,
|
|
75
|
+
message: error.response?.body?.message || error.message,
|
|
76
|
+
details: error.response?.body,
|
|
77
|
+
});
|
|
78
|
+
throw error;
|
|
79
|
+
});
|
|
80
|
+
// Find target container
|
|
81
|
+
const containers = existingDeployment.spec.template.spec.containers;
|
|
82
|
+
let targetContainerIndex = params.containerName
|
|
83
|
+
? containers.findIndex(c => c.name === params.containerName)
|
|
84
|
+
: 0;
|
|
85
|
+
if (targetContainerIndex === -1) {
|
|
86
|
+
throw new McpError(ErrorCode.InvalidRequest, `Container '${params.containerName}' not found in deployment`);
|
|
87
|
+
}
|
|
88
|
+
// Prepare container config
|
|
89
|
+
const templateConfig = containerTemplates[params.template];
|
|
90
|
+
let containerConfig;
|
|
91
|
+
if (params.template === "custom") {
|
|
92
|
+
if (!params.customConfig) {
|
|
93
|
+
throw new McpError(ErrorCode.InvalidRequest, "Custom container configuration is required when using 'custom' template");
|
|
94
|
+
}
|
|
95
|
+
const validatedConfig = CustomContainerConfig.parse(params.customConfig);
|
|
96
|
+
containerConfig = {
|
|
97
|
+
...containers[targetContainerIndex],
|
|
98
|
+
...templateConfig,
|
|
99
|
+
image: validatedConfig.image,
|
|
100
|
+
command: validatedConfig.command,
|
|
101
|
+
args: validatedConfig.args,
|
|
102
|
+
ports: validatedConfig.ports,
|
|
103
|
+
resources: validatedConfig.resources,
|
|
104
|
+
env: validatedConfig.env,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
containerConfig = {
|
|
109
|
+
...containers[targetContainerIndex],
|
|
110
|
+
...templateConfig,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
// Update deployment
|
|
114
|
+
const updatedContainers = [...containers];
|
|
115
|
+
updatedContainers[targetContainerIndex] = containerConfig;
|
|
116
|
+
const updatedDeployment = {
|
|
117
|
+
...existingDeployment,
|
|
118
|
+
spec: {
|
|
119
|
+
...existingDeployment.spec,
|
|
120
|
+
replicas: params.replicas ?? existingDeployment.spec.replicas,
|
|
121
|
+
template: {
|
|
122
|
+
...existingDeployment.spec.template,
|
|
123
|
+
spec: {
|
|
124
|
+
...existingDeployment.spec.template.spec,
|
|
125
|
+
containers: updatedContainers,
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
const { body } = await k8sManager
|
|
131
|
+
.getAppsApi()
|
|
132
|
+
.replaceNamespacedDeployment(params.name, params.namespace, updatedDeployment)
|
|
133
|
+
.catch((error) => {
|
|
134
|
+
if (error instanceof McpError)
|
|
135
|
+
throw error;
|
|
136
|
+
throw new McpError(ErrorCode.InternalError, `Failed to update deployment: ${error}`);
|
|
137
|
+
});
|
|
138
|
+
return {
|
|
139
|
+
content: [
|
|
140
|
+
{
|
|
141
|
+
type: "text",
|
|
142
|
+
text: JSON.stringify({
|
|
143
|
+
message: "Deployment updated successfully",
|
|
144
|
+
deployment: {
|
|
145
|
+
name: body.metadata?.name,
|
|
146
|
+
namespace: body.metadata?.namespace,
|
|
147
|
+
replicas: body.spec?.replicas,
|
|
148
|
+
image: body.spec?.template.spec?.containers[targetContainerIndex].image,
|
|
149
|
+
containerName: body.spec?.template.spec?.containers[targetContainerIndex].name,
|
|
150
|
+
},
|
|
151
|
+
}, null, 2),
|
|
152
|
+
},
|
|
153
|
+
],
|
|
154
|
+
};
|
|
155
|
+
}
|