cloud-audit-mcp
by Avinash9694
README.md
# cloud-audit-mcp
Read-only AWS security-posture checks as [Model Context Protocol](https://modelcontextprotocol.io) tools for Claude Desktop, Claude Code and Cursor — with an offline fixture mode that needs no credentials.
**Docs and live demo:** https://avinash9694.github.io/cloud-audit-mcp/
[](https://avinash9694.github.io/cloud-audit-mcp/)
## Install
The server is installed straight from GitHub through `npx`; nothing to clone or publish. It starts in **fixture mode**, serving a bundled sample estate so every tool works before you hand it any credentials.
**Claude Desktop** — `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
```json
{
"mcpServers": {
"cloud-audit": {
"command": "npx",
"args": ["-y", "github:Avinash9694/cloud-audit-mcp"],
"env": { "CLOUD_AUDIT_MODE": "fixture" }
}
}
}
```
**Claude Code:**
```sh
claude mcp add cloud-audit -e CLOUD_AUDIT_MODE=fixture -- npx -y github:Avinash9694/cloud-audit-mcp
```
**Cursor** — `~/.cursor/mcp.json` or `.cursor/mcp.json` in a project: same JSON as Claude Desktop.
### Pointing it at a real account
Set `CLOUD_AUDIT_MODE=aws` in the `env` block. Credentials come from the standard AWS chain (`AWS_PROFILE` including SSO profiles, `AWS_ACCESS_KEY_ID`/`AWS_SECRET_ACCESS_KEY`, instance or container roles); `AWS_REGION` picks the region for regional services. The AWS-managed **SecurityAudit** policy covers every call. If credentials are missing or expired, tools return an error that says how to fix it — the server never crashes.
| Variable | Default | Purpose |
| ----------------------- | -------------- | ------------------------------------------------------- |
| `CLOUD_AUDIT_MODE` | `fixture` | `fixture` serves JSON; `aws` queries the account. |
| `CLOUD_AUDIT_FIXTURE` | bundled estate | Path to an alternative estate JSON in fixture mode. |
| `CLOUD_AUDIT_LOG_LEVEL` | `info` | `error`, `warn`, `info` or `debug`. Logs go to stderr. |
| `AWS_REGION` | `us-east-1` | Region for EC2, RDS and CloudTrail calls in `aws` mode. |
Then ask: _"Run every check and summarise the posture"_, _"Which security groups let the internet reach SSH?"_, or use the bundled `security_review` prompt for a full prioritised report.
## Why I built this
My day job is a Tauri + React + Rust desktop app that scans AWS and Azure estates, runs compliance checks and hands the results to an AI assistant. The most useful part of that loop is letting the model _ask_ for the next piece of evidence rather than reading a 400-row report. This project distils that into the smallest honest version: a provider interface, a set of checks that are each a tested module, and an MCP surface that is read-only by construction rather than by promise.
## Features
- **Six tools**: `whoami`, `list_checks`, `run_check`, `run_all_checks` (with a severity floor), `describe_resource`, `summarize_posture` (counts, top five risks, a 0–100 score).
- **Two resources** (`audit://checks`, `audit://fixtures/estate`) and one **prompt** (`security_review`) that walks the model through a prioritised review.
- **Fourteen checks** across S3, EC2, RDS, IAM and CloudTrail, each with severity, description, remediation and tests against both a misconfigured and a hardened fixture.
- **Read-only, enforced**: the AWS provider imports only `List*`, `Describe*` and `Get*` commands, and a unit test fails if any other command name appears in that module. Every tool is annotated `readOnlyHint: true`.
- **Fixture mode** for demos, tests and CI: no network, no credentials.
- **Structured output**: every tool returns a concise markdown summary for the model and a JSON object validated against a Zod output schema.
- **Graceful failure**: missing credentials, denied permissions and bad fixture files become tool errors with hints; logging is stderr-only so the stdio protocol stays clean.
- **Docs site** that imports the server's own check catalog and tool manifest, so it cannot drift from the code, plus a terminal demo that runs the real checks in the browser.
## Checks
| Id | Severity | Service | What it flags |
| ------------------------------------ | -------- | ---------- | --------------------------------------------------------------------------------- |
| `s3-public-bucket` | critical | S3 | Public ACL grants, public bucket policy, or a missing/partial Public Access Block |
| `sg-admin-ports-open-to-world` | critical | EC2 | Ingress from `0.0.0.0/0` or `::/0` reaching port 22, 3389 or every port |
| `rds-publicly-accessible` | critical | RDS | Instances with `PubliclyAccessible` enabled |
| `root-access-key-present` | critical | IAM | Root user has programmatic access keys |
| `s3-no-default-encryption` | high | S3 | No default server-side encryption rule |
| `ebs-unencrypted` | high | EC2 | Volumes created without encryption |
| `rds-unencrypted` | high | RDS | Storage, backups and snapshots unencrypted at rest |
| `iam-user-no-mfa` | high | IAM | Console users without an MFA device |
| `iam-inline-admin-policy` | high | IAM | Inline policy allowing `*` on `*` |
| `cloudtrail-not-multi-region` | high | CloudTrail | No trail, no logging trail, or no multi-region trail |
| `sg-open-to-world` | medium | EC2 | Internet ingress on any non-admin port |
| `iam-access-key-stale` | medium | IAM | Active access keys older than 90 days |
| `cloudtrail-log-validation-disabled` | medium | CloudTrail | Trails without log file validation |
| `s3-no-versioning` | low | S3 | Versioning disabled or suspended |
Against the bundled sample estate `run_all_checks` reports 17 findings (6 critical, 6 high, 3 medium, 2 low) and `summarize_posture` scores it 42/100 (grade D).
## Architecture
```
MCP client ──stdio/JSON-RPC──▶ cloud-audit-mcp
(Claude Desktop, Claude Code, ├─ tools · resources · prompt (src/server.ts, src/tools/)
Cursor) ├─ 14 checks (src/checks/*.ts)
└─ AuditProvider interface (src/providers/audit-provider.ts)
├─ AwsSdkProvider — SDK v3, List/Describe/Get only
└─ FixtureProvider — fixtures/estate.json
```
- Checks only ever see `AuditProvider`; swapping providers is one environment variable.
- `withCache` memoises each inventory call for 60 s so `run_all_checks` lists a service once, not once per check.
- `defineCheck` binds id, severity, title and remediation to every finding so a check module only describes what it saw.
- The posture score starts at 100 and deducts 10/5/2/1 per critical/high/medium/low finding, with repeats of the same severity at half weight.
- `docs-site/` depends on the server package (`file:..`) and renders `CHECK_CATALOG` and `TOOL_MANIFEST` directly; a test pins the manifest to the real Zod schemas.
## Tech stack
TypeScript (strict, ESM) · `@modelcontextprotocol/sdk` · Zod · AWS SDK v3 (S3, EC2, IAM, RDS, CloudTrail, STS) · Vitest · ESLint (typescript-eslint, type-checked) · Prettier · GitHub Actions. Docs site: Vite · React 18 · CSS modules · Testing Library.
## Running locally
```sh
npm install # also builds dist/ through the prepare script
npm run dev # start the server over stdio with tsx (fixture mode)
npm run build # compile to dist/
npm run smoke # spawn dist/cli.js over stdio, list tools, run every check
cd docs-site
npm install
npm run dev # docs site on http://localhost:5173/cloud-audit-mcp/
```
Try the server interactively with the MCP Inspector:
```sh
npx @modelcontextprotocol/inspector node dist/cli.js
```
## Testing
```sh
npm run lint && npm run typecheck && npm test && npm run smoke # server
cd docs-site && npm run lint && npm run typecheck && npm test # docs
```
The server suite (99 tests) runs every check against both fixtures, exercises each tool through the SDK's in-memory transport, checks provider selection and AWS response normalisation with stubbed clients, and pins the read-only import rule. The docs suite (10 tests) covers the catalog table, install tabs, copy buttons, theme toggle and the in-browser demo.
## Project structure
```
src/
cli.ts stdio entry point (bin: cloud-audit-mcp)
server.ts McpServer wiring: tools, resources, prompt, error mapping
manifest.ts tool / resource / prompt docs shared with the site
posture.ts scoring, ordering, severity filters
checks/ one module per check + index/catalog
providers/ AuditProvider, AwsSdkProvider, FixtureProvider, cache, selection
resources/ describe-resource lookup
tools/ zod schemas, handlers, markdown formatting
__tests__/ vitest suites
fixtures/ estate.json (misconfigured) and clean-estate.json (hardened)
scripts/smoke.mjs end-to-end stdio smoke test
docs-site/ Vite + React docs, deployed to GitHub Pages
.github/workflows/ deploy.yml (gates + Pages), ci.yml (Node 20 and 22)
```
## Roadmap and known limitations
- Azure and GCP providers behind the same `AuditProvider` interface.
- More checks: IAM password policy, S3 access logging, unused security groups, RDS backup retention.
- `aws` mode queries one region per process; multi-region sweeps mean running it once per `AWS_REGION`.
- IAM access-key age uses creation date, not last rotation, which is what IAM exposes.
- The posture score is a triage aid, not a compliance number.
## License
[MIT](LICENSE)
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues