Skip to main content
Glama
getsuga-tenshou

AWS Infrastructure MCP Server

README.md
# AWS Infrastructure MCP Server

A local FastMCP server (Python 3.12+, stdio transport) that exposes AWS diagnostic tools for serverless applications directly to IDE AI agents. It connects Cursor, VS Code Copilot, or Claude Code to CloudWatch, Lambda, DynamoDB, IAM, and Bedrock through the Model Context Protocol.

## Architecture & Design Choices

### Transport & Stack

| Component         | Choice                                |
| ----------------- | ------------------------------------- |
| Runtime           | Python 3.12+                          |
| MCP framework     | FastMCP 3.x                           |
| Package manager   | uv                                    |
| AWS SDK           | boto3                                 |
| Schema validation | Pydantic 2.x                          |
| Transport         | stdio (IDE spawns the server process) |

### Authentication

Uses boto3's default credential chain with no custom auth logic. The chain checks, in order: environment variables (`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`) → `~/.aws/credentials` → `~/.aws/config` → instance metadata. Set `AWS_PROFILE` to target a specific named profile.

### Tool Surface

15 tools across 4 modules:

**CloudWatch & Lambda — Service Diagnostics** (`tools/cloudwatch.py`, `tools/lambda_ops.py`)

| Tool                                            | Description                                                                    |
| ----------------------------------------------- | ------------------------------------------------------------------------------ |
| `list_lambdas(prefix?)`                         | List Lambda functions, optionally filtered by name prefix                      |
| `get_lambda_logs(function_name, minutes=15)`    | Pull recent CloudWatch log events for a Lambda's log group                     |
| `get_lambda_errors(function_name, minutes=60)`  | Filter log events to ERROR/exception/Traceback lines only                      |
| `get_lambda_metrics(function_name, period=300)` | Retrieve invocations, errors, duration, and throttle metrics                   |
| `invoke_lambda(function_name, payload)`         | Synchronous invocation (`RequestResponse`); response is truncated and scrubbed |

**DynamoDB — Data Health** (`tools/dynamodb.py`)

| Tool                                                   | Description                                                              |
| ------------------------------------------------------ | ------------------------------------------------------------------------ |
| `list_tables(prefix?)`                                 | List DynamoDB tables, optionally filtered by name prefix                 |
| `describe_table(table_name)`                           | Return key schema, attributes, GSIs, item count, size, status            |
| `query_table(table_name, key_condition, limit=10)`     | Query by primary key condition; hard-capped at 25 items                  |
| `scan_table(table_name, filter_expression?, limit=10)` | Scan with optional filter; hard-capped at 25 items                       |
| `get_table_metrics(table_name, period=300)`            | Query CloudWatch for ReadThrottleEvents, WriteThrottleEvents, UserErrors |

**IAM — Security Governance** (`tools/iam.py`)

| Tool                                  | Description                                                  |
| ------------------------------------- | ------------------------------------------------------------ |
| `list_roles(prefix?)`                 | List IAM roles, optionally filtered by name prefix           |
| `get_role_policy(role_name)`          | Return trust policy and all attached/inline policy documents |
| `list_lambda_roles()`                 | Map each Lambda function to its execution role ARN           |
| `validate_least_privilege(role_name)` | Flag three high-risk patterns (see below)                    |

`validate_least_privilege` checks:

1. **Wildcard permissions** — statements with `"Action": "*"` or `"Resource": "*"`
2. **Dangerous managed policies** — `AdministratorAccess` or `PowerUserAccess` attached
3. **Unscoped data access** — S3 or DynamoDB read/write actions without specific resource ARNs

**Bedrock — AI Root-Cause Analysis** (`tools/bedrock.py`)

| Tool                                                           | Description                                                     |
| -------------------------------------------------------------- | --------------------------------------------------------------- |
| `analyze_incident(context, model_id="amazon.nova-micro-v1:0")` | Send diagnostic context to Bedrock; returns structured analysis |

Accepts raw text (logs, stack traces, metric summaries). Returns a validated Pydantic object:

```python
class IncidentAnalysis(BaseModel):
    severity: Literal["LOW", "MEDIUM", "HIGH", "CRITICAL"]
    root_cause_summary: str
    affected_components: list[str]
    recommended_fix: str
```

The `model_id` parameter accepts any Bedrock-available model (for example: `meta.llama3-2-3b`).

### Security Boundary

- All tools are **read-only** except `invoke_lambda` (synchronous invocation only).
- No tool creates, updates, or deletes Lambda functions, DynamoDB items, IAM policies, or any other AWS resource.
- A global credential scrubber (`utils/scrubber.py`) redacts AWS access key IDs, secret access keys, and session tokens from all tool outputs before data reaches the LLM context. Applied to log messages, invocation responses, and error outputs.

## Project Structure

```
mcpserver/
├── app.py                 # FastMCP instance (single shared object)
├── server.py              # Entry point — imports tool modules, runs stdio
├── tools/
│   ├── __init__.py
│   ├── cloudwatch.py      # get_lambda_logs, get_lambda_errors, get_lambda_metrics
│   ├── lambda_ops.py      # list_lambdas, invoke_lambda
│   ├── dynamodb.py        # list_tables, describe_table, query_table, scan_table, get_table_metrics
│   ├── iam.py             # list_roles, get_role_policy, validate_least_privilege, list_lambda_roles
│   └── bedrock.py         # analyze_incident + IncidentAnalysis schema
├── utils/
│   ├── __init__.py
│   └── scrubber.py        # Credential redaction regex patterns
├── tests/
│   ├── test_scrubber.py
│   ├── test_cloudwatch.py
│   ├── test_lambda_ops.py
│   ├── test_dynamodb.py
│   ├── test_iam.py
│   └── test_bedrock.py
├── pyproject.toml
└── uv.lock
```

## Installation & Local Setup

**Prerequisites:**

- Python 3.12+
- [uv](https://docs.astral.sh/uv/) package manager
- AWS CLI configured (`~/.aws/credentials` or environment variables)

```bash
# Clone and install dependencies
cd mcpserver
uv sync

# Run the server locally (stdio)
uv run python server.py
```

## IDE Configuration

### VS Code / Cursor

Create `.vscode/mcp.json` in the project root:

```json
{
  "servers": {
    "aws-infra-mcp": {
      "type": "stdio",
      "command": "uv",
      "args": ["--directory", "/absolute/path/to/mcpserver", "run", "server.py"]
    }
  }
}
```

Replace `/absolute/path/to/mcpserver` with the actual path to this directory. On Windows, use forward slashes (`C:/Users/.../mcpserver`).

### Claude Desktop

Add to `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "aws-infra-mcp": {
      "command": "uv",
      "args": ["--directory", "/absolute/path/to/mcpserver", "run", "server.py"]
    }
  }
}
```

## Testing Strategy

Tests use a single-seam design: each `@mcp.tool()` function is tested at its public boundary with boto3 mocked beneath it using `unittest.mock.patch`. Tests verify the tool's transformation logic — log filtering, hard cap enforcement, credential scrubbing, Pydantic validation — without requiring live AWS credentials or making network calls.

57 unit tests across 6 test files:

```bash
# Run the full suite
uv run pytest

# Run a single cluster's tests
uv run pytest tests/test_iam.py -v
```