UNPKG

mcp-server-kubernetes

Version:

MCP server for interacting with Kubernetes clusters via kubectl

239 lines (238 loc) 9.4 kB
#!/usr/bin/env node import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { installHelmChart, installHelmChartSchema, upgradeHelmChart, upgradeHelmChartSchema, uninstallHelmChart, uninstallHelmChartSchema, } from "./tools/helm-operations.js"; import { explainResource, explainResourceSchema, listApiResources, listApiResourcesSchema, } from "./tools/kubectl-operations.js"; import { execInPod, execInPodSchema } from "./tools/exec_in_pod.js"; import { getResourceHandlers } from "./resources/handlers.js"; import { ListResourcesRequestSchema, ReadResourceRequestSchema, ListToolsRequestSchema, CallToolRequestSchema, ErrorCode, McpError, } from "@modelcontextprotocol/sdk/types.js"; import { KubernetesManager } from "./types.js"; import { serverConfig } from "./config/server-config.js"; import { cleanupSchema } from "./config/cleanup-config.js"; import { startSSEServer } from "./utils/sse.js"; import { startPortForward, PortForwardSchema, stopPortForward, StopPortForwardSchema, } from "./tools/port_forward.js"; import { kubectlScale, kubectlScaleSchema } from "./tools/kubectl-scale.js"; import { kubectlContext, kubectlContextSchema, } from "./tools/kubectl-context.js"; import { kubectlGet, kubectlGetSchema } from "./tools/kubectl-get.js"; import { kubectlDescribe, kubectlDescribeSchema, } from "./tools/kubectl-describe.js"; import { kubectlApply, kubectlApplySchema } from "./tools/kubectl-apply.js"; import { kubectlDelete, kubectlDeleteSchema } from "./tools/kubectl-delete.js"; import { kubectlCreate, kubectlCreateSchema } from "./tools/kubectl-create.js"; import { kubectlLogs, kubectlLogsSchema } from "./tools/kubectl-logs.js"; import { kubectlGeneric, kubectlGenericSchema, } from "./tools/kubectl-generic.js"; import { kubectlPatch, kubectlPatchSchema } from "./tools/kubectl-patch.js"; import { kubectlRollout, kubectlRolloutSchema, } from "./tools/kubectl-rollout.js"; import { registerPromptHandlers } from "./prompts/index.js"; import { ping, pingSchema } from "./tools/ping.js"; import { startStreamableHTTPServer } from "./utils/streamable-http.js"; // Check environment variables for tool filtering const allowOnlyReadonlyTools = process.env.ALLOW_ONLY_READONLY_TOOLS === "true"; const allowedToolsEnv = process.env.ALLOWED_TOOLS; const nonDestructiveTools = process.env.ALLOW_ONLY_NON_DESTRUCTIVE_TOOLS === "true"; // Define readonly tools const readonlyTools = [ kubectlGetSchema, kubectlDescribeSchema, kubectlLogsSchema, kubectlContextSchema, explainResourceSchema, listApiResourcesSchema, pingSchema, ]; // Define destructive tools (delete and uninstall operations) const destructiveTools = [ kubectlDeleteSchema, // This replaces all individual delete operations uninstallHelmChartSchema, cleanupSchema, // Cleanup is also destructive as it deletes resources kubectlGenericSchema, // Generic kubectl command can perform destructive operations ]; // Get all available tools const allTools = [ // Core operation tools cleanupSchema, // Unified kubectl-style tools - these replace many specific tools kubectlGetSchema, kubectlDescribeSchema, kubectlApplySchema, kubectlDeleteSchema, kubectlCreateSchema, kubectlLogsSchema, kubectlScaleSchema, kubectlPatchSchema, kubectlRolloutSchema, // Kubernetes context management kubectlContextSchema, // Special operations that aren't covered by simple kubectl commands explainResourceSchema, // Helm operations installHelmChartSchema, upgradeHelmChartSchema, uninstallHelmChartSchema, // Port forwarding PortForwardSchema, StopPortForwardSchema, execInPodSchema, // API resource operations listApiResourcesSchema, // Generic kubectl command kubectlGenericSchema, // Ping utility pingSchema, ]; const k8sManager = new KubernetesManager(); const server = new Server({ name: serverConfig.name, version: serverConfig.version, }, { ...serverConfig, capabilities: { prompts: {}, ...serverConfig.capabilities, }, }); // Resources handlers const resourceHandlers = getResourceHandlers(k8sManager); server.setRequestHandler(ListResourcesRequestSchema, resourceHandlers.listResources); server.setRequestHandler(ReadResourceRequestSchema, resourceHandlers.readResource); // Register prompt handlers registerPromptHandlers(server, k8sManager); // Tools handlers server.setRequestHandler(ListToolsRequestSchema, async () => { let tools; if (allowedToolsEnv) { const allowedToolNames = allowedToolsEnv.split(",").map((t) => t.trim()); tools = allTools.filter((tool) => allowedToolNames.includes(tool.name)); } else if (allowOnlyReadonlyTools) { tools = readonlyTools; } else if (nonDestructiveTools) { tools = allTools.filter((tool) => !destructiveTools.some((dt) => dt.name === tool.name)); } else { tools = allTools; } return { tools }; }); server.setRequestHandler(CallToolRequestSchema, async (request) => { try { const { name, arguments: input = {} } = request.params; // Handle new kubectl-style commands if (name === "kubectl_context") { return await kubectlContext(k8sManager, input); } if (name === "kubectl_get") { return await kubectlGet(k8sManager, input); } if (name === "kubectl_describe") { return await kubectlDescribe(k8sManager, input); } if (name === "kubectl_apply") { return await kubectlApply(k8sManager, input); } if (name === "kubectl_delete") { return await kubectlDelete(k8sManager, input); } if (name === "kubectl_create") { return await kubectlCreate(k8sManager, input); } if (name === "kubectl_logs") { return await kubectlLogs(k8sManager, input); } if (name === "kubectl_patch") { return await kubectlPatch(k8sManager, input); } if (name === "kubectl_rollout") { return await kubectlRollout(k8sManager, input); } if (name === "kubectl_generic") { return await kubectlGeneric(k8sManager, input); } if (name === "kubectl_events") { return await kubectlGet(k8sManager, { resourceType: "events", namespace: input.namespace, fieldSelector: input.fieldSelector, labelSelector: input.labelSelector, sortBy: input.sortBy, output: input.output, context: input.context, }); } // Handle specific non-kubectl operations switch (name) { case "cleanup": { await k8sManager.cleanup(); return { content: [ { type: "text", text: JSON.stringify({ success: true, }, null, 2), }, ], }; } case "explain_resource": { return await explainResource(input); } case "install_helm_chart": { return await installHelmChart(input); } case "uninstall_helm_chart": { return await uninstallHelmChart(input); } case "upgrade_helm_chart": { return await upgradeHelmChart(input); } case "list_api_resources": { return await listApiResources(input); } case "port_forward": { return await startPortForward(k8sManager, input); } case "stop_port_forward": { return await stopPortForward(k8sManager, input); } case "kubectl_scale": { return await kubectlScale(k8sManager, input); } case "ping": { return await ping(); } case "exec_in_pod": { return await execInPod(k8sManager, input); } default: throw new McpError(ErrorCode.InvalidRequest, `Unknown tool: ${name}`); } } catch (error) { if (error instanceof McpError) throw error; throw new McpError(ErrorCode.InternalError, `Tool execution failed: ${error}`); } }); // Start the server if (process.env.ENABLE_UNSAFE_SSE_TRANSPORT) { startSSEServer(server); console.log(`SSE server started`); } else if (process.env.ENABLE_UNSAFE_STREAMABLE_HTTP_TRANSPORT) { startStreamableHTTPServer(server); console.log(`Streamable HTTP server started`); } else { const transport = new StdioServerTransport(); console.error(`Starting Kubernetes MCP server v${serverConfig.version}, handling commands...`); server.connect(transport); } ["SIGINT", "SIGTERM"].forEach((signal) => { process.on(signal, async () => { console.log(`Received ${signal}, shutting down...`); await server.close(); process.exit(0); }); }); export { allTools, destructiveTools };