Node Management Operations in MCP Server Kubernetes: Cordon, Uncordon, and Drain Implementation

The nodeManagement tool in flux159/mcp-server-kubernetes implements cordon, uncordon, and drain operations by wrapping kubectl commands with safety checks, timeouts, and explicit confirmation requirements.

The flux159/mcp-server-kubernetes repository provides a Model Context Protocol (MCP) server that exposes Kubernetes cluster operations as callable tools. Node management operations—specifically cordon, uncordon, and drain—are implemented in src/tools/node_management.ts as a single dispatcher function that routes to specialized handlers based on the requested operation.

Core Implementation Architecture

The node management functionality centers on the nodeManagement() function exported from src/tools/node_management.ts. This function acts as a dispatcher that validates input parameters and delegates to three internal handlers:

  • handleCordonNode() – Marks a node as unschedulable
  • handleUncordonNode() – Marks a node as schedulable
  • handleDrainNode() – Evicts pods and prepares a node for maintenance

Each handler follows a consistent pattern: verify node existence via getNodeStatus(), check current state for idempotency, execute the appropriate kubectl command via executeCommand(), and return structured results.

Tool Schema and Parameter Validation

The tool schema defined in lines 24-87 of src/tools/node_management.ts specifies the JSON payload structure accepted by the MCP server. The schema requires an operation field (enum: cordon, uncordon, drain) and a nodeName string.

For drain operations, the schema exposes optional flags that map directly to kubectl drain options:

  • force – Delete pods not managed by a ReplicationController, ReplicaSet, Job, DaemonSet, or StatefulSet
  • gracePeriod – Time to wait for pod termination (seconds)
  • ignoreDaemonsets – Ignore DaemonSet-managed pods
  • deleteLocalData – Continue even if pods use emptyDir volumes
  • timeout – Maximum time to wait for drain completion (e.g., 5m)
  • dryRun – Execute as client-side dry run
  • confirmDrain – Required boolean to prevent accidental execution

The TypeScript interface NodeManagementParams (lines 92-103) mirrors this schema for internal type safety.

Command Execution Infrastructure

All kubectl invocations route through the executeCommand() helper function (lines 112-123). This wrapper provides critical production safeguards:

Timeout Protection: Commands execute with a hard 5-minute timeout to prevent hanging operations from blocking the MCP server indefinitely.

Buffer Management: The function calls getSpawnMaxBuffer() (from src/config/max-buffer.ts) to determine the maximum stdout/stderr buffer size. By default, this is 1,048,577 bytes, but operators can override via the SPAWN_MAX_BUFFER environment variable.

Environment Propagation: The wrapper explicitly passes process.env.KUBECONFIG to the child process, ensuring kubectl uses the correct cluster credentials regardless of how the MCP server was launched.

Error handling converts non-zero exit codes and timeouts into descriptive exceptions that bubble up to the MCP response.

Node Status Verification

Before modifying node state, handlers invoke getNodeStatus() (lines 131-142) to fetch current node metadata. This function executes kubectl get node <name> -o json and parses the JSON output to determine:

  • Node existence (error if node not found)
  • Current spec.unschedulable status (boolean)

This verification enables idempotent operations—cordon requests against already-unschedulable nodes return immediately with a friendly message rather than re-executing kubectl cordon.

Operation Handlers

Cordon Operation

The handleCordonNode() function (lines 198-236) implements the cordon workflow:

  1. Retrieve node status via getNodeStatus()
  2. Check if spec.unschedulable is already true; if so, return "already cordoned" message
  3. Execute kubectl cordon <nodeName> via executeCommand()
  4. Return success confirmation

This prevents unnecessary kubectl invocations when the desired state already exists.

Uncordon Operation

The handleUncordonNode() function (lines 238-276) mirrors the cordon logic:

  1. Fetch node status
  2. Check if spec.unschedulable is false; if so, return "already schedulable" message
  3. Execute kubectl uncordon <nodeName>
  4. Return success confirmation

Drain Operation

The handleDrainNode() function (lines 280-369) is the most complex handler, implementing safe node eviction:

Pre-flight Checks:

  • Verifies node exists and is currently schedulable (draining an already cordoned node is rejected as a no-op)
  • Requires confirmDrain: true unless dryRun: true is set, preventing accidental cluster disruption

Command Construction: Builds the kubectl drain argument array based on provided flags:

  • --force (if force: true)
  • --grace-period=<seconds> (if gracePeriod specified)
  • --delete-local-data (if deleteLocalData: true)
  • --ignore-daemonsets (if ignoreDaemonsets: true)
  • --timeout=<duration> (if timeout specified)
  • --dry-run=client (if dryRun: true)

Execution: Invokes executeCommand() with the constructed argument list. Returns dry-run output verbatim for inspection, or success confirmation for actual drain operations.

Safety Mechanisms and Error Handling

The implementation incorporates multiple safeguards to prevent accidental cluster damage:

Idempotency: Cordon and uncordon operations check current node state before invoking kubectl. If the node already matches the desired state, the handler returns immediately with an informational message, avoiding unnecessary API calls.

Explicit Confirmation: The drain operation requires confirmDrain: true to proceed. This acts as a circuit breaker against automated scripts accidentally evicting production workloads. The dry-run capability allows operators to preview impact before confirming.

Timeout Protection: All kubectl invocations are wrapped with a 5-minute timeout via execFileSync. This prevents the MCP server from hanging indefinitely if the Kubernetes API becomes unresponsive during drain operations.

Buffer Management: Large output streams from kubectl drain (which can be verbose) are handled via configurable buffer sizes defaulting to ~1MB, with environment variable overrides available for high-volume clusters.

Credential Propagation: The KUBECONFIG environment variable is explicitly forwarded to child processes, ensuring kubectl authenticates correctly regardless of how the MCP server process was launched.

Usage Examples

Example 1: Cordon a Node

{
  "tool": "node_management",
  "input": {
    "operation": "cordon",
    "nodeName": "worker-01"
  }
}

Result (when the node is not already cordoned):

Successfully cordoned node 'worker-01'. The node is now unschedulable.

Example 2: Dry-Run Drain with Confirmation Bypass

{
  "tool": "node_management",
  "input": {
    "operation": "drain",
    "nodeName": "worker-02",
    "dryRun": true,
    "ignoreDaemonsets": true,
    "confirmDrain": false
  }
}

Result:

Dry run drain operation for node 'worker-02':

(output from kubectl drain … --dry-run=client)

Example 3: Full Drain with Force and Timeout

{
  "tool": "node_management",
  "input": {
    "operation": "drain",
    "nodeName": "worker-03",
    "force": true,
    "gracePeriod": 30,
    "ignoreDaemonsets": true,
    "timeout": "5m",
    "dryRun": false,
    "confirmDrain": true
  }
}

Result (on success):

Successfully drained node 'worker-03'.

(full kubectl drain stdout)

Summary

  • The node management functionality is centralized in src/tools/node_management.ts through the nodeManagement() dispatcher function.
  • Three operations are supported: cordon (make unschedulable), uncordon (make schedulable), and drain (evict pods safely).
  • All kubectl invocations use executeCommand(), which enforces a 5-minute timeout, configurable buffer sizes via SPAWN_MAX_BUFFER, and proper KUBECONFIG propagation.
  • Idempotency is enforced for cordon/uncordon by checking spec.unschedulable status before executing commands.
  • Drain operations require explicit confirmation via confirmDrain: true unless running in dryRun mode, preventing accidental workload eviction.
  • The implementation supports standard kubectl drain flags including --force, --grace-period, --delete-local-data, --ignore-daemonsets, and --timeout.

Frequently Asked Questions

How does the MCP server prevent accidental node draining?

The handleDrainNode() function in src/tools/node_management.ts requires the confirmDrain parameter to be set to true before executing any destructive operations. If confirmDrain is false or omitted, the function returns an error message requesting explicit confirmation. This safety check can be bypassed only when dryRun: true is set, allowing operators to preview the impact without risk.

What happens if I try to cordon a node that is already cordoned?

The implementation checks the node's current spec.unschedulable status via getNodeStatus() before invoking kubectl cordon. If the node is already unschedulable, handleCordonNode() returns immediately with a message indicating the node is already cordoned, avoiding unnecessary API calls and ensuring idempotent behavior.

How are long-running drain operations handled to prevent server timeouts?

All kubectl commands execute through executeCommand(), which wraps child_process.execFileSync with a hardcoded 5-minute timeout. This prevents the MCP server from hanging indefinitely if the Kubernetes API becomes unresponsive during pod eviction. Additionally, the timeout parameter in drain operations maps to kubectl drain --timeout, allowing users to specify shorter internal timeouts for the drain process itself.

Can the buffer size be adjusted for high-volume drain output?

Yes. The executeCommand() function retrieves the maximum buffer size from getSpawnMaxBuffer() in src/config/max-buffer.ts. By default, this is set to 1,048,577 bytes, but operators can override it by setting the SPAWN_MAX_BUFFER environment variable before starting the MCP server. This ensures that verbose drain output does not cause buffer overflow errors on large clusters.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →