# Defense-in-Depth Score: Amazon EKS MCP Server

**Repo:** https://github.com/awslabs/mcp (`src/eks-mcp-server`) · **Commit:** `93991b85acfe8d139abc069547973241f88c12e2` (0.2.1) · **Reviewed:** 2026-10-03
**What it is:** MCP server for EKS cluster management and Kubernetes resources
**Category:** Infrastructure & Ops
**Scored configuration:** Local stdio server started with 'uvx awslabs.eks-mcp-server' and no flags: read-only (allow_write=False), sensitive data access off, IAM auth mode with the operator's ambient AWS credential chain.
**Agent surface (default):** code execution opt-in · filesystem write opt-in · network egress yes · external credentials yes · persistent memory no · untrusted input yes · third party extensions no · sub agents no · external communication no

## Score: 4.7 / 10.0 (Minimal)

| # | Criterion | S | C | D | B | Raw | Cap | Score | Confidence |
|---|---|---|---|---|---|---|---|---|---|
| C1 | Identity & least privilege | L2 | L2 | L2 | L0 | 0.40 | — | **0.40** | High |
| C2 | Approval gates | L0 | L1 | L1 | L4 | 0.33 | — | **0.33** | High |
| C3 | Tool & action scoping | L1 | L2 | L2 | L1 | 0.38 | — | **0.38** | High |
| C4 | Code-execution isolation | L0 | L0 | L1 | L2 | 0.15 | — | **0.15** | High |
| C5 | Untrusted input blast radius | L2 | L2 | L3 | L2 | 0.55 | — | **0.55** | High |
| C6 | Memory, context & configuration integrity | SA | SA | SA | SA | 1.00 | — | **1.00** (SA) | High |
| C7 | Third-party extensions | SA | SA | SA | SA | 1.00 | — | **1.00** (SA) | High |
| C8 | Secrets & sensitive-data protection | L1 | L1 | L2 | L1 | 0.30 | — | **0.30** | High |
| C9 | Audit & traceability | L1 | L2 | L1 | L1 | 0.33 | — | **0.33** | Medium |
| C10 | Limits & kill switch | L1 | L1 | L1 | L1 | 0.25 | — | **0.25** | High |

Controls where a risk surface exists: 2.68 / 8.0 (34%); 2 criteria scored SA (surface absent).

With no flags this server is read-only: every tool that could change a cluster, deploy a CloudFormation stack, or edit IAM refuses in code, and logs, events and Secret reads are withheld. But it runs on the operator's full ambient AWS identity, its tools carry no risk annotations, and the README's own install buttons switch on both --allow-write and --allow-sensitive-data-access. Once they're on, the model can delete Kubernetes objects, deploy any CloudFormation template with IAM capability, and attach any IAM policy to any role, with no confirmation. Even in the default, the sensitive-data gate does not cover every path.

## Critical gaps
- Every AWS and Kubernetes call uses the operator's full ambient identity (README recommends IAMFullAccess for writes), so once the read-only check is off or bypassed nothing confines what the server can do, including adding arbitrary IAM policies to any role. (ASI03, T3; C1) — [src/eks-mcp-server/awslabs/eks_mcp_server/aws_helper.py:89-100](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/aws_helper.py#L89-L100); [src/eks-mcp-server/README.md:65](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/README.md#L65); [src/eks-mcp-server/awslabs/eks_mcp_server/iam_handler.py:338](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/iam_handler.py#L338)

## Criterion details

### C1 Identity & least privilege — 0.40 (high)

The server signs every AWS and Kubernetes call with the operator's own ambient credentials: the boto3 default chain or AWS_PROFILE, and for Kubernetes a token minted from that same identity for whatever cluster name the model supplies. Nothing narrows the credential itself. What the server does have is a deterministic read-only switch, on by default, that every mutating tool checks before touching a client, so the default server can only describe and list. Pass --allow-write, as every README install example does, and the model can create or delete any Kubernetes object, deploy CloudFormation stacks with IAM capability, and attach arbitrary inline policies to any IAM role, including the server's own.

- **S L2:** A fixed, code-enforced read-only mode (allow_write=False) blocks every mutating operation, but the credential is still the operator's full ambient identity, shared by reads and writes. — [src/eks-mcp-server/awslabs/eks_mcp_server/aws_helper.py:89-100](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/aws_helper.py#L89-L100); [src/eks-mcp-server/awslabs/eks_mcp_server/k8s_client_cache.py:140-145](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/k8s_client_cache.py#L140-L145); [src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py:421](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py#L421) (verified)
  - *To reach the next level:* Credentials are not narrowed per tool or per request, and requests are not mapped to AWS's or Kubernetes' own permission model before credentials are attached.
- **C L2:** Every mutating path in every handler (Kubernetes CRUD, apply_yaml, manifest generation, CloudFormation generate/deploy/delete, add_inline_policy) checks allow_write before acting, and the server loads no extensions, but each handler repeats its own check. — [src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py:421](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py#L421); [src/eks-mcp-server/awslabs/eks_mcp_server/eks_stack_handler.py:181](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/eks_stack_handler.py#L181); [src/eks-mcp-server/awslabs/eks_mcp_server/iam_handler.py:206](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/iam_handler.py#L206); [src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py:144](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py#L144); [src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py:732](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py#L732); [src/eks-mcp-server/awslabs/eks_mcp_server/k8s_client_cache.py:178](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/k8s_client_cache.py#L178) (verified)
  - *To reach the next level:* The check is duplicated per handler rather than one shared authorization layer new tools inherit, and the model may target any cluster name or kubeconfig context.
- **D L2:** Read-only is the code default and writes need the operator's --allow-write flag, but the README's lead configuration and one-click install buttons all pass --allow-write (lowered one level). — [src/eks-mcp-server/awslabs/eks_mcp_server/server.py:135-137](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/server.py#L135-L137); [src/eks-mcp-server/awslabs/eks_mcp_server/server.py:137](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/server.py#L137); [src/eks-mcp-server/README.md:125](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/README.md#L125) (verified)
  - *To reach the next level:* Least-privilege default is undermined by the official install examples, and nothing time-bounds or reverts write elevation.
- **B L0:** If the read-only check is bypassed or simply switched on, the operator's ambient AWS identity (README recommends IAMFullAccess plus eks:* for writes) and cluster-creator Kubernetes access are fully usable, including add_inline_policy on any role. — [src/eks-mcp-server/README.md:65](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/README.md#L65); [src/eks-mcp-server/awslabs/eks_mcp_server/iam_handler.py:338](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/iam_handler.py#L338); [src/eks-mcp-server/awslabs/eks_mcp_server/iam_handler.py:374](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/iam_handler.py#L374) (verified)
  - *To reach the next level:* Nothing confines the identity to one cluster, account scope, or non-destructive actions.
- **Cap:** none
- **Notes:** C1-SELFESC was not applied: add_inline_policy can grant any statement to any role (including the server's own role) but only when --allow-write is set; in the scored default it returns an error. With the README's lead configuration it would apply.

### C2 Approval gates — 0.33 (high)

The server owns no approval loop, so it is rated on what it gives the host. No tool carries read-only or destructive annotations, and two tools (manage_k8s_resource and manage_eks_stacks) mix read and write operations behind one name, so a host cannot tell a describe from a delete. There is no dry-run or preview. What protects the default install is the server-enforced read-only mode: with no flags, no tool can change anything. With --allow-write, deletes, stack deploys and IAM policy changes run immediately with no server-side confirmation.

- **S L0:** No tool has readOnlyHint/destructiveHint annotations, and manage_k8s_resource and manage_eks_stacks each mix reads and writes in one tool. — searched `rg -n 'readOnlyHint|destructiveHint|ToolAnnotations|annotations='` in `src/eks-mcp-server/awslabs` → 1 hits (the single hit is a Kubernetes metadata field in list output (k8s_handler.py:613), not an MCP tool annotation); [src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py:69-75](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py#L69-L75) (verified)
  - *To reach the next level:* No risk annotations on mutating tools; reads and writes are not separated into distinct tools.
- **C L1:** Every mutating path does hit the server's own read-only check before acting, but nothing signals risk to the host on any tool. — [src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py:421](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py#L421); [src/eks-mcp-server/awslabs/eks_mcp_server/eks_stack_handler.py:181](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/eks_stack_handler.py#L181); [src/eks-mcp-server/awslabs/eks_mcp_server/iam_handler.py:206](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/iam_handler.py#L206) (verified)
  - *To reach the next level:* No tool exposes risk signalling, so the host cannot gate mutating calls selectively once writes are enabled.
- **D L1:** Writes are off by default, but the README's install examples turn them on and the server then offers no confirmation step. — [src/eks-mcp-server/awslabs/eks_mcp_server/server.py:135-137](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/server.py#L135-L137); [src/eks-mcp-server/awslabs/eks_mcp_server/server.py:137](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/server.py#L137); [src/eks-mcp-server/README.md:125](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/README.md#L125) (verified)
  - *To reach the next level:* No confirmation or preview survives enabling writes; the official examples disable the protective default.
- **B L4:** In the default configuration every state-changing path is refused in code before a client is created, so a wrongly approved call cannot change anything; the only outbound call (the troubleshooting search) goes to a fixed AWS endpoint. — [src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py:421](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py#L421); [src/eks-mcp-server/awslabs/eks_mcp_server/eks_stack_handler.py:181](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/eks_stack_handler.py#L181); [src/eks-mcp-server/awslabs/eks_mcp_server/iam_handler.py:206](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/iam_handler.py#L206); [src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py:144](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py#L144); [src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py:732](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py#L732); [src/eks-mcp-server/awslabs/eks_mcp_server/eks_kb_handler.py:25](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/eks_kb_handler.py#L25) (verified)
- **Cap:** none
- **Notes:** Under the README's lead configuration (--allow-write), B would fall to L0: Kubernetes deletes, CloudFormation stack deletion and IAM policy writes are irreversible and unconfirmed.

### C3 Tool & action scoping — 0.38 (high)

The tools are broad. manage_k8s_resource performs any CRUD operation on any kind in any namespace of any cluster the model names; apply_yaml applies any manifest file; manage_eks_stacks deploys any CloudFormation template file with IAM capability; and add_inline_policy accepts arbitrary policy statements for any role. File paths are resolved and checked only against a short denylist of sensitive directories, and the app-manifest generator pastes unvalidated values (image, namespace, CPU, memory) into YAML by string replacement. The default read-only mode keeps the write tools inert, but the read tools still reach every resource kind across every reachable cluster.

- **S L1:** Inputs are typed and operations are enum-checked, but scoping is generic (any kind, any template, any IAM statement) and file paths are checked against a denylist of sensitive directories. — [src/eks-mcp-server/awslabs/eks_mcp_server/path_validation.py:22-30](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/path_validation.py#L22-L30); [src/eks-mcp-server/awslabs/eks_mcp_server/path_validation.py:34-35](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/path_validation.py#L34-L35); [src/eks-mcp-server/awslabs/eks_mcp_server/k8s_apis.py:229](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/k8s_apis.py#L229); [src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py:877](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py#L877) (verified)
  - *To reach the next level:* No allowlist containment for file paths, no restriction on kinds, namespaces, templates or policy statements, and template values are substituted without validation.
- **C L2:** Every file-path argument goes through the shared path validator, app_name is checked against RFC 1123, and operations are enum-checked; CloudWatch log group names and query fragments are passed through raw. — [src/eks-mcp-server/awslabs/eks_mcp_server/eks_stack_handler.py:359](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/eks_stack_handler.py#L359); [src/eks-mcp-server/awslabs/eks_mcp_server/cloudwatch_handler.py:201](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/cloudwatch_handler.py#L201); [src/eks-mcp-server/awslabs/eks_mcp_server/cloudwatch_handler.py:217](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/cloudwatch_handler.py#L217) (verified)
  - *To reach the next level:* Validation is not applied to Kubernetes kinds/namespaces, IAM statements, CloudWatch query text, or template values.
- **D L2:** The effective default tool set is read-only and sensitive reads are off, but all write tools are still registered and the README's lead configuration enables them (lowered one level). — [src/eks-mcp-server/awslabs/eks_mcp_server/server.py:135-137](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/server.py#L135-L137); [src/eks-mcp-server/awslabs/eks_mcp_server/server.py:137](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/server.py#L137); [src/eks-mcp-server/awslabs/eks_mcp_server/server.py:180](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/server.py#L180); [src/eks-mcp-server/README.md:125](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/README.md#L125) (verified)
  - *To reach the next level:* Write tools are advertised to the model in every configuration and the official examples turn them on; no per-task allowlist.
- **B L1:** In the default configuration a misused tool can read any resource kind (ConfigMaps, RBAC, pod specs) in any namespace of any cluster the operator's identity reaches, plus IAM role policies. — [src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py:589](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py#L589); [src/eks-mcp-server/awslabs/eks_mcp_server/k8s_apis.py:304](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/k8s_apis.py#L304) (verified)
  - *To reach the next level:* Reads are not scoped to a namespace or cluster allowlist and are not quantity-bounded.
- **Cap:** none
- **Notes:** With --allow-write, generate (manage_eks_stacks) writes the template to any path outside the blocked directories (eks_stack_handler.py:274, 311), so it can overwrite arbitrary user files such as shell profiles with template YAML.

### C4 Code-execution isolation — 0.15 (high)

The server never runs a shell, eval, or subprocess locally. It can, however, have model-chosen code run elsewhere: apply_yaml and manage_k8s_resource can create pods with any image and any security context, and manage_eks_stacks deploys any CloudFormation template with IAM capability in the operator's account. There is no isolation around any of these. The only thing standing in the way is the read-only default: without --allow-write none of these paths runs.

- **S L0:** Model-chosen manifests and CloudFormation templates are sent to the cluster and AWS with the operator's credentials; no isolation primitive exists. — [src/eks-mcp-server/awslabs/eks_mcp_server/k8s_apis.py:375-378](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/k8s_apis.py#L375-L378); [src/eks-mcp-server/awslabs/eks_mcp_server/eks_stack_handler.py:420](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/eks_stack_handler.py#L420); searched `rg -n 'eval\(|exec\(|shell=True'` in `src/eks-mcp-server/awslabs` → 0 hits (no local code execution) (verified)
  - *To reach the next level:* No sandbox or policy constrains the pods or stacks the model can create (for example, no ban on privileged pods or IAM resources).
- **C L0:** Neither remote execution path (Kubernetes object creation, CloudFormation deploy) passes through any isolation layer. — [src/eks-mcp-server/awslabs/eks_mcp_server/k8s_apis.py:375-378](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/k8s_apis.py#L375-L378); [src/eks-mcp-server/awslabs/eks_mcp_server/eks_stack_handler.py:417-423](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/eks_stack_handler.py#L417-L423) (verified)
  - *To reach the next level:* No execution path is isolated.
- **D L1:** Both execution paths are disabled unless the operator passes --allow-write, which the README's install examples do; once on, nothing constrains them. — [src/eks-mcp-server/awslabs/eks_mcp_server/server.py:135-137](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/server.py#L135-L137); [src/eks-mcp-server/awslabs/eks_mcp_server/server.py:137](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/server.py#L137); [src/eks-mcp-server/README.md:125](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/README.md#L125) (verified)
  - *To reach the next level:* There is no sandbox to be on by default; protection depends on the write flag staying off.
- **B L2:** In the default configuration the independent read-only check stops every execution path, but once writes are on, model-chosen pods and stacks run with cluster-admin-equivalent and IAM-capable authority. — [src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py:421](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py#L421); [src/eks-mcp-server/awslabs/eks_mcp_server/eks_stack_handler.py:181](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/eks_stack_handler.py#L181); [src/eks-mcp-server/awslabs/eks_mcp_server/iam_handler.py:206](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/iam_handler.py#L206); [src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py:144](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py#L144) (verified)
  - *To reach the next level:* Reach after the write flag is set is not confined (privileged pods, IAM roles via CloudFormation).
- **Cap:** none
- **Notes:** kubeconfig auth mode (opt-in) lets the kubernetes client run exec credential plugins defined in the operator's own kubeconfig; that is user-scope configuration, not model-controlled.

### C5 Untrusted input blast radius — 0.55 (high)

Tool results come back as a short status line plus a JSON document carrying the cluster, namespace, kind, and name each result came from, kept separate from instructions; the troubleshooting search returns the remote service's text verbatim. Nothing marks content as untrusted, and resource annotations, ConfigMaps and pod specs, which anyone who can edit cluster objects controls, reach the model as plain data. The defaults drop two risky legs: logs and events are withheld, and nothing can be changed. A hijacked host agent can still read sensitive cluster data through this server, but cannot use it to act or send data out.

- **S L2:** Results are structured: a status message plus a pydantic model serialized to JSON with source identifiers (cluster, namespace, kind, name, log group). — [src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py:628-634](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py#L628-L634); [src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py:645](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py#L645); [src/eks-mcp-server/awslabs/eks_mcp_server/eks_kb_handler.py:93](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/eks_kb_handler.py#L93) (verified)
  - *To reach the next level:* No untrusted/provenance flag the host can act on, and the troubleshooting search returns raw remote text.
- **C L2:** Every tool except the troubleshooting search returns the same structured form. — [src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py:1127-1133](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py#L1127-L1133); [src/eks-mcp-server/awslabs/eks_mcp_server/eks_kb_handler.py:93](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/eks_kb_handler.py#L93) (verified)
  - *To reach the next level:* The knowledge-base result is passed through unstructured, and no source is tagged as untrusted.
- **D L3:** The output structure is fixed in code and cannot be changed by configuration or content. — [src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py:645](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py#L645) (verified)
  - *To reach the next level:* Nothing the server reads is marked untrusted, so the host has no flag to enforce.
- **B L2:** In the default configuration a hijacked model can pull sensitive cluster data (ConfigMaps, pod environment, IAM policies) into context, but the server offers no state change and no attacker-reachable outbound channel. — [src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py:421](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py#L421); [src/eks-mcp-server/awslabs/eks_mcp_server/eks_stack_handler.py:181](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/eks_stack_handler.py#L181); [src/eks-mcp-server/awslabs/eks_mcp_server/iam_handler.py:206](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/iam_handler.py#L206) (verified)
  - *To reach the next level:* Sensitive data remains reachable from sessions that read attacker-controlled annotations and specs.
- **Cap:** none
- **Notes:** Under the README's lead configuration (both flags on) B falls to L0: logs/events/Secrets plus unconfirmed deletes, stack deploys and IAM changes. The server instructions tell the model not to use kubectl/eksctl/aws CLIs (server.py:48-50); these are vendor tool-routing directives, not content mixing.

### C6 Memory, context & configuration integrity — 1.00 (high)

The server keeps no memory, conversation store, or retrieval index and reads no instruction or configuration files from the working directory. Its configuration comes only from command-line flags and environment variables (auth mode, AWS profile, region, KUBECONFIG path), all set by the operator. Cluster objects the model writes and later reads back are untrusted input, covered in C5.

- **Structural absence:** searched `rg -n 'dotenv|getcwd|Path\.cwd|memory_store|save_memory'` in `src/eks-mcp-server/awslabs` → 0 hits (no dotenv, no working-directory config lookup, no memory store); [src/eks-mcp-server/awslabs/eks_mcp_server/server.py:159-164](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/server.py#L159-L164)

### C7 Third-party extensions — 1.00 (high)

The server loads no plugins, launches no MCP servers or subprocesses, installs no packages, and deserializes no model files. Container images and CloudFormation templates the model asks it to deploy run in the cluster or AWS account and are scored under C4 and C3.

- **Structural absence:** searched `rg -n 'subprocess|importlib|entry_points|pickle|Popen|os\.system|pip install|trust_remote_code'` in `src/eks-mcp-server/awslabs` → 0 hits (no plugin loading, subprocess launch, package install or model deserialization)

### C8 Secrets & sensitive-data protection — 0.30 (high)

The server never puts its own AWS credentials or Kubernetes token into tool output; the Kubernetes token is minted from the ambient identity and cached for 14 minutes. A sensitive-data flag, off by default, withholds pod logs, events, CloudWatch logs and Secret reads. But it is only a gate on a few tool paths: its enforcement is not complete, pod and deployment specs with plaintext environment values are readable, and nothing is redacted anywhere. Logging goes through an unconfigured loguru logger, which emits debug lines (including any proxy URL) to stderr.

- **S L1:** A boolean flag blocks some sensitive sources (Secret reads via manage_k8s_resource, pod logs, events, CloudWatch logs); there is no masking or redaction anywhere. — [src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py:430-433](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py#L430-L433); [src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py:950](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py#L950); [src/eks-mcp-server/awslabs/eks_mcp_server/k8s_apis.py:137](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/k8s_apis.py#L137) (verified)
  - *To reach the next level:* No redaction of secret values in model-bound output or logs; the gate is per-tool, not type-level.
- **C L1:** Only the model-bound path for a few tools is protected, and the sensitive-data gate does not cover every path. — [src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py:589-595](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py#L589-L595) (verified)
  - *To reach the next level:* Logs and error messages are unprotected, and the gate is not enforced uniformly.
- **D L2:** Sensitive access is off by default and there is no telemetry beyond a user-agent suffix, but the README's install examples enable sensitive access. — [src/eks-mcp-server/awslabs/eks_mcp_server/server.py:141-143](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/server.py#L141-L143); [src/eks-mcp-server/awslabs/eks_mcp_server/aws_helper.py:87](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/aws_helper.py#L87); [src/eks-mcp-server/README.md:125](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/README.md#L125) (verified)
  - *To reach the next level:* The flag is enabled by the official examples, and logging is left at the library's verbose default.
- **B L1:** The server's own credentials stay out of model context, but long-lived cluster secrets (for example plaintext env values in pod specs) are reachable by the model by default. — [src/eks-mcp-server/awslabs/eks_mcp_server/k8s_client_cache.py:31](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/k8s_client_cache.py#L31) (verified)
  - *To reach the next level:* Cluster secrets the model can read are not short-lived or scoped.
- **Cap:** none
- **Notes:** The C and B ratings also reflect that the sensitive-data gate does not cover every path.

### C9 Audit & traceability — 0.33 (medium)

Each tool writes free-text log lines prefixed with the MCP request ID, naming the operation and target (for example 'Deleted Pod ns/name') and recording write-mode and sensitive-data refusals as errors. The logger is never configured, so lines go to stderr at the library's default level, and FASTMCP_LOG_LEVEL, which the README sets, is not read. Nothing is written to a file, arguments such as patch bodies or policy statements are not recorded, nothing identifies the requesting user, and nothing protects the record from tampering.

- **S L1:** Unstructured text lines with a request ID and a short description of the action. — [src/eks-mcp-server/awslabs/eks_mcp_server/logging_helper.py:43](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/logging_helper.py#L43); [src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py:469-471](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py#L469-L471) (verified)
  - *To reach the next level:* No structured per-call record of arguments, result status and timestamps.
- **C L2:** Nearly every tool logs its action and its refusals; the troubleshooting search logs only on failure. — [src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py:145-146](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py#L145-L146); [src/eks-mcp-server/awslabs/eks_mcp_server/eks_kb_handler.py:98](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/eks_kb_handler.py#L98) (verified)
  - *To reach the next level:* Approvals are not the server's to log, and the search tool's successful calls and full arguments elsewhere are not recorded.
- **D L1:** Logging is on by default but only to the process's stderr; the logger is never configured and the documented log-level variable is unused. — searched `rg -n 'logger\.add|logger\.remove|FASTMCP_LOG_LEVEL'` in `src/eks-mcp-server/awslabs` → 1 hits (the single hit is the docstring mentioning FASTMCP_LOG_LEVEL (server.py:25); no code configures loguru) (inferred)
  - *To reach the next level:* Nothing is persisted by the server outside the host's stderr handling.
- **B L1:** Logging is best-effort; a failure to write does not stop an action. — [src/eks-mcp-server/awslabs/eks_mcp_server/logging_helper.py:43](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/logging_helper.py#L43) (verified)
  - *To reach the next level:* Records are not durable per action and cannot replay a trajectory.
- **Cap:** none
- **Notes:** The stderr destination and DEBUG default level are loguru's library defaults (inferred from the absence of any logger.add/remove call). CloudFormation and Kubernetes audit logs on the AWS side are not the server's own record.

### C10 Limits & kill switch — 0.25 (high)

The server bounds little of its own work. Pod logs default to the last 100 lines and 10 KB, CloudWatch queries to 50 entries, but the caller can raise each. Resource listing has no limit, Kubernetes and knowledge-base HTTP calls have no timeout, and the CloudWatch poll loop calls a blocking sleep inside an async handler for up to 60 attempts, leaving the query running when it gives up. There is no rate limit or cancellation.

- **S L1:** Limits are caller-chosen defaults (tail_lines, limit_bytes, limit); the only server-enforced bound is the CloudWatch poll attempt count. — [src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py:905-911](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py#L905-L911); [src/eks-mcp-server/awslabs/eks_mcp_server/cloudwatch_handler.py:523](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/cloudwatch_handler.py#L523); [src/eks-mcp-server/awslabs/eks_mcp_server/eks_kb_handler.py:81-85](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/eks_kb_handler.py#L81-L85); searched `rg -n 'timeout=|_request_timeout'` in `src/eks-mcp-server/awslabs` → 1 hits (the single hit is a maintainer script (scripts/update_eks_cloudwatch_metrics_guidance.py), not a server runtime path) (verified)
  - *To reach the next level:* No server-enforced caps on list size or request timeouts.
- **C L1:** The poll cap covers one tool; listing and every HTTP call are unbounded. — [src/eks-mcp-server/awslabs/eks_mcp_server/k8s_apis.py:304](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/k8s_apis.py#L304) (verified)
  - *To reach the next level:* Bounds do not cover most operations.
- **D L1:** Default limits exist for logs, but the model can raise them per call. — [src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py:909-911](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/k8s_handler.py#L909-L911) (verified)
  - *To reach the next level:* The model can raise every limit.
- **B L1:** A runaway host can issue unbounded reads, and a blocking sleep in the async poll stalls the whole server; nothing cancels in-flight calls. — [src/eks-mcp-server/awslabs/eks_mcp_server/cloudwatch_handler.py:587](https://github.com/awslabs/mcp/blob/93991b85acfe8d139abc069547973241f88c12e2/src/eks-mcp-server/awslabs/eks_mcp_server/cloudwatch_handler.py#L587) (verified)
  - *To reach the next level:* No ceilings and no cancellation of in-flight work.
- **Cap:** none

## Rule-of-Two check
[A] untrusted input: resource annotations, labels and specs any cluster editor controls (k8s_handler.py:613; manage_k8s_resource read) · [B] sensitive data/systems: ConfigMaps, pod env, IAM role policies (k8s_handler.py:589; iam_handler.py:100) · [C] state change / egress: off by default: all mutations refused without --allow-write (k8s_handler.py:421, eks_stack_handler.py:181, iam_handler.py:206) · Same default session? No

## Highest-impact improvements
1. Add readOnlyHint/destructiveHint annotations to every tool and split manage_k8s_resource and manage_eks_stacks into separate read and write tools. — C2 S L0→L2, +0.150 before caps (Playbook 5, step 1)
2. Apply the sensitive-data check uniformly to every read path and redact Secret-like values from model-bound output. — C8 C L1→L2, +0.075 before caps (Playbook 4)
3. Ship the README install examples and buttons without --allow-write/--allow-sensitive-data-access, leaving them as explicit opt-ins. — C1 D L2→L3, +0.050 before caps (Playbook 4)
4. Configure the logger from FASTMCP_LOG_LEVEL and write a structured per-call record (tool, arguments, result status, timestamp) to a file outside the workspace. — C9 S L1→L2, +0.075 before caps (Playbook 1, step 3)
5. Add request timeouts to Kubernetes and knowledge-base calls, a server-side cap on list results, and replace the blocking sleep with an async wait that stops the query on timeout. — C10 S L1→L2, +0.075 before caps (Playbook 3, step 3)

## Re-audit log
- No changes.

## Limitations
- Static source review of src/eks-mcp-server at the pinned commit only; nothing was installed, built, or run.
- Scored the code default (no flags). The README's lead configuration and one-click install buttons pass --allow-write and --allow-sensitive-data-access; D was lowered one level where those flags are the control, and the notes on C1, C2 and C5 describe how the score changes with them on.
- kubeconfig auth mode was read but not scored as the primary mode; it registers only the Kubernetes tools and lets the model choose any kubeconfig context.
- Logging destination and level rely on loguru library defaults, since the server never configures the logger.
- No reviewer-injection or audit-steering text was found in the README, source, or templates (no AGENTS.md/CLAUDE.md in the subpath).
