Skip to main content
Glama
rafaself

AWS MCP Gateway

by rafaself
README.md
<p align="center">
  <img src="docs/assets/aws-mcp-gateway-logo.png" alt="AWS MCP Gateway" width="200" />
</p>

# AWS MCP Gateway

AWS MCP Gateway is a security-focused [Model Context Protocol](https://modelcontextprotocol.io/) server that lets ChatGPT read selected AWS account data through explicit, read-only tools.

It runs as a Cloudflare Worker, authenticates requests, validates every tool input, signs allowed AWS API calls, and returns normalized results without exposing generic AWS API access.

## What is this?

This project is a self-hosted MCP gateway for connecting ChatGPT to AWS account data in a controlled way.

Instead of giving ChatGPT broad AWS credentials, shell access, or a generic AWS API proxy, the gateway exposes a small set of audited MCP tools. Each tool has a fixed purpose, validated input, bounded output, and read-only AWS permissions.

Every public tool is **manifest-backed**: a `ToolManifest` in `src/mcp/tools/definitions/` is the source of truth for registration, ChatGPT descriptors, AWS capability metadata, and cost-control limits. A central **policy gate** runs before handler execution and fails closed when a tool pack is disabled, cost-control metadata is missing, or request limits are exceeded.

```text
ChatGPT Connector
  -> OAuth / bearer authentication
  -> Cloudflare Worker /mcp endpoint
  -> Manifest-backed tool registry
  -> Policy gate (packs, cost-control, capabilities)
  -> Typed read-only handlers
  -> Signed read-only AWS API requests
  -> Normalized AWS cost, inventory, alarm, and log data
```

The registry defines **38** public tools. Default deployments expose **21** through tool packs (`core`, `cost`, `inventory`, `observability`, `database`). Three aggregate overview tools are opt-in via the `aggregates` pack. Five security tools (SSM inventory, S3 posture, SES configuration, SNS topic status, EventBridge/Scheduler status) are opt-in via the `security` pack. Nine application profile tools are opt-in via the `application-ops` pack. See [tool exposure](#tool-exposure-optional) and [`docs/aws-capability-matrix.md`](docs/aws-capability-matrix.md).

## Current status

The gateway is currently designed for:

- remote MCP usage over HTTPS;
- ChatGPT custom app connector integration;
- OAuth-based ChatGPT connector authentication;
- local bearer mode development;
- read-only AWS cost, EC2, Lambda, S3, CloudWatch, and CloudWatch Logs inspection.

Production deployments should still run the verification and acceptance checks documented in [`docs/chatgpt-connector-production-acceptance.md`](docs/chatgpt-connector-production-acceptance.md).

## Features

- Remote MCP endpoint at `/mcp`.
- ChatGPT-compatible OAuth connector flow.
- Explicit read-only AWS tools only.
- No generic AWS CLI or arbitrary AWS API proxy.
- Least-privilege IAM policy template.
- Region allowlist and input validation.
- Cloudflare KV caching for AWS-backed tool responses.
- OAuth request rate limiting with a Durable Object.
- Offline unit tests with a fetch guard against accidental network calls.
- Contract checks for MCP tool discovery and ChatGPT connector compatibility.

## Available MCP tools

The registry defines **38** public read-only tools. Default deployments expose **21** via tool packs. Opt-in packs add security posture tools (`security`), aggregate overviews (`aggregates`), and KV-backed application profile workflows (`application-ops`).

See [tool exposure](#tool-exposure-optional) for pack mappings and configuration examples. Full per-tool contracts: [`docs/mcp-tools.md`](docs/mcp-tools.md). Platform architecture: [`docs/specs/secure-tool-platform.md`](docs/specs/secure-tool-platform.md).

## When to use it

Use this gateway when you want ChatGPT to answer questions such as:

- “How much did my AWS account spend this month?”
- “Which services are driving my AWS bill?”
- “What EC2 instances are running in my allowed regions?”
- “Are there any CloudWatch alarms in ALARM state?”
- “What Lambda functions are deployed in my allowed regions?”
- “What S3 buckets exist in my account?”
- “Which CloudWatch log groups are defined in us-east-1?”

The project is useful for personal AWS account inspection, lightweight cloud operations, cost visibility, and controlled ChatGPT-based observability workflows.

## When not to use it

Do not use this project as-is if you need:

- AWS write or management operations;
- provisioning, remediation, or infrastructure mutation;
- arbitrary AWS API access;
- a generic AWS CLI over MCP;
- multi-tenant SaaS isolation;
- a dashboard, database, or long-running backend service.

Management tools may be added later only behind stricter security boundaries. See [`docs/post-mvp-boundaries.md`](docs/post-mvp-boundaries.md).

## Requirements

- Node.js `>=22`
- `pnpm` `11.8.0`
- Cloudflare account with Workers enabled
- AWS account with a dedicated read-only IAM user
- Wrangler authentication or a scoped Cloudflare API token
- Auth0 or another OIDC-compatible provider for production ChatGPT OAuth setup

## Quick start: local development

Install dependencies:

```bash
pnpm install
```

Create local runtime secrets:

```bash
cp .dev.vars.example .dev.vars
```

Edit `.dev.vars` and fill:

```text
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_REGION=us-east-1
AWS_ALLOWED_REGIONS=us-east-1,sa-east-1
AUTH_MODE=local-bearer
MCP_AUTH_TOKEN=
```

**Minimal local loop** (fast iteration during development):

```bash
pnpm run typecheck
pnpm test
pnpm run test:integrity
```

**Full pre-PR / pre-deploy validation** (same gate as [`docs/deployment.md`](docs/deployment.md)):

```bash
pnpm run repo:safety
pnpm run output:guardrail
pnpm run verify:connector-contract
pnpm run typecheck
pnpm test
pnpm run test:integrity
```

`verify:connector-contract` runs typecheck, unit tests, and test-integrity checks; the last three commands are listed explicitly to match CI and deployment docs. Gitleaks secret scanning runs separately on every PR via [`.github/workflows/secret-scan.yml`](.github/workflows/secret-scan.yml).

Start the local Worker:

```bash
pnpm dev
```

The local MCP endpoint is available at:

```text
http://localhost:8787/mcp
```

Local development uses `AUTH_MODE=local-bearer` by default. Production ChatGPT connector deployments should use OAuth.

For the implementation-aligned authentication model, see
[`docs/auth/README.md`](docs/auth/README.md).

## Configuration

Both [`wrangler.jsonc`](wrangler.jsonc) and [`wrangler.example.jsonc`](wrangler.example.jsonc) are tracked and must stay structurally in sync with **placeholders only**. Real deployment values (worker host, Auth0 tenant, KV namespace ids) belong in gitignored `wrangler.deploy.jsonc` — copy from [`wrangler.deploy.example.jsonc`](wrangler.deploy.example.jsonc). `pnpm run repo:safety` enforces structural parity and blocks live values in tracked Wrangler files.

On a fresh clone:

```bash
cp wrangler.example.jsonc wrangler.jsonc
cp wrangler.deploy.example.jsonc wrangler.deploy.jsonc
```

Fill `wrangler.deploy.jsonc` with at least:

- `AWS_REGION`
- `AWS_ALLOWED_REGIONS`
- `AUTH_MODE`
- `MCP_RESOURCE_URL`
- `OAUTH_ISSUER`
- `OAUTH_AUDIENCE`
- `OAUTH_JWKS_URI`
- `OAUTH_REQUIRED_SCOPES`
- `kv_namespaces[].id`

Important URL model:

```text
ChatGPT Connector Server URL: https://<worker-host>/mcp
MCP_RESOURCE_URL:              https://<worker-host>
OAUTH_AUDIENCE:                https://<worker-host>
OAuth protected metadata:      https://<worker-host>/.well-known/oauth-protected-resource
```

`MCP_RESOURCE_URL` and `OAUTH_AUDIENCE` must use the Worker origin only. Do not append `/mcp` to those values.

Authentication lifecycle and route responsibilities are documented in
[`docs/auth/README.md`](docs/auth/README.md),
[`docs/auth/oauth-lifecycle.md`](docs/auth/oauth-lifecycle.md), and
[`docs/auth/token-validation.md`](docs/auth/token-validation.md).

### Tool exposure (optional)

Self-hosted deployments can limit which MCP tools are exposed without changing source code. Prefer enabling fewer tools for least privilege.

| Variable | Default | Purpose |
|----------|---------|---------|
| `AWS_MCP_ENABLED_TOOL_PACKS` | `core,cost,inventory,observability,database` | Comma-separated packs to expose |
| `AWS_MCP_ENABLED_TOOLS` | *(empty — all tools in enabled packs)* | Optional allowlist of tool names |
| `AWS_MCP_DISABLED_TOOLS` | *(empty)* | Tool names to hide and deny |
| `AWS_MCP_MAX_RISK_LEVEL` | `read-only` | Maximum allowed tool risk level |

Tool packs:

```text
core:           search, fetch, get_gateway_status
cost:           get_aws_cost_summary, get_aws_cost_by_service, get_budget_status
inventory:      list_ec2_instances, list_lambda_functions, list_s3_buckets, get_ecr_image_status, compare_ecs_task_image_with_ecr
observability:  get_cloudwatch_alarms, get_cloudwatch_logs, get_cloudwatch_alarm_summary, get_recent_log_errors, list_log_groups, get_ecs_service_health, list_ecs_tasks, get_recent_stopped_ecs_tasks
database:       get_rds_instance_health, get_rds_metrics
aggregates:     aws_account_overview, aws_cost_overview, aws_observability_overview (opt-in)
security:       check_ssm_parameter_inventory, get_s3_bucket_posture, get_ses_configuration_status, get_sns_topic_status, get_eventbridge_rules_status (opt-in)
application-ops: list_application_profiles, get_application_environment_overview, get_application_compute_status, get_application_database_status, get_application_logs, get_application_secret_inventory, get_application_artifact_status, get_application_alerting_status, get_application_cost_status (opt-in)
```

The `aggregates` pack is opt-in. Enable it when you want bounded overview tools that compose existing inventory, cost, observability, and database capabilities. The `security` pack is opt-in for SSM parameter inventory, S3 posture, SES configuration metadata, SNS topic status, and EventBridge/Scheduler status. The `application-ops` pack is opt-in for KV-backed application profile discovery and profile-driven operational summaries. Default deployments expose 21 MCP tools; enabling `aggregates` adds three more; enabling `security` adds five more; enabling `application-ops` adds nine more.

Exposure rules:

1. The tool's pack must be enabled.
2. The tool must not appear in `AWS_MCP_DISABLED_TOOLS`.
3. When `AWS_MCP_ENABLED_TOOLS` is set, only listed tools are exposed (within enabled packs).
4. The tool's risk level must match `AWS_MCP_MAX_RISK_LEVEL`.

Disabled tools are omitted from `tools/list` and fail safely if called directly. Unknown pack or tool names fail configuration validation.

Example — cost tools only (no core helpers):

```text
AWS_MCP_ENABLED_TOOL_PACKS=cost
```

Example — cost tools plus ChatGPT catalog helpers:

```text
AWS_MCP_ENABLED_TOOL_PACKS=core,cost
```

Example — enable aggregate overview tools:

```text
AWS_MCP_ENABLED_TOOL_PACKS=core,cost,inventory,observability,database,aggregates
```

Example — default packs (explicit; same as omitting the variable):

```text
AWS_MCP_ENABLED_TOOL_PACKS=core,cost,inventory,observability,database
```

Example — full read-only operational mode (all generic packs):

```text
AWS_MCP_ENABLED_TOOL_PACKS=core,cost,inventory,observability,database,security,aggregates,application-ops
```

Example — application profile mode:

```text
AWS_MCP_ENABLED_TOOL_PACKS=core,cost,inventory,observability,database,security,application-ops
```

## AWS IAM setup

Use a dedicated IAM user with only the permissions required by the gateway.

The canonical read-only policy is maintained at [`infra/aws/iam-readonly-policy.json`](infra/aws/iam-readonly-policy.json).

See [`docs/aws-iam-setup.md`](docs/aws-iam-setup.md) for the complete IAM setup flow. For multi-account access with STS `AssumeRole`, see [`docs/aws-credentials.md`](docs/aws-credentials.md) and [`docs/iam-cross-account.md`](docs/iam-cross-account.md).

Do not use `AdministratorAccess` or broad AWS-managed policies for this gateway.

## Optional KV cache

Cloudflare KV can cache normalized AWS tool responses to reduce repeated AWS API calls and Cost Explorer usage.

Create the namespace:

```bash
wrangler kv:namespace create "AWS_MCP_CACHE"
```

Then copy the returned namespace id into `wrangler.deploy.jsonc`.

Default cache TTLs:

| Data | TTL |
| --- | --- |
| AWS cost summary | 30 minutes |
| AWS cost by service | 30 minutes |
| EC2 inventory | 5 minutes |
| Lambda functions | 5 minutes |
| S3 buckets | 5 minutes |
| CloudWatch alarms | 5 minutes |
| Log groups | 5 minutes |
| Recent log events | 5 minutes |

The cache is optional for local development and tests. If the binding is absent, tools run without caching.

**Application profiles** use a separate optional KV binding (`AWS_MCP_APP_CONFIG`) for saved operational context. Application profiles are optional — do not configure `AWS_MCP_APP_CONFIG` unless you use the application-ops profile workflow. Profiles are not required for `/mcp` or generic AWS tools. See [`docs/application-profiles.md`](docs/application-profiles.md) for schema details, [`docs/aws-tools.md`](docs/aws-tools.md) for direct generic tools, and [`docs/application-profiles.md#cli-workflow`](docs/application-profiles.md#cli-workflow) for `pnpm app-profile:*` commands.

**Cost Explorer billing estimates:** Non-cached `ce:GetCostAndUsage` requests are estimated at approximately **US$ 0.01** per live API call. Cached responses report `estimatedCostUsd: 0` and do not make a new Cost Explorer request. These values are approximate gateway estimates only — final AWS billing is determined by your AWS account usage and pricing.

Successful AWS-backed tool responses also expose cache status, AWS request counts, and conservative billing metadata at `structuredContent.execution`. See [`docs/mcp-tools.md#execution-metadata`](docs/mcp-tools.md#execution-metadata).

## Deploy to Cloudflare Workers

Prepare deploy-time credentials:

```bash
cp .env.deploy.example .env.deploy.local
```

Fill the required values in `.env.deploy.local` and deployment values in `wrangler.deploy.jsonc`, then deploy:

```bash
pnpm run deploy:configured
```

Or deploy code after syncing Worker secrets:

```bash
pnpm run sync-secrets
pnpm deploy
```

`pnpm deploy` uses `wrangler.deploy.jsonc` (not tracked `wrangler.jsonc`). For OAuth production mode, configure OAuth values in `wrangler.deploy.jsonc` `[vars]` and use Worker secrets only for credentials and private client secrets.

See [`docs/deployment.md`](docs/deployment.md) for the full deployment guide.

## Connect to ChatGPT

This gateway is designed for a ChatGPT custom app connector.

In ChatGPT connector setup, use:

```text
Server URL: https://<worker-host>/mcp
Authentication: OAuth
Scope: aws:read
```

The Worker OAuth resource and audience must be the origin only:

```text
https://<worker-host>
```

After deploying, configure deployment targets (`AWS_MCP_GATEWAY_WORKER_URL`, `AWS_MCP_GATEWAY_AUTH0_DOMAIN` in `.env.deploy.local` or as script arguments), then run:

```bash
pnpm run verify:connector-contract
source .env.deploy.local && pnpm run verify:oauth
pnpm run verify:oauth:authenticated
```

Then create or refresh the ChatGPT connector. The Actions list should expose all **enabled** MCP tools from `tools/list` (18 by default; 21 when the `aggregates` pack is enabled). Disabled or pack-gated tools do not appear as Actions.

Detailed setup and troubleshooting:

- [`docs/chatgpt-connector.md`](docs/chatgpt-connector.md)
- [`docs/auth-chatgpt-oauth.md`](docs/auth-chatgpt-oauth.md)
- [`docs/chatgpt-connector-smoke-test.md`](docs/chatgpt-connector-smoke-test.md)
- [`docs/chatgpt-connector-production-acceptance.md`](docs/chatgpt-connector-production-acceptance.md)

## Security model

The gateway is intentionally read-only.

Required controls:

- MCP requests must be authenticated.
- AWS credentials must be stored outside Git as Cloudflare secrets.
- IAM permissions must be least-privilege and read-only.
- Tools must be explicit and allowlisted.
- Tool inputs must enforce date, region, and result-size limits.
- AWS responses must be normalized before returning to the client.
- Logs and errors must not expose secrets, AWS access keys, bearer tokens, OAuth tokens, or raw stack traces.

Forbidden in the current scope:

- no `run_aws_cli` tool;
- no `call_any_aws_api` or generic AWS API proxy;
- no AWS write or management permissions;
- no raw AWS API responses returned to MCP clients;
- no committed `.env`, `.dev.vars`, `.env.deploy.local`, `wrangler.deploy.jsonc`, `.wrangler/`, or real credentials.

For the full security checklist, see [`SECURITY.md`](SECURITY.md).

### AWS operations model

- **Generic tools** — pass resource names directly (`clusterName`, `logGroupName`, `dbInstanceIdentifier`, …); no profiles required. See [`docs/aws-tools.md`](docs/aws-tools.md).
- **Application profiles** — optional saved operational context in KV for the `application-ops` pack. See [`docs/application-profiles.md`](docs/application-profiles.md).
- **Cross-account IAM** — one gateway user plus `sts:AssumeRole` into read-only target roles. See [`docs/iam-cross-account.md`](docs/iam-cross-account.md).

## Testing

**Minimal local loop:**

```bash
pnpm run typecheck
pnpm test
pnpm run test:integrity
```

**Full pre-PR / pre-deploy validation:**

```bash
pnpm run repo:safety
pnpm run output:guardrail
pnpm run verify:connector-contract
pnpm run typecheck
pnpm test
pnpm run test:integrity
```

- `pnpm run repo:safety` — tracked files stay public-safe (no local env files, secret-like values, or maintainer deployment defaults in Git).
- `pnpm run output:guardrail` — production source routes runtime output through `src/observability/` and does not call `console.*` elsewhere.
- `pnpm run verify:connector-contract` — local ChatGPT Connector contract gate (manifest, policy, capability, exposure, descriptors, `tools/list`).

Tests are offline by default. A global fetch guard fails any unmocked network request during unit tests.

CI runs `repo:safety`, `output:guardrail`, and `verify:connector-contract` in [`.github/workflows/ci.yml`](.github/workflows/ci.yml). Gitleaks secret scanning runs in [`.github/workflows/secret-scan.yml`](.github/workflows/secret-scan.yml).

Runtime MCP/auth dependency upgrades must be treated as protocol changes. See [`docs/dependency-upgrade-contract.md`](docs/dependency-upgrade-contract.md).

## Documentation

| Document | Purpose |
| --- | --- |
| [`docs/mcp-tools.md`](docs/mcp-tools.md) | Public MCP tool contracts |
| [`docs/aws-tools.md`](docs/aws-tools.md) | Generic direct-input AWS tools (no profiles required) |
| [`docs/application-profiles.md`](docs/application-profiles.md) | Optional KV-backed application profiles |
| [`docs/iam-cross-account.md`](docs/iam-cross-account.md) | Multi-account AssumeRole IAM pattern |
| [`docs/chatgpt-connector.md`](docs/chatgpt-connector.md) | ChatGPT connector integration guide |
| [`docs/auth/README.md`](docs/auth/README.md) | Authentication lifecycle, route surface, and token validation map |
| [`docs/auth-chatgpt-oauth.md`](docs/auth-chatgpt-oauth.md) | OAuth/Auth0 setup |
| [`docs/chatgpt-connector-production-acceptance.md`](docs/chatgpt-connector-production-acceptance.md) | Production acceptance gate |
| [`docs/chatgpt-connector-smoke-test.md`](docs/chatgpt-connector-smoke-test.md) | Detailed connector smoke runbook |
| [`docs/deployment.md`](docs/deployment.md) | Cloudflare deployment guide |
| [`docs/aws-iam-setup.md`](docs/aws-iam-setup.md) | AWS IAM setup |
| [`docs/aws-credentials.md`](docs/aws-credentials.md) | Default credentials and STS AssumeRole model |
| [`docs/aws-capability-matrix.md`](docs/aws-capability-matrix.md) | Tool-to-IAM capability matrix |
| [`docs/mcp-testing.md`](docs/mcp-testing.md) | Manual MCP smoke tests |
| [`docs/post-mvp-boundaries.md`](docs/post-mvp-boundaries.md) | Future write/management safety boundaries |
| [`docs/dependency-upgrade-contract.md`](docs/dependency-upgrade-contract.md) | MCP/auth dependency upgrade contract |
| [`docs/tooling-conventions.md`](docs/tooling-conventions.md) | Contributor tool conventions |
| [`docs/specs/README.md`](docs/specs/README.md) | When implementation specs are required |
| [`SECURITY.md`](SECURITY.md) | Security checklist and public-safe repository rules |

## Repository safety

Safe to commit:

- source code;
- tests;
- documentation;
- tool schemas;
- example IAM policies;
- example environment files;
- Wrangler configuration without secrets.

Never commit:

- AWS access keys;
- Cloudflare API tokens;
- OAuth client secrets;
- bearer tokens;
- `.env`;
- `.dev.vars`;
- `.env.deploy.local`;
- `wrangler.deploy.jsonc`;
- `.wrangler/`.

## Contributing

Before opening a pull request, run the full pre-PR validation block from [Testing](#testing) (all six commands). When changing MCP descriptors, tool manifests, or connector discovery, `pnpm run verify:connector-contract` is required.

Use conventional commits:

```text
type(scope): message
```

Examples:

```text
docs(readme): improve setup guide
feat(mcp): add read-only budget status tool
fix(auth): reject tokens without required scope
security(aws): tighten IAM policy actions
```

Pull requests that change public tool behavior should update [`docs/mcp-tools.md`](docs/mcp-tools.md) and include focused contract tests.

## License

MIT. See [`LICENSE`](LICENSE).

## Disclaimer

This project is not affiliated with AWS, Cloudflare, OpenAI, or Auth0. Use it with dedicated credentials, least-privilege IAM permissions, and your own security review before production use.