Skip to main content
Glama
farmountain

aaws - AI-Assisted AWS CLI

aaws - AI-Assisted AWS CLI

PyPI version Python 3.11+ License: Apache 2.0

Natural language in, AWS command out.

pip install aaws -- pypi.org/project/aaws

Stop context-switching to AWS docs. Describe what you want in plain English and aaws generates, explains, and safely executes the correct AWS CLI command.

$ aaws "list my S3 buckets"

Command: aws s3api list-buckets --output json
Lists all S3 buckets in your account.

 Name                 CreationDate
 my-app-assets        2024-03-15T10:22:00+00:00
 my-logs-bucket       2024-06-01T08:00:00+00:00
 staging-uploads      2025-01-10T14:30:00+00:00

3 result(s)

Table of Contents


How It Works

Every aaws invocation follows a six-stage pipeline. Some stages are user-initiated (you trigger them), and others are agentic (the system handles them autonomously). This separation is the core design principle.

 YOU                              aaws (AGENT)
 ───                              ────────────
 Type natural language ──────────> LLM translates to AWS CLI command
                                   Agent classifies risk tier (0-3)
                                   Agent selects safety gate
 Review command + confirm ───────> Agent executes via subprocess
                                   Agent detects output shape
                                   Agent formats as table/card/JSON
 Read formatted results <────────
                                   (on error) Agent classifies error
                                   (on error) Agent generates recovery advice
 Read error + suggestion <───────

Using with Claude Code (MCP)

Use aaws with your Claude Code subscription — no API keys, no LLM config, zero extra cost.

Instead of aaws calling an LLM directly, Claude Code becomes the LLM. The aaws MCP server provides safety classification, command execution, and output formatting as tools that Claude Code calls.

 Standalone CLI:                     MCP Mode:
 User -> aaws -> LLM (you pay)      User -> Claude Code (subscription) -> aaws MCP tools
                -> AWS CLI                                               -> AWS CLI

Setup

Prerequisite: AWS CLI v2 must be installed and configured (see Installation above).

# Install with MCP support
pip install aaws[mcp]

# Register with Claude Code (one-time)
claude mcp add --scope user aaws -- python -m aaws.mcp_server

Or add a project-scoped .mcp.json (version-controlled, shared with team):

{
  "mcpServers": {
    "aaws": {
      "command": "python",
      "args": ["-m", "aaws.mcp_server"]
    }
  }
}

Verify with /mcp inside Claude Code to see the tools listed.

Available MCP Tools

Tool

Purpose

LLM Needed?

classify_aws_command

Risk tier classification (0-3) for any AWS CLI command

No (static table)

execute_aws_command

Safe subprocess execution with profile/region injection

No

format_aws_output

JSON shape detection -> plain-text tables/cards

No

list_safety_tiers

Browse known command risk tiers by service

No

check_aws_environment

Verify AWS CLI, active profile, region

No

Example Conversation in Claude Code

You: List my S3 buckets in us-west-2

Claude Code:
  1. Calls check_aws_environment() -> {aws_cli_available: true, active_profile: "default"}
  2. Calls classify_aws_command("aws s3api list-buckets --output json")
     -> {tier: 0, tier_label: "Read-only", should_confirm: false}
  3. Calls execute_aws_command("aws s3api list-buckets --output json", region="us-west-2")
     -> {stdout: '{"Buckets": [...]}', success: true}
  4. Calls format_aws_output(stdout)
     -> Formatted table with bucket names and dates

You: Now delete the one named old-logs

Claude Code:
  1. Calls classify_aws_command("aws s3 rb s3://old-logs --force")
     -> {tier: 2, tier_label: "Destructive", should_confirm: true}
  2. Asks: "This is a destructive operation (tier 2). Delete bucket old-logs?"
  3. You confirm
  4. Calls execute_aws_command(...)

What Changes vs Standalone CLI

Aspect

Standalone CLI

MCP Mode

LLM provider

You configure (Bedrock/OpenAI)

Claude Code subscription (free)

NL translation

aaws translator.py

Claude Code LLM

Session memory

In-process, 10-turn limit

Claude Code built-in (full context)

Multi-step workflows

One command at a time

Claude Code orchestrates multiple

Error interpretation

LLM call per error

Claude Code reasons over stderr

Configuration

aaws config init required

Just register MCP server

AWS Cloud Engineering Lifecycle (MCP)

Lifecycle Stage

Standalone CLI

+ MCP with Claude Code

Discovery

Query-based

+ Autonomous inventory, cross-service

Provisioning

Single-command

+ Multi-step with dependency ordering

Monitoring

Snapshot queries

+ Conversational drill-down

Troubleshooting

Hardcoded + LLM errors

+ Autonomous log/metric investigation

Maintenance

Manual delete/resize

+ Agent finds waste, suggests optimization

Security

CLI pass-through

+ Permission auditing

Disaster Recovery

Single-command backup

+ Orchestrated DR workflows


Value Stream: User-Initiated vs Agentic Actions

The following maps every activity in the development and usage lifecycle to who owns it: the user (manual, intentional) or the agent (autonomous, zero-touch).

Setup Phase

#

Activity

Owner

Description

1

Install aaws

User

pip install aaws

2

Run config wizard

User

aaws config init — choose provider, model, profile, region

3

Detect missing config

Agent

If no config exists, prints actionable message and exits

4

Validate config schema

Agent

Pydantic validates all fields, rejects bad values

5

Resolve ${ENV_VAR} in config

Agent

Substitutes environment variable references in YAML values

6

Apply AAWS_* env overrides

Agent

Environment variables override file-based config (CI/CD friendly)

7

Detect AWS CLI presence

Agent

Checks aws in PATH on startup; fails fast with install link

One-Shot Command Flow

#

Activity

Owner

Description

8

Write natural language request

User

aaws "show my running EC2 instances"

9

Resolve AWS profile + region

Agent

Merges --profile/--region flags > config > boto3 session > fallback

10

Build LLM prompt with context

Agent

Injects system prompt + profile/region context + user request

11

Call LLM via tool/function calling

Agent

Sends structured tool schema, forces tool use (no free-text)

12

Validate command starts with aws

Agent

Rejects hallucinated non-AWS output

13

Auto-retry on invalid command

Agent

Sends corrective instruction, retries once, then fails with clear error

14

Return clarification if ambiguous

Agent

If request is vague, asks ONE clarifying question instead of guessing

15

Classify risk tier (static table)

Agent

Longest-prefix match against 100+ known command patterns

16

Fallback to LLM-assigned tier

Agent

Unknown commands use the LLM's risk assessment

17

Check protected profile

Agent

Blocks all writes on prod-* or user-defined glob patterns

18

Show command + explanation

Agent

Displays the generated command with plain-English explanation

19

Confirm or cancel execution

User

Tier 0: auto-run. Tier 1: y/n. Tier 2: type "yes". Tier 3: refused. --yes auto-confirms 1-2

20

Offer --dry-run for EC2

Agent

For destructive EC2 commands, offers to validate with --dry-run first

21

Execute via subprocess

Agent

shlex.split() + subprocess.run(shell=False) — no injection possible

22

Detect output shape

Agent

Inspects JSON: list -> table, dict -> card, empty -> "No results."

23

Render formatted output

Agent

Rich tables with column hints per resource type, or syntax-highlighted JSON

24

Classify error on failure

Agent

Regex matches for credential, permission, resource errors

25

Provide hardcoded fix for auth

Agent

Expired token -> aws sso login. No creds -> aws configure

26

LLM-interpret resource errors

Agent

Sends failed command + stderr to LLM for plain-English recovery steps

Interactive Session Flow

#

Activity

Owner

Description

27

Start session

User

aaws session [--profile X] [--region Y]

28

Display session header

Agent

Shows active profile, region, exit instructions

29

Type follow-up requests

User

Conversational input referencing prior context

30

Maintain conversation history

Agent

Appends each exchange, bounded to last 10 for LLM context

31

Translate with history context

Agent

LLM sees prior conversation for multi-turn refinement

32

Full safety pipeline per turn

Agent

Every command goes through classify -> gate -> execute -> format

33

Exit session

User

Type exit/quit or Ctrl+C

34

Handle Ctrl+C gracefully

Agent

Catches KeyboardInterrupt, prints "Goodbye.", no stack trace

Utility Flows

#

Activity

Owner

Description

35

Explain existing command

User

aaws explain "aws ec2 describe-instances --filters ..."

36

LLM generates explanation

Agent

Describes what the command does, each flag, and safety caveats

37

View resolved config

User

aaws config show — effective config with secrets masked

38

Use --raw for scripting

User

aaws --raw "list my buckets" | jq '.Buckets[].Name'

39

Use --dry-run to preview

User

Shows generated command without executing

40

Override tier-3 refusal

User

aaws --i-accept-responsibility "delete all IAM users"

CI/CD and Automation

#

Activity

Owner

Description

41

Configure via env vars only

User

Set AAWS_LLM_PROVIDER, AAWS_AWS_REGION, etc. — no config file needed

42

Pipe raw output to tools

User

aaws --raw "..." | jq ... for scripted consumption

43

Tests on push (GitHub Actions)

Agent

Lint (ruff) + type check (mypy) + pytest across Python 3.11-3.13

44

Publish to PyPI on tag

Agent

hatch build + trusted publishing on v* tags


Installation

Step 1: Install AWS CLI v2

aaws requires the AWS CLI to be installed and in your PATH. It delegates all AWS operations to the aws command.

macOS:

brew install awscli

Windows:

Download and run the installer from https://awscli.amazonaws.com/AWSCLIV2.msi

Or via winget:

winget install Amazon.AWSCLI

Linux (x86_64):

curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install

Verify:

aws --version
# aws-cli/2.x.x Python/3.x.x ...

Step 2: Configure AWS Credentials

You need at least one AWS profile configured with valid credentials.

Option A: IAM Access Keys (simplest)

aws configure

You'll be prompted for:

AWS Access Key ID [None]: AKIAIOSFODNN7EXAMPLE
AWS Secret Access Key [None]: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
Default region name [None]: us-east-1
Default output format [None]: json

Option B: AWS SSO (recommended for organizations)

aws configure sso

Follow the browser login flow. Then activate the session:

aws sso login --profile your-profile-name

Option C: Environment Variables (CI/CD)

export AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
export AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
export AWS_DEFAULT_REGION=us-east-1

Verify credentials work:

aws sts get-caller-identity
# Should return your account ID, ARN, and user ID

Step 3: Install aaws

Requirements: Python 3.11+, AWS CLI v2 (configured above)

The package is published at pypi.org/project/aaws.

Standalone CLI (needs an LLM provider — Bedrock or OpenAI):

pip install aaws

With Claude Code MCP support (use your Anthropic subscription, no API key needed):

pip install aaws[mcp]
claude mcp add --scope user aaws -- python -m aaws.mcp_server

Verify the installation:

aaws --help
aws sts get-caller-identity   # confirm AWS creds work

Upgrade to latest version

pip install --upgrade aaws

Install from source (development)

git clone https://github.com/farmountain/ai_aws_cli.git
cd ai_aws_cli
pip install -e ".[dev,mcp]"

Quick Start

1. Configure

aaws config init

The wizard walks you through:

aaws configuration wizard

LLM provider [bedrock/openai] (bedrock):
Bedrock model ID (anthropic.claude-3-5-haiku-20241022-v1:0):
Default AWS profile (default):
Default AWS region (us-east-1):

 Configuration saved to ~/.config/aaws/config.yaml
Run aaws "list my S3 buckets" to test.

2. Run your first command

aaws "list my S3 buckets"

3. Try more commands

# Read-only (auto-executes, no confirmation)
aaws "show my running EC2 instances in us-west-2"
aaws "how many Lambda functions do I have"
aaws "get the details of my RDS database named prod-db"

# Write operations (asks y/n)
aaws "create an S3 bucket named my-new-bucket in us-east-1"
aaws "tag instance i-abc123 with Environment=staging"

# Preview without executing
aaws --dry-run "terminate instance i-abc123"

# Explain an existing command
aaws explain "aws iam attach-role-policy --role-name MyRole --policy-arn arn:aws:iam::aws:policy/ReadOnlyAccess"

# Raw output for scripting
aaws --raw "list my S3 buckets" | jq '.Buckets[].Name'

Configuration

Config file location

OS

Path

Linux/macOS

~/.config/aaws/config.yaml

Windows

%APPDATA%\aaws\config.yaml

Full config reference

llm:
  provider: bedrock          # "bedrock" or "openai"
  model: anthropic.claude-3-5-haiku-20241022-v1:0
  api_key: ${OPENAI_API_KEY} # Only for OpenAI; supports ${ENV_VAR} syntax
  temperature: 0.1           # Low for deterministic output
  timeout: 30                # Seconds

aws:
  default_profile: default
  default_region: us-east-1

safety:
  auto_execute_tier: 0       # Auto-run commands at or below this tier (0=read-only)
  protected_profiles:        # Glob patterns — all writes blocked on these profiles
    - "prod-*"
    - "production"

output:
  format: auto               # "auto" detects tables/cards/JSON
  raw: false                 # true = always output raw JSON
  color: true

Environment variable overrides

Every config field can be overridden with AAWS_-prefixed env vars. Useful for CI/CD where file config is impractical.

Env Var

Config Field

AAWS_LLM_PROVIDER

llm.provider

AAWS_LLM_MODEL

llm.model

AAWS_LLM_API_KEY

llm.api_key

AAWS_LLM_TEMPERATURE

llm.temperature

AAWS_LLM_TIMEOUT

llm.timeout

AAWS_AWS_PROFILE

aws.default_profile

AAWS_AWS_REGION

aws.default_region

AAWS_SAFETY_AUTO_EXECUTE_TIER

safety.auto_execute_tier

AAWS_OUTPUT_FORMAT

output.format

AAWS_OUTPUT_RAW

output.raw

AAWS_OUTPUT_COLOR

output.color


Commands Reference

aaws "<request>"

Translate natural language to an AWS CLI command and run it.

aaws [OPTIONS] "<natural language request>"

Flag

Description

--profile, -p

Override AWS profile for this invocation

--region, -r

Override AWS region for this invocation

--raw

Output raw JSON (no tables, no formatting)

--dry-run

Show the generated command without executing

--yes, -y

Auto-confirm tier 1-2 commands (skip interactive prompts)

--i-accept-responsibility

Override tier-3 catastrophic operation refusal

aaws explain "<command>"

Explain what an existing AWS CLI command does.

$ aaws explain "aws s3 rm s3://my-bucket --recursive"

Command: aws s3 rm s3://my-bucket --recursive

This command deletes all objects in the S3 bucket "my-bucket" recursively.
The --recursive flag means it will delete everything inside the bucket, not
just a single object. This is a destructive operation that cannot be undone.
Ensure you have backups before running this command.

aaws session

Start an interactive REPL with conversation context.

$ aaws session --profile dev --region eu-west-1

 aaws interactive session
 Profile: dev  Region: eu-west-1
 Type 'exit' or 'quit' to end the session. Ctrl+C to abort.

[aaws]> show my EC2 instances
  ...table output...

[aaws]> now stop the one named web-server
  # LLM sees the prior context and knows which instance you mean

[aaws]> what's the status now?
  # Multi-turn conversation continues

[aaws]> exit
Goodbye.

aaws config init

Interactive first-time setup wizard.

aaws config show

Print the resolved effective config with secrets masked.

$ aaws config show
{
  "llm": {
    "provider": "bedrock",
    "model": "anthropic.claude-3-5-haiku-20241022-v1:0",
    "api_key": "***",
    "temperature": 0.1,
    "timeout": 30
  },
  ...
}

End-to-End Flows with Examples

Flow 1: Read-Only Query (Tier 0 - Fully Agentic)

Zero friction. You type, agent does everything.

$ aaws "what EC2 instances are running in us-east-1"
Pipeline:
  [User]  Types request
  [Agent] Translates -> "aws ec2 describe-instances --filters Name=instance-state-name,Values=running --region us-east-1 --output json"
  [Agent] Classifies -> tier 0 (aws ec2 describe = read-only)
  [Agent] Safety gate -> auto-execute (tier 0, no prompt)
  [Agent] Executes subprocess
  [Agent] Detects JSON shape -> Reservations -> flattens Instances -> table
  [Agent] Renders Rich table
  [User]  Reads formatted output
 InstanceId     InstanceType  State    PublicIpAddress  LaunchTime
 i-0abc123def   t3.micro      running  54.123.45.67     2025-03-01T...
 i-0def456ghi   t3.large      running  54.234.56.78     2025-03-15T...

2 result(s)

User actions: 1 (type request). Agent actions: 6 (translate, classify, gate, execute, detect, render).


Flow 2: Write Operation (Tier 1 - User Confirms)

$ aaws "create an S3 bucket named analytics-2025 in us-west-2"
Pipeline:
  [User]  Types request
  [Agent] Translates -> "aws s3api create-bucket --bucket analytics-2025 --region us-west-2 --create-bucket-configuration LocationConstraint=us-west-2"
  [Agent] Classifies -> tier 1 (aws s3api create-bucket = write)
  [Agent] Shows command + explanation
  [User]  Confirms y/n
  [Agent] Executes, formats output
Command: aws s3api create-bucket --bucket analytics-2025 --region us-west-2 --create-bucket-configuration LocationConstraint=us-west-2
Creates a new S3 bucket named "analytics-2025" in us-west-2.

Run this command? [y/n]: y

 Location: http://analytics-2025.s3.amazonaws.com/

User actions: 2 (type request, confirm). Agent actions: 5.


Flow 3: Destructive Operation (Tier 2 - Warning + Explicit Confirm)

$ aaws "terminate instance i-0abc123def"
Pipeline:
  [User]  Types request
  [Agent] Translates -> "aws ec2 terminate-instances --instance-ids i-0abc123def"
  [Agent] Classifies -> tier 2 (aws ec2 terminate-instances = destructive)
  [Agent] Shows command + explanation
  [Agent] Shows WARNING panel
  [Agent] Offers --dry-run validation (EC2 commands support it)
  [User]  Chooses dry-run or types "yes" to confirm
  [Agent] Executes
Command: aws ec2 terminate-instances --instance-ids i-0abc123def
Terminates EC2 instance i-0abc123def. This permanently destroys the instance.

+---------- Destructive Operation ----------+
| WARNING  This operation is irreversible    |
| - it cannot be undone.                     |
+--------------------------------------------+

Validate first with --dry-run (no changes will be made)? [y/n]: y

Running: aws ec2 terminate-instances --instance-ids i-0abc123def --dry-run
Dry run succeeded - no changes were made.

User actions: 2 (type request, choose dry-run or confirm). Agent actions: 7 (translate, classify, gate, warn, offer dry-run, execute, render).


Flow 4: Catastrophic Operation (Tier 3 - Refused by Default)

$ aaws "delete all objects in s3://production-data recursively"
Pipeline:
  [Agent] Translates -> "aws s3 rm s3://production-data --recursive"
  [Agent] Classifies -> tier 3 (aws s3 rm + --recursive = catastrophic)
  [Agent] REFUSES execution
+---------- Catastrophic Operation Blocked ----------+
| REFUSED  This operation is catastrophic and may     |
| permanently alter or destroy your AWS account.      |
|                                                     |
| If you are absolutely certain, re-run with          |
| --i-accept-responsibility.                          |
+-----------------------------------------------------+

To override (with full tier-2 confirmation flow):

$ aaws --i-accept-responsibility "delete all objects in s3://production-data recursively"

Flow 5: Ambiguous Request (Agent Asks for Clarification)

$ aaws "stop that server"
Pipeline:
  [User]  Types vague request
  [Agent] Translates -> clarification needed (no resource ID)
  [Agent] Returns clarification question
  [User]  Reads question, re-runs with specifics
? Which EC2 instance would you like to stop? Please provide the instance ID
  (e.g., i-0abc123def) or a name tag to identify it.

In session mode, you can answer directly:

[aaws]> stop that server
? Which EC2 instance would you like to stop?

[aaws]> the one tagged Name=web-server
Command: aws ec2 stop-instances --instance-ids i-0abc123def
Run this command? [y/n]:

Flow 6: Error Recovery (Agent Diagnoses + Suggests Fix)

$ aaws "delete S3 bucket old-logs"
Pipeline:
  [User]  Confirms deletion
  [Agent] Executes -> fails (BucketNotEmpty)
  [Agent] Classifies error -> resource error
  [Agent] Sends command + error to LLM for interpretation
  [Agent] Renders error panel with suggestion
+---------- Error ----------+
| An error occurred          |
| (BucketNotEmpty) when      |
| calling DeleteBucket:      |
| The bucket is not empty.   |
|                            |
| Suggestion: The bucket     |
| "old-logs" still contains  |
| objects. Empty it first:   |
|   aws s3 rm s3://old-logs  |
|     --recursive            |
| Then retry the delete.     |
+----------------------------+

Flow 7: Credential Error (Agent Provides Hardcoded Fix)

+---------- Error ----------+
| ExpiredTokenException:     |
| The security token in the  |
| request has expired.       |
|                            |
| Suggestion: Your AWS       |
| session has expired.       |
| Refresh it with:           |
|   aws sso login            |
|     --profile default      |
+----------------------------+

No LLM call needed for credential errors -- instant, deterministic fix.


Flow 8: Protected Profile (Agent Blocks Writes)

# config.yaml
safety:
  protected_profiles:
    - "prod-*"
    - "production"
$ aaws --profile prod-us-east-1 "delete instance i-abc123"

Blocked: Profile 'prod-us-east-1' is protected (read-only). Switch profiles to make changes.

Read operations still work:

$ aaws --profile prod-us-east-1 "list my EC2 instances"
# Executes normally (tier 0, read-only)

Flow 9: Interactive Multi-Turn Session

$ aaws session --profile dev

 aaws interactive session
 Profile: dev  Region: us-east-1
 Type 'exit' or 'quit' to end the session. Ctrl+C to abort.

[aaws]> show my security groups
  GroupId       GroupName      Description     VpcId
  sg-abc123     web-sg         Web servers     vpc-111
  sg-def456     db-sg          Database        vpc-111
  2 result(s)

[aaws]> what are the inbound rules for the web one?
  # Agent knows "the web one" = sg-abc123 from conversation history
  Command: aws ec2 describe-security-group-rules --filters Name=group-id,Values=sg-abc123 --output json
  ...rules table...

[aaws]> add port 443 to it
  Command: aws ec2 authorize-security-group-ingress --group-id sg-abc123 --protocol tcp --port 443 --cidr 0.0.0.0/0
  Run this command? [y/n]: y
  ...success...

[aaws]> exit
Goodbye.

History is bounded to the last 10 exchanges. Session state is in-memory only (never written to disk).


Flow 10: Scripting and Piping (Raw Mode)

# Get instance IDs as plain text for scripting
INSTANCES=$(aaws --raw "list running EC2 instances" | jq -r '.Reservations[].Instances[].InstanceId')

# Use in a loop
for id in $INSTANCES; do
  echo "Processing $id..."
done

# Combine with other tools
aaws --raw "show my IAM users" | jq '.Users[] | select(.UserName | startswith("temp-"))' | wc -l

Flow 11: CI/CD Automation with --yes Flag

# In a CI pipeline, skip interactive confirmations for tier 1-2 commands
aaws --yes "tag instance i-abc123 with Environment=staging"

# Combine with --raw for machine-readable output
aaws --yes --raw "create an S3 bucket named build-artifacts-$(date +%s)" | jq .

# Configure entirely via environment variables (no config file needed)
export AAWS_LLM_PROVIDER=bedrock
export AAWS_AWS_REGION=us-east-1
aaws --yes "create a CloudWatch alarm for high CPU on instance i-abc123"

Note: --yes auto-confirms tier 1 (write) and tier 2 (destructive) commands. It does NOT bypass tier 3 (catastrophic) refusal -- that still requires --i-accept-responsibility.


Safety Model

Risk Tiers

Tier

Label

Examples

Gate

User Action

0

Read-only

describe, list, get, head

Auto-execute

None

1

Write

create, put, update, start, tag

Show + y/n

Press y (or --yes)

2

Destructive

delete, terminate, detach, rm

Warn + type "yes" + dry-run offer

Type yes (or --yes)

3

Catastrophic

Bulk delete, org-level, IAM nuke

Refused

--i-accept-responsibility (not --yes)

Classification Strategy

  1. Tier 3 substring check -- patterns like aws s3 rm + --recursive always match catastrophic

  2. Static tier table -- 100+ command prefix-to-tier mappings (longest match wins). Deterministic, zero LLM cost, cannot be overridden by a confused model

  3. LLM fallback -- for unknown/novel commands, the LLM's own risk_tier assessment is used

Protected Profiles

Configure glob patterns in safety.protected_profiles. Any write operation (tier > 0) against a matching profile is blocked with a clear error. Case-insensitive matching via fnmatch.

Shell Injection Prevention

Commands are never passed to a shell. shlex.split() tokenizes the command string and subprocess.run() receives a list with shell=False. This is enforced at the architecture level, not by convention.


LLM Providers

AWS Bedrock (default)

Uses your existing AWS credentials. No extra API key, no data leaving your AWS account.

llm:
  provider: bedrock
  model: anthropic.claude-3-5-haiku-20241022-v1:0

Requires Bedrock model access to be enabled in the AWS Console. If not, aaws detects the AccessDeniedException and tells you exactly where to enable it.

OpenAI

llm:
  provider: openai
  model: gpt-4o-mini
  api_key: ${OPENAI_API_KEY}

Or set OPENAI_API_KEY as an environment variable.

Structured Output

Both providers use tool/function calling to guarantee structured responses (command, explanation, risk_tier, clarification). This eliminates JSON parsing failures from free-text prompting. A fallback JSON parser handles edge cases where tool calling is unavailable.


Output Formatting

JSON Shape

Rendered As

Example

Top-level list of dicts

Rich table

EC2 instances, S3 buckets, IAM users

Single dict

Key-value card panel

Bucket details, instance metadata

Empty list/dict

"No results."

Non-JSON text

Plain text passthrough

aws s3 ls text output

Any (with --raw)

Raw stdout

For piping to jq, grep, etc.

Column hints are built in for 25+ AWS resource types (EC2 Instances, S3 Buckets, IAM Users/Roles/Policies, Lambda Functions, CloudFormation Stacks, ECS Clusters, RDS instances, etc.), ensuring the most useful columns appear first.


Error Recovery

Error Type

Detection

Recovery

LLM Call?

Expired token

Regex on stderr

aws sso login --profile <name>

No

No credentials

Regex on stderr

aws configure

No

Access denied

Regex on stderr

Shows denied action + profile switch advice

No

Bucket not empty

Regex on stderr

LLM explains + suggests aws s3 rm --recursive

Yes

Resource not found

Regex on stderr

LLM interprets error + suggests fix

Yes

Resource conflict

Regex on stderr

LLM interprets error + suggests fix

Yes

Unknown error

Fallback

Raw stderr in styled error panel

No

LLM timeout

Exception catch

"Check your network connection and try again."

No

Credential and permission errors use hardcoded messages (instant, free, deterministic). Resource errors are sent to the LLM for interpretation (contextual, actionable).


Architecture

src/aaws/
  __init__.py          # Package version
  cli.py               # Typer entry point: root command, explain, session, config
  translator.py        # NL -> AWS CLI via LLM (system prompt, validation, retry)
  executor.py          # subprocess.run(shlex.split(cmd), shell=False)
  formatter.py         # JSON shape detection -> Rich tables/cards/JSON
  config.py            # Pydantic models, YAML loader, env var resolution
  errors.py            # Error classification, credential messages, LLM interpretation
  session.py           # Interactive REPL with bounded conversation history
  providers/
    __init__.py         # get_provider() factory
    base.py             # LLMProvider protocol, TOOL_SCHEMA, LLMResponse, Message
    bedrock_provider.py # AWS Bedrock converse API with tool use
    openai_provider.py  # OpenAI chat completions with function calling
  safety/
    __init__.py         # Package exports
    classifier.py       # classify(), apply_safety_gate(), is_protected_profile()
    tier_table.py       # Static dict: 100+ command prefix -> tier mappings

Design Decisions

Decision

Choice

Rationale

Execution

subprocess.run over boto3

Auditable commands, no 300+ service API to model

LLM output

Tool/function calling over prompting

Guaranteed structured schema, no JSON parse failures

Safety

Static table + LLM fallback

Deterministic floor (90%+ commands) + flexible fallback

CLI framework

Typer + Rich

Type-annotated CLI + production-grade terminal UI

Config

Pydantic + YAML + env vars

Validation, file-based defaults, CI/CD-friendly overrides

Provider

Bedrock default

Same creds, same account, no extra API key

Python

3.11+ minimum

Not EOL until 2027, match/case, wide platform availability


UX and Agent Experience Assessment

What works well

  • Zero-config for Bedrock users: If you have AWS creds, you have an LLM. No signup, no API key.

  • Progressive disclosure of safety: Tier 0 is frictionless, each tier adds exactly one more confirmation step. The escalation is proportional to risk.

  • Clarification over guessing: The agent asks rather than hallucinating resource IDs -- this is a critical safety decision.

  • Deterministic safety floor: The static tier table means aws ec2 terminate-instances is always tier 2, regardless of what the LLM says. The LLM can escalate but never downgrade.

  • Error recovery is contextual: Credential errors get instant hardcoded fixes (no LLM latency/cost). Resource errors get LLM interpretation. This is the right split.

  • Session mode preserves context: "stop the one tagged web-server" works because the agent remembers the prior listing.

  • Raw mode for composability: --raw makes aaws a good Unix citizen that pipes into jq and scripts.

Areas for improvement

  • No command history persistence: Session history lives in memory only. Restarting loses all context. A ~/.aaws/history.json with last N sessions would help.

  • No streaming output: Long-running commands (e.g., aws s3 sync) show nothing until completion. Streaming stdout would improve perceived latency.

  • No command validation against AWS help: The agent trusts the LLM to produce valid flags. A post-generation check against aws <service> help output would catch hallucinated flags.

  • Protected profile patterns are config-only: No aaws safety add-profile "prod-*" command to manage them interactively.

  • Single-command-at-a-time: No agentic multi-step workflows (acknowledged as v2 scope).

Agent autonomy balance

The current split is well-calibrated for a CLI tool that runs real infrastructure commands:

  • High autonomy for read-only operations and output formatting (no friction for safe actions)

  • Shared control for writes and destructive operations (agent prepares, human decides)

  • Human-only for catastrophic operations (agent refuses, human must explicitly override)

  • Full autonomy for error recovery (agent diagnoses and suggests without asking)

This is the right balance. A more autonomous agent (auto-executing writes) would be dangerous for AWS operations. A less autonomous one (confirming reads) would be annoying.


Development

Setup

git clone https://github.com/farmountain/ai_aws_cli.git
cd ai_aws_cli
pip install -e ".[dev]"

Run tests

pytest --tb=short -q

Lint and type check

ruff check src/ tests/
mypy src/

CI/CD

  • Push to main or PR: runs tests on Python 3.11, 3.12, 3.13 + lint + type check

  • Push v* tag: builds and publishes to PyPI via trusted publishing

Publish a release

git tag v0.1.0
git push origin v0.1.0
# GitHub Actions builds and publishes to PyPI

License

Apache License 2.0 - see LICENSE for details.

-
security - not tested
A
license - permissive license
-
quality - not tested

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/farmountain/ai_aws_cli'

If you have feedback or need assistance with the MCP directory API, please join our Discord server