mcp-server-kubernetes
Version:
MCP server for interacting with Kubernetes clusters via kubectl
239 lines (238 loc) • 9.4 kB
JavaScript
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 };