mcp-server-kubernetes
Version:
MCP server for interacting with Kubernetes clusters via kubectl
174 lines (173 loc) • 6.7 kB
JavaScript
import { execSync } from "child_process";
import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
import * as fs from "fs";
import * as path from "path";
import * as os from "os";
export const kubectlDeleteSchema = {
name: "kubectl_delete",
description: "Delete Kubernetes resources by resource type, name, labels, or from a manifest file",
inputSchema: {
type: "object",
properties: {
resourceType: {
type: "string",
description: "Type of resource to delete (e.g., pods, deployments, services, etc.)"
},
name: {
type: "string",
description: "Name of the resource to delete"
},
namespace: {
type: "string",
description: "Namespace of the resource (optional - defaults to 'default' for namespaced resources)",
default: "default"
},
labelSelector: {
type: "string",
description: "Delete resources matching this label selector (e.g. 'app=nginx')"
},
manifest: {
type: "string",
description: "YAML manifest defining resources to delete (optional)"
},
filename: {
type: "string",
description: "Path to a YAML file to delete resources from (optional)"
},
allNamespaces: {
type: "boolean",
description: "If true, delete resources across all namespaces",
default: false
},
force: {
type: "boolean",
description: "If true, immediately remove resources from API and bypass graceful deletion",
default: false
},
gracePeriodSeconds: {
type: "number",
description: "Period of time in seconds given to the resource to terminate gracefully"
}
},
required: ["resourceType", "name", "namespace"],
},
};
export async function kubectlDelete(k8sManager, input) {
try {
// Validate input - need at least one way to identify resources
if (!input.resourceType && !input.manifest && !input.filename) {
throw new McpError(ErrorCode.InvalidRequest, "Either resourceType, manifest, or filename must be provided");
}
// If resourceType is provided, need either name or labelSelector
if (input.resourceType && !input.name && !input.labelSelector) {
throw new McpError(ErrorCode.InvalidRequest, "When using resourceType, either name or labelSelector must be provided");
}
const namespace = input.namespace || "default";
const allNamespaces = input.allNamespaces || false;
const force = input.force || false;
let command = "kubectl delete";
let tempFile = null;
// Handle deleting from manifest or file
if (input.manifest) {
// Create temporary file for the manifest
const tmpDir = os.tmpdir();
tempFile = path.join(tmpDir, `delete-manifest-${Date.now()}.yaml`);
fs.writeFileSync(tempFile, input.manifest);
command += ` -f ${tempFile}`;
}
else if (input.filename) {
command += ` -f ${input.filename}`;
}
else {
// Handle deleting by resource type and name/selector
command += ` ${input.resourceType}`;
if (input.name) {
command += ` ${input.name}`;
}
if (input.labelSelector) {
command += ` -l ${input.labelSelector}`;
}
}
// Add namespace flags
if (allNamespaces) {
command += " --all-namespaces";
}
else if (namespace && input.resourceType && !isNonNamespacedResource(input.resourceType)) {
command += ` -n ${namespace}`;
}
// Add force flag if requested
if (force) {
command += " --force";
}
// Add grace period if specified
if (input.gracePeriodSeconds !== undefined) {
command += ` --grace-period=${input.gracePeriodSeconds}`;
}
// Execute the command
try {
const result = execSync(command, { encoding: "utf8", env: { ...process.env, KUBECONFIG: process.env.KUBECONFIG } });
// Clean up temp file if created
if (tempFile) {
try {
fs.unlinkSync(tempFile);
}
catch (err) {
console.warn(`Failed to delete temporary file ${tempFile}: ${err}`);
}
}
return {
content: [
{
type: "text",
text: result,
},
],
};
}
catch (error) {
// Clean up temp file if created, even if command failed
if (tempFile) {
try {
fs.unlinkSync(tempFile);
}
catch (err) {
console.warn(`Failed to delete temporary file ${tempFile}: ${err}`);
}
}
if (error.status === 404 || error.message.includes("not found")) {
return {
content: [
{
type: "text",
text: JSON.stringify({
error: `Resource not found`,
status: "not_found",
}, null, 2),
},
],
isError: true,
};
}
throw new McpError(ErrorCode.InternalError, `Failed to delete resource: ${error.message}`);
}
}
catch (error) {
if (error instanceof McpError) {
throw error;
}
throw new McpError(ErrorCode.InternalError, `Failed to execute kubectl delete command: ${error.message}`);
}
}
// Helper function to determine if a resource is non-namespaced
function isNonNamespacedResource(resourceType) {
const nonNamespacedResources = [
"nodes", "node", "no",
"namespaces", "namespace", "ns",
"persistentvolumes", "pv",
"storageclasses", "sc",
"clusterroles",
"clusterrolebindings",
"customresourcedefinitions", "crd", "crds"
];
return nonNamespacedResources.includes(resourceType.toLowerCase());
}