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

> Learn how MCP Server Kubernetes implements cordon uncordon and drain node management operations. Explore safety checks timeouts and confirmation requirements for safer cluster management.

- Repository: [Suyog Sonwalkar/mcp-server-kubernetes](https://github.com/flux159/mcp-server-kubernetes)
- Tags: internals
- Published: 2026-03-02

---

**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`](https://github.com/flux159/mcp-server-kubernetes/blob/main/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`](https://github.com/flux159/mcp-server-kubernetes/blob/main/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`](https://github.com/flux159/mcp-server-kubernetes/blob/main/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`](https://github.com/flux159/mcp-server-kubernetes/blob/main/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

```json
{
  "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

```json
{
  "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

```json
{
  "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`](https://github.com/flux159/mcp-server-kubernetes/blob/main/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`](https://github.com/flux159/mcp-server-kubernetes/blob/main/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`](https://github.com/flux159/mcp-server-kubernetes/blob/main/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.