MCP Hub
Back to servers

aaws - AI-Assisted AWS CLI

A Python CLI package aaws published to GitHub with PyPI distribution via GitHub Actions. Natural language in → AWS CLI command out, with explain, confirm, and safety layers. Available as MCP server.

glama
Updated
Apr 7, 2026

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

ToolPurposeLLM Needed?
classify_aws_commandRisk tier classification (0-3) for any AWS CLI commandNo (static table)
execute_aws_commandSafe subprocess execution with profile/region injectionNo
format_aws_outputJSON shape detection -> plain-text tables/cardsNo
list_safety_tiersBrowse known command risk tiers by serviceNo
check_aws_environmentVerify AWS CLI, active profile, regionNo

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

AspectStandalone CLIMCP Mode
LLM providerYou configure (Bedrock/OpenAI)Claude Code subscription (free)
NL translationaaws translator.pyClaude Code LLM
Session memoryIn-process, 10-turn limitClaude Code built-in (full context)
Multi-step workflowsOne command at a timeClaude Code orchestrates multiple
Error interpretationLLM call per errorClaude Code reasons over stderr
Configurationaaws config init requiredJust register MCP server

AWS Cloud Engineering Lifecycle (MCP)

Lifecycle StageStandalone CLI+ MCP with Claude Code
DiscoveryQuery-based+ Autonomous inventory, cross-service
ProvisioningSingle-command+ Multi-step with dependency ordering
MonitoringSnapshot queries+ Conversational drill-down
TroubleshootingHardcoded + LLM errors+ Autonomous log/metric investigation
MaintenanceManual delete/resize+ Agent finds waste, suggests optimization
SecurityCLI pass-through+ Permission auditing
Disaster RecoverySingle-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

#ActivityOwnerDescription
1Install aawsUserpip install aaws
2Run config wizardUseraaws config init — choose provider, model, profile, region
3Detect missing configAgentIf no config exists, prints actionable message and exits
4Validate config schemaAgentPydantic validates all fields, rejects bad values
5Resolve ${ENV_VAR} in configAgentSubstitutes environment variable references in YAML values
6Apply AAWS_* env overridesAgentEnvironment variables override file-based config (CI/CD friendly)
7Detect AWS CLI presenceAgentChecks aws in PATH on startup; fails fast with install link

One-Shot Command Flow

#ActivityOwnerDescription
8Write natural language requestUseraaws "show my running EC2 instances"
9Resolve AWS profile + regionAgentMerges --profile/--region flags > config > boto3 session > fallback
10Build LLM prompt with contextAgentInjects system prompt + profile/region context + user request
11Call LLM via tool/function callingAgentSends structured tool schema, forces tool use (no free-text)
12Validate command starts with aws AgentRejects hallucinated non-AWS output
13Auto-retry on invalid commandAgentSends corrective instruction, retries once, then fails with clear error
14Return clarification if ambiguousAgentIf request is vague, asks ONE clarifying question instead of guessing
15Classify risk tier (static table)AgentLongest-prefix match against 100+ known command patterns
16Fallback to LLM-assigned tierAgentUnknown commands use the LLM's risk assessment
17Check protected profileAgentBlocks all writes on prod-* or user-defined glob patterns
18Show command + explanationAgentDisplays the generated command with plain-English explanation
19Confirm or cancel executionUserTier 0: auto-run. Tier 1: y/n. Tier 2: type "yes". Tier 3: refused. --yes auto-confirms 1-2
20Offer --dry-run for EC2AgentFor destructive EC2 commands, offers to validate with --dry-run first
21Execute via subprocessAgentshlex.split() + subprocess.run(shell=False) — no injection possible
22Detect output shapeAgentInspects JSON: list -> table, dict -> card, empty -> "No results."
23Render formatted outputAgentRich tables with column hints per resource type, or syntax-highlighted JSON
24Classify error on failureAgentRegex matches for credential, permission, resource errors
25Provide hardcoded fix for authAgentExpired token -> aws sso login. No creds -> aws configure
26LLM-interpret resource errorsAgentSends failed command + stderr to LLM for plain-English recovery steps

Interactive Session Flow

#ActivityOwnerDescription
27Start sessionUseraaws session [--profile X] [--region Y]
28Display session headerAgentShows active profile, region, exit instructions
29Type follow-up requestsUserConversational input referencing prior context
30Maintain conversation historyAgentAppends each exchange, bounded to last 10 for LLM context
31Translate with history contextAgentLLM sees prior conversation for multi-turn refinement
32Full safety pipeline per turnAgentEvery command goes through classify -> gate -> execute -> format
33Exit sessionUserType exit/quit or Ctrl+C
34Handle Ctrl+C gracefullyAgentCatches KeyboardInterrupt, prints "Goodbye.", no stack trace

Utility Flows

#ActivityOwnerDescription
35Explain existing commandUseraaws explain "aws ec2 describe-instances --filters ..."
36LLM generates explanationAgentDescribes what the command does, each flag, and safety caveats
37View resolved configUseraaws config show — effective config with secrets masked
38Use --raw for scriptingUseraaws --raw "list my buckets" | jq '.Buckets[].Name'
39Use --dry-run to previewUserShows generated command without executing
40Override tier-3 refusalUseraaws --i-accept-responsibility "delete all IAM users"

CI/CD and Automation

#ActivityOwnerDescription
41Configure via env vars onlyUserSet AAWS_LLM_PROVIDER, AAWS_AWS_REGION, etc. — no config file needed
42Pipe raw output to toolsUseraaws --raw "..." | jq ... for scripted consumption
43Tests on push (GitHub Actions)AgentLint (ruff) + type check (mypy) + pytest across Python 3.11-3.13
44Publish to PyPI on tagAgenthatch 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

OSPath
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 VarConfig Field
AAWS_LLM_PROVIDERllm.provider
AAWS_LLM_MODELllm.model
AAWS_LLM_API_KEYllm.api_key
AAWS_LLM_TEMPERATUREllm.temperature
AAWS_LLM_TIMEOUTllm.timeout
AAWS_AWS_PROFILEaws.default_profile
AAWS_AWS_REGIONaws.default_region
AAWS_SAFETY_AUTO_EXECUTE_TIERsafety.auto_execute_tier
AAWS_OUTPUT_FORMAToutput.format
AAWS_OUTPUT_RAWoutput.raw
AAWS_OUTPUT_COLORoutput.color

Commands Reference

aaws "<request>"

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

aaws [OPTIONS] "<natural language request>"
FlagDescription
--profile, -pOverride AWS profile for this invocation
--region, -rOverride AWS region for this invocation
--rawOutput raw JSON (no tables, no formatting)
--dry-runShow the generated command without executing
--yes, -yAuto-confirm tier 1-2 commands (skip interactive prompts)
--i-accept-responsibilityOverride 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

TierLabelExamplesGateUser Action
0Read-onlydescribe, list, get, headAuto-executeNone
1Writecreate, put, update, start, tagShow + y/nPress y (or --yes)
2Destructivedelete, terminate, detach, rmWarn + type "yes" + dry-run offerType yes (or --yes)
3CatastrophicBulk delete, org-level, IAM nukeRefused--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 ShapeRendered AsExample
Top-level list of dictsRich tableEC2 instances, S3 buckets, IAM users
Single dictKey-value card panelBucket details, instance metadata
Empty list/dict"No results."
Non-JSON textPlain text passthroughaws s3 ls text output
Any (with --raw)Raw stdoutFor 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 TypeDetectionRecoveryLLM Call?
Expired tokenRegex on stderraws sso login --profile <name>No
No credentialsRegex on stderraws configureNo
Access deniedRegex on stderrShows denied action + profile switch adviceNo
Bucket not emptyRegex on stderrLLM explains + suggests aws s3 rm --recursiveYes
Resource not foundRegex on stderrLLM interprets error + suggests fixYes
Resource conflictRegex on stderrLLM interprets error + suggests fixYes
Unknown errorFallbackRaw stderr in styled error panelNo
LLM timeoutException 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

DecisionChoiceRationale
Executionsubprocess.run over boto3Auditable commands, no 300+ service API to model
LLM outputTool/function calling over promptingGuaranteed structured schema, no JSON parse failures
SafetyStatic table + LLM fallbackDeterministic floor (90%+ commands) + flexible fallback
CLI frameworkTyper + RichType-annotated CLI + production-grade terminal UI
ConfigPydantic + YAML + env varsValidation, file-based defaults, CI/CD-friendly overrides
ProviderBedrock defaultSame creds, same account, no extra API key
Python3.11+ minimumNot 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.

Reviews

No reviews yet

Sign in to write a review