AWS shipped their official MCP server to general availability last week. It's a hosted server with curated skills, a Python sandbox, and days-fresh API coverage - the obvious choice for a lot of teams. I'd been building @yawlabs/aws-mcp before that and kept going, because it solves a few things differently.

These are alternatives, not complements - both call any AWS API, so picking both just hands the model two redundant tools. This post is about when the yawlabs server is the better pick, and where the official server wins.

SSO re-login that survives Windows

The pain: your SSO token expires mid-session. The assistant calls aws sso login as a subprocess. That command tries to open the OS default browser - and on Windows, the handoff from an AI-assistant-spawned subprocess to your user-session browser drops silently. You context-switch to a terminal, run the command yourself, then come back. The flow that was supposed to take ten seconds takes a minute and breaks your train of thought.

The fix is the --no-browser device-code flow. The CLI prints a short URL and a code to stdout instead of trying to open a browser. The MCP server parses them out and surfaces them to the assistant:

You:    "How many objects are in the staging-artifacts bucket?"

Claude: (aws_whoami) -> SSO session expired for profile 'staging'.
        (aws_login_start with profile='staging')
        "Your SSO token expired. Open
         https://device.sso.us-east-1.amazonaws.com/
         and enter code: ABCD-EFGH
         I'll wait."

You:    *click, authenticate in your browser*

Claude: (aws_login_complete with sessionId)
        (aws_call with service='s3api', operation='list-objects-v2',
                     params={ Bucket: 'staging-artifacts' }, query='KeyCount')
        "There are 4,182 objects in staging-artifacts."

One click in the browser you already have open. The token lands in ~/.aws/sso/cache/ the same way a normal aws sso login would, so the rest of your SDK ecosystem picks it up transparently. There's also aws_refresh_if_expiring_soon for proactive top-ups before a long workflow, so the assistant can avoid the mid-flight expiry altogether.

The official server bridges IAM SigV4 to OAuth through a local proxy - that's a different problem (cloud-side auth), and it doesn't help with the local aws sso login browser-handoff failure.

Generic CRUD with dry-run diffs

AWS Cloud Control API (CCAPI) is a single typed CRUD surface over any resource type with a CloudFormation schema - hundreds of services, most of the control plane. The yawlabs server wraps it in seven tools: aws_resource_get, list, create, update, delete, status, and diff. The same lifecycle works whether you're creating a Lambda function, an SSM parameter, an IAM role, or an RDS instance.

Create and update are async on AWS's side - they return a request token and you poll. The wrapper collapses that into one call with awaitCompletion: true:

(aws_resource_create with
   typeName='AWS::SSM::Parameter',
   desiredState={Name: '/my/param', Type: 'String', Value: 'hello'},
   awaitCompletion: true)
-> server polls get-resource-request-status until SUCCESS / FAILED / CANCEL_COMPLETE
   and returns the terminal ProgressEvent in one call

And aws_resource_diff is the one I reach for the most. It fetches the current resource state, simulates applying an RFC 6902 JSON Patch in memory, and returns before/after plus a flat list of changed paths. Nothing is sent to AWS:

(aws_resource_diff with
   typeName='AWS::Lambda::Function',
   identifier='my-fn',
   patchDocument=[{op: 'replace', path: '/MemorySize', value: 1024}])
-> { before: {MemorySize: 256, ...},
     after:  {MemorySize: 1024, ...},
     changes: [{op: 'replace', path: '/MemorySize', before: 256, after: 1024}] }

Useful when the agent is about to invoke aws_resource_update on a production resource and you want to see exactly what's going to land. Supports the add/remove/replace subset of RFC 6902, which covers the vast majority of CCAPI updates.

Multi-region fan-out in one call

AWS's official server is single-region-per-call. For fleet-wide reads - "describe-instances across all our regions," "list buckets everywhere," "what does the IAM password policy look like across the fleet" - that's one tool call per region, then the assistant has to aggregate. aws_multi_region bundles it:

(aws_multi_region with
   service='ec2', operation='describe-instances',
   regions=['us-east-1','us-west-2','eu-west-1'],
   query='Reservations[].Instances[].InstanceId')
-> {okCount: 3, errorCount: 0,
    results: [{region, ok, data}, ...]}

Runs up to 32 regions in parallel with a bounded concurrency window. Partial failure is expected and surfaced per-region - services aren't available everywhere, authorization can be region-scoped, transient errors happen. The caller decides what to do with the mix.

IAM pre-flight checks

Most 403s in agent-driven AWS work are predictable. The agent is about to call something the principal can't do. aws_iam_simulate wraps IAM's Policy Simulator so the assistant can ask the question before eating the failure:

(aws_iam_simulate with
   principalArn='arn:aws:iam::123:role/deploy-role',
   actions=['lambda:CreateFunction', 's3:GetObject'],
   resources=['arn:aws:s3:::my-bucket/*'])
-> summary: {allowed: 1, denied: 1, total: 2}
   results: [
     {action: 'lambda:CreateFunction', resource: '*',
      decision: 'allowed', matchedStatementIds: ['AdminAccess#statement-2']},
     {action: 's3:GetObject', resource: 'arn:aws:s3:::my-bucket/*',
      decision: 'implicitDeny', missingContextValues: ['aws:RequestTag/Project']}
   ]

You learn which IAM statement decided each action, and which condition-key context the policy wanted but you didn't provide. The caller needs iam:SimulatePrincipalPolicy on themselves to use this - one IAM statement on the principal that runs the assistant.

A JS scripting sandbox

Some workflows are "list X, fetch Y for each, return Z." Done with N tool calls, the assistant ping-pongs through every intermediate result - tokens spent, context bloated. aws_script runs a short JS snippet inside a node:vm sandbox with aws.call, aws.paginate, aws.paginateAll, aws.resource.*, and aws.logsTail available, and returns a combined result:

(aws_script with code=`
   const listed = await aws.resource.list({
     typeName: "AWS::Lambda::Function"
   });
   const big = [];
   for (const r of listed.resources) {
     const cfg = await aws.resource.get({
       typeName: "AWS::Lambda::Function",
       identifier: r.identifier
     });
     if (cfg.properties.MemorySize > 1024) {
       big.push({
         name: cfg.properties.FunctionName,
         mem: cfg.properties.MemorySize
       });
     }
   }
   return big;
 `)
-> one round-trip; the agent gets the filtered list
   without N intermediate tool calls

Same shape as the official server's run_script. Theirs runs Python server-side in their hosted sandbox; this one is JS-native and runs locally. The sandbox strips the obvious filesystem and process escape hatches (no require, process, fs, fetch, timers, globalThis) but the threat model is "model writes JS that calls our tools," not "untrusted code from the internet" - treat the same way you treat anything else the model can call.

What I borrowed from the official server

Two pieces of the yawlabs server were shaped by the official one. Credit where due:

Where the official server wins

The honest tradeoffs cut both ways. The official AWS MCP server is the better pick if you want:

Their server requires Python and uv, and routes through a proxy that bridges IAM SigV4 to OAuth. The yawlabs server is Node-only via npx, runs locally, and stays in the SSO flow your CLI already uses.

Try it

Install with npm and add it to your MCP client config:

{
  "mcpServers": {
    "aws": {
      "command": "npx",
      "args": ["-y", "@yawlabs/aws-mcp"]
    }
  }
}

Or skip the JSON entirely and add it to mcp.hosting so it syncs to every MCP client on your machine. For more on that orchestration pattern, see Stop Juggling MCP Servers.

The repo, with the full comparison table: github.com/YawLabs/aws-mcp.


Jeff Yaw, Yaw Labs. Follow along at tokenlimit.news for weekly notes on AI infrastructure.