# How the kubectl_patch Tool Handles Strategic, Merge, and JSON Patches in Kubernetes

> Learn how kubectl_patch handles strategic, merge, and JSON patches in Kubernetes by mapping patchType to the --type flag and managing patch data efficiently.

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

---

**The kubectl_patch tool supports three Kubernetes patch types—strategic (default), merge, and JSON—by mapping the `patchType` parameter to kubectl's `--type` flag and managing patch data through temporary files or direct file references.**

The `kubectl_patch` tool in the [flux159/mcp-server-kubernetes](https://github.com/flux159/mcp-server-kubernetes) repository provides MCP clients with a type-safe wrapper around `kubectl patch`, abstracting the complexity of RFC 6902 JSON patches, JSON Merge patches, and Strategic Merge patches while handling resource cleanup and error propagation automatically.

## Overview of Patch Types in kubectl_patch

The tool implements the three standard Kubernetes patching strategies as defined by the upstream kubectl CLI. Each strategy modifies resources differently and suits specific use cases:

- **Strategic merge patch** (default): Uses server-side logic to merge lists based on merge keys like `name` or `containerPort`, ideal for native Kubernetes resources such as Deployments and Services.
- **Merge patch**: Implements JSON Merge Patch (RFC 7386), which replaces entire fields rather than merging them, useful for generic or custom resources.
- **JSON patch**: Implements JSON Patch (RFC 6902) with explicit operations (`add`, `remove`, `replace`, `move`, `copy`, `test`), providing surgical precision for specific field modifications.

## Implementation Details in src/tools/kubectl-patch.ts

The core implementation resides in **[`src/tools/kubectl-patch.ts`](https://github.com/flux159/mcp-server-kubernetes/blob/main/src/tools/kubectl-patch.ts)**, where the tool constructs shell commands, validates inputs, and manages the lifecycle of temporary patch files.

### Input Validation and Defaults

The tool enforces strict input requirements at lines 70-74, throwing a `McpError` with `ErrorCode.InvalidRequest` if neither `patchData` nor `patchFile` is provided. Default values are established at lines 77-79: `namespace` falls back to `"default"`, `patchType` defaults to `"strategic"`, and `dryRun` initializes to `false`.

### Command Assembly and Patch Type Selection

Command construction begins at line 84 with the base array `["patch", resourceType, name, "-n", namespace]`. A `switch` statement at lines 87-99 injects the appropriate `--type` flag based on the `patchType` parameter:

- `"strategic"` → `--type strategic` (line 89)
- `"merge"` → `--type merge` (line 92)
- `"json"` → `--type json` (line 95)

### Payload Handling and Execution

For inline JSON payloads (`patchData`), the tool creates a temporary file under `os.tmpdir()` with a unique name at lines 111-114, passing the path via `--patch-file`. When `patchFile` is provided, the path is forwarded directly at lines 115-117. Optional flags including `--dry-run=client` (lines 120-122) and `--context` (lines 124-126) are appended before execution via `execFileSync` with a bounded buffer from `getSpawnMaxBuffer()` (lines 131-135).

Cleanup logic at lines 137-164 ensures temporary files are deleted regardless of execution success or failure. Error handling at lines 155-168 wraps execution failures in `McpError` instances with `ErrorCode.InternalError`, preserving stack traces while providing actionable error messages.

## Code Examples

The following examples demonstrate how MCP clients interact with the `kubectl_patch` tool through the SDK. All assume an established client connection.

### Strategic Merge Patch (Default)

```typescript
await client.request(
  {
    method: "tools/call",
    params: {
      name: "kubectl_patch",
      arguments: {
        resourceType: "deployment",
        name: "web-server",
        namespace: "production",
        patchData: {
          spec: {
            replicas: 3,
            template: {
              spec: {
                containers: [{
                  name: "nginx",
                  image: "nginx:1.25"
                }]
              }
            }
          }
        }
      }
    }
  },
  z.any()
);

```

*Implementation reference:* Default `patchType` handling at line 78 and strategic flag injection at line 89.

### JSON Merge Patch

```typescript
await client.request(
  {
    method: "tools/call",
    params: {
      name: "kubectl_patch",
      arguments: {
        resourceType: "configmap",
        name: "app-config",
        namespace: "default",
        patchType: "merge",
        patchData: {
          data: {
            LOG_LEVEL: "debug"
          }
        }
      }
    }
  },
  z.any()
);

```

*Implementation reference:* Merge type flag at line 92.

### JSON Patch (RFC 6902)

```typescript
await client.request(
  {
    method: "tools/call",
    params: {
      name: "kubectl_patch",
      arguments: {
        resourceType: "pod",
        name: "worker-7d9f4",
        namespace: "batch-jobs",
        patchType: "json",
        patchData: [
          { op: "replace", path: "/spec/containers/0/image", value: "busybox:1.36" },
          { op: "add", path: "/metadata/labels/environment", value: "staging" },
          { op: "remove", path: "/spec/terminationGracePeriodSeconds" }
        ]
      }
    }
  },
  z.any()
);

```

*Implementation reference:* JSON type flag at line 95.

### Using External Patch Files

```typescript
await client.request(
  {
    method: "tools/call",
    params: {
      name: "kubectl_patch",
      arguments: {
        resourceType: "service",
        name: "api-gateway",
        namespace: "ingress",
        patchFile: "/opt/patches/service-annotations.yaml"
      }
    }
  },
  z.any()
);

```

*Implementation reference:* File path handling at lines 115-117.

### Dry-Run Mode

```typescript
await client.request(
  {
    method: "tools/call",
    params: {
      name: "kubectl_patch",
      arguments: {
        resourceType: "deployment",
        name: "critical-app",
        namespace: "production",
        patchType: "strategic",
        patchData: {
          spec: { replicas: 10 }
        },
        dryRun: true
      }
    }
  },
  z.any()
);

```

*Implementation reference:* Dry-run flag injection at line 121.

## Error Handling and Resource Cleanup

The tool implements defensive programming patterns to prevent resource leaks and provide clear failure diagnostics. As implemented in [`src/tools/kubectl-patch.ts`](https://github.com/flux159/mcp-server-kubernetes/blob/main/src/tools/kubectl-patch.ts), any `execFileSync` failure triggers the cleanup block at lines 137-164, which removes temporary JSON files created for inline patch data. Errors are wrapped at lines 155-168 with descriptive messages indicating whether the failure occurred during command execution, file system operations, or JSON serialization. Unexpected errors outside the main try-catch block are also captured and re-wrapped at lines 170-178 to ensure all exceptions conform to the MCP protocol's error structure.

## Summary

- **Strategic merge** is the default patching strategy, activated when `patchType` is omitted or explicitly set to `"strategic"`, utilizing kubectl's server-side merging logic.
- **JSON Merge** and **JSON Patch** require explicit `patchType` values of `"merge"` and `"json"` respectively, each triggering different kubectl `--type` flags and accepting distinct payload structures.
- **Temporary file management** handles inline `patchData` by writing to `os.tmpdir()` and automatically cleaning up after execution, while `patchFile` references existing server-side files directly.
- **Safety features** include client-side dry-run support via the `dryRun` parameter and comprehensive error wrapping that converts all failures into standardized `McpError` objects.

## Frequently Asked Questions

### What happens if I provide both patchData and patchFile to kubectl_patch?

The tool validates inputs at lines 70-74 and requires at least one of these parameters, but does not explicitly reject providing both. When both are present, the implementation prioritizes `patchData` because the conditional logic at lines 111-114 creates a temporary file and appends `--patch-file` before checking `patchFile` at lines 115-117, effectively overwriting the file path argument.

### Why does kubectl_patch create temporary files instead of passing JSON directly to kubectl?

The implementation uses temporary files (lines 111-114) because kubectl's `--patch` flag has strict length limitations and shell escaping requirements that can corrupt complex JSON payloads. Writing to a temporary file via `--patch-file` ensures large patches, special characters, and multi-line strings are handled correctly without command-line injection risks or buffer overflows.

### How does the tool handle kubectl context switching?

The tool accepts an optional `context` parameter that appends `--context` to the command array at lines 124-126. This allows MCP clients to target specific clusters defined in the kubeconfig file without modifying the `KUBECONFIG` environment variable, which is inherited from the parent process as referenced at lines 131-135.

### What patch type should I use for Custom Resource Definitions (CRDs)?

For CRDs, use **JSON Merge Patch** (`patchType: "merge"`) or **JSON Patch** (`patchType: "json"`). According to the Kubernetes API conventions, Strategic Merge Patch requires server-side knowledge of resource schemas and merge keys, which native resources possess but most CRDs lack unless explicitly defined in the CRD's OpenAPI v3 schema.