Skip to content

feat: multi-account AWS MCP profiles; persist SSO tokens across rebuilds - #31

Merged
ChrisSc merged 1 commit into
mainfrom
feat/aws-mcp-profiles
Aug 31, 2026
Merged

ChrisSc merged 1 commit into
mainfrom
feat/aws-mcp-profiles

Conversation

@ChrisSc

@ChrisSc ChrisSc commented Aug 31, 2026

Copy link
Copy Markdown
Owner

Follow-up to #30. Answers "can the MCP server authenticate from a profile?" — yes, and better than one-profile-at-a-time.

AWS_MCP_PROFILES — one server, many accounts

The proxy's --profile flag is variadic:

--profile PROFILE [PROFILE ...]
    AWS profile(s) to use. First profile is the default.
    Additional profiles enable per-call switching via
    aws_profile tool parameter

So the first name is the server's default identity and the rest are selectable per tool call — read from one account and act in another in the same conversation, no restart. Set it per host in .devcontainer/.env (gen-env.sh never rewrites an existing .env, so added lines survive); shipped empty since profile names are host-specific. Documented in .env.example.

The boot credential probe now targets the default profile rather than the ambient chain — with a list configured those are different identities, and only the first one determines whether the handshake succeeds.

⚠ AWS_MCP_READ_ONLY now defaults to 0

Writes are enabled by default. The MCP tools act with the full IAM permissions of the selected profile and can create, modify, and delete real resources — in any account in AWS_MCP_PROFILES, not just the default. The list is documented least-privileged-first because position 1 is what an unqualified call uses, and seed/CLAUDE.md now instructs Claude to confirm the target account before mutating. AWS_MCP_READ_ONLY=1 restores describe-only.

Fix: aws sso login did not survive a rebuild

Found while checking where the tokens actually land:

~/.aws          → overlay      (container layer — destroyed by make rebuild)
~/.claude       → /dev/vda1    (named volume — persistent)

AWS_CONFIG_FILE / AWS_SHARED_CREDENTIALS_FILE relocate only config and credentials. The SSO/OIDC token cache path is not configurable — the CLI always writes ~/.aws/cli/cache/session.db and ~/.aws/sso/cache/. So config persisted but tokens didn't, and every profile needed a fresh login after each make rebuild.

seed-claude.sh now points ~/.aws at ~/.claude/aws as a directory symlink — the same fix, and the same per-file-symlink trap (temp-file + atomic rename), as the existing ~/.ssh treatment. An existing ~/.aws is migrated in with cp -an first, so a live token cache is preserved rather than dropped. make boot-check requires the new seed.aws.linked event.

Since ~/.aws is ~/.claude/aws now, the toolkit's skills stamp moved to ~/.claude/aws-toolkit/ so its bookkeeping never surfaces inside the user's AWS directory. The old stamp is migrated rather than discarded, so existing containers don't re-download all 23 skills.

Verification

  • make lint clean; make smoke → smoke OK; make boot-check → boot-check OK (now including seed.aws.linked).
  • ~/.aws resolves to /home/claude/.claude/aws on /dev/vda1, with the pre-existing cli/cache/session.db carried across the migration.
  • Server registers with all seven profiles and no --read-only; aws.mcp.registered records default_profile=sandbox, read_only=0.
  • Credential probe reports against sandbox specifically (aws.credentials.absent profile=sandbox), not the ambient chain.
  • Restart hits the migrated stamp → aws.skills.current, no re-download.
  • ENVIRONMENT.md renders the active profile list, the write posture, and live credential status.

🤖 Generated with Claude Code

Three changes to the Agent Toolkit for AWS wiring, all still unreleased.

AWS_MCP_PROFILES — the proxy's --profile flag is variadic, so one MCP
server can span an AWS Organization: the first profile in the list is the
default identity and the rest are selectable PER TOOL CALL via its
`aws_profile` parameter, with no restart between accounts. Empty by
default (profile names are host-specific); set per host in .env, which
gen-env.sh never rewrites once generated. The boot credential probe now
targets the default profile rather than the ambient chain — with a list
configured those are different identities, and only the first governs
whether the handshake succeeds.

AWS_MCP_READ_ONLY now defaults to 0 (writes enabled). The MCP tools act
with the full IAM permissions of the selected profile and can create,
modify and delete real resources in ANY account in the list, not just the
default — so the list is documented least-privileged-first, since
position 1 is what an unqualified call uses. seed/CLAUDE.md tells Claude
to confirm the target account before mutating.

Fix: `aws sso login` did not survive a rebuild. AWS_CONFIG_FILE and
AWS_SHARED_CREDENTIALS_FILE relocate only `config` and `credentials`, but
the SSO/OIDC token cache path is NOT configurable — the CLI always writes
~/.aws/cli/cache/session.db and ~/.aws/sso/cache/, which live in the
container layer and die with every `make rebuild`. Config persisted,
tokens didn't, so every profile needed a fresh login after each rebuild.
seed-claude.sh now points ~/.aws at ~/.claude/aws as a DIRECTORY symlink,
same fix and same per-file-symlink trap (temp-file + atomic rename) as
~/.ssh. An existing ~/.aws is migrated in with `cp -an` so a live token
cache is preserved. boot-check requires the new seed.aws.linked event.

Because ~/.aws IS ~/.claude/aws now, the toolkit's skills stamp moved to
~/.claude/aws-toolkit/ so its bookkeeping never surfaces inside the
user's AWS dir; the old stamp is migrated, not discarded, so existing
containers don't re-download all 23 skills.

Verified: make lint / smoke / boot-check green; ~/.aws resolves onto the
volume with the pre-existing session.db carried over; the server registers
with all seven profiles and no --read-only; the probe reports against
`sandbox` specifically; a restart hits the migrated stamp
(aws.skills.current, no re-download).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@ChrisSc
ChrisSc merged commit 6a39931 into main Aug 31, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant