Skip to main content
Glama

aws-sso-mcp

Got more than one AWS account or org? Sign in once, ask in plain English, and get one merged answer from all of them, using the IAM roles your rules choose, never the AI's.

aws-sso-mcp is an MCP (Model Context Protocol) server. It plugs into a chat assistant such as GitHub Copilot Chat in VS Code or Claude Desktop, and lets that assistant query and manage AWS through your AWS IAM Identity Center (SSO) login.

"List all stopped EC2 instances across every prod account in org1."

One sentence in. One combined answer out, from every matching account.

Contents

Related MCP server: Sandy

Is this for you?

If you work in more than one AWS account, yes. The more accounts and orgs you have, the more it saves you.

  • Several orgs. Keep every org in one config file. Switch between them in chat, or query several in a single request. Each org needs its own login, and you never mix them up by accident.

  • Dozens or hundreds of accounts. Find accounts by name, id or environment, then ask one question of all of them. No account-by-account console hopping, no profile juggling.

  • A different role per account. Prod gets ReadOnlyAccess, sandboxes get PowerUserAccess. You write that once, and it is applied everywhere.

  • Questions that cross accounts. "Where is this running?", "Which accounts have public buckets?", "What is stopped in prod?" Each is one sentence instead of an afternoon of clicking.

If you only ever use a single account, it still works, but a plain AWS CLI profile is simpler.

Why this exists

Companies often have tens or hundreds of AWS accounts, spread over several AWS Organizations. Answering "where is X running?" normally means logging in to each account in turn.

This server removes that grind:

Without it

With it

Sign in to every account, one by one

Sign in once per organization

Remember which IAM role to use in which account

Roles are chosen by rules you write in a config file

Run the same CLI command N times

One request fans out to all N accounts in parallel

Copy and merge the outputs yourself

One merged, sorted, redacted result

What you can do with it

  • Query any AWS service. One generic tool (aws_query) takes the same service and operation names as the AWS CLI and boto3, for example ec2 and DescribeInstances. There are no per-service tools to wait for.

  • Select accounts flexibly. Pick by org, exact name, id, name substring, or an environment (prod, staging, dev) that you define. Exclusions are supported.

  • Chain steps. run_dag runs a small multi-step plan per account. For example, list the instances, then describe their volumes using the first step's output.

  • Manage sessions. Log in, switch org, check token status, search the cached account directory.

  • Audit everything. Every call is appended to a local JSONL audit log.

How it works in 30 seconds

flowchart TD
    You([You]) -->|1. ask in plain English| Chat[Chat assistant<br/>Copilot, Claude, ...]
    Chat -->|2. MCP tool call over stdio| MCP[aws-sso-mcp]
    MCP -->|3. sign in once, device login| IDC[AWS IAM Identity Center]
    IDC -->|4. short-lived role keys<br/>kept in memory only| MCP
    MCP -->|5. API calls, in parallel| ACC[Your AWS accounts<br/>EC2, S3, IAM ...]
    ACC -->|one reply per account| MCP
    MCP -->|6. merged, redacted result| Chat
    Chat -->|7. answer in plain English| You

The full, step-by-step explanation (with code references) is in docs/how-it-works.md. The detailed connectivity diagrams are in docs/architecture.md.

Quick start

You need Python 3.12+ and uv.

For the full walkthrough, see docs/setup-guide.md.

1. Install

uv tool install aws-sso-mcp     # or: pipx install aws-sso-mcp
aws-sso-mcp --version

2. Create your config (references only, never secrets)

mkdir -p ~/.config/aws-sso-mcp
curl -fsSL https://raw.githubusercontent.com/arnabdas24/aws-sso-mcp/main/examples/config.example.toml \
  -o ~/.config/aws-sso-mcp/config.toml

Edit it and set your start_url and region per org. The minimum is:

default_org = "org1"

[portals.org1]
start_url = "https://your-org1.awsapps.com/start"
region    = "us-east-1"   # where your Identity Center lives, NOT where you query

[portals.org1.environments.prod]
match    = "endswith"
patterns = ["-prod"]
roles    = ["ReadOnlyAccess"]

On Windows, use %USERPROFILE%\.config\aws-sso-mcp\config.toml, or point AWS_SSO_MCP_CONFIG at any path.

3. Register it with your chat client (VS Code example, in your user mcp.json)

{
  "servers": {
    "aws-sso-mcp": { "type": "stdio", "command": "aws-sso-mcp" }
  }
}

Claude Desktop, Claude Code, Cursor, Windsurf and non-default config paths are all covered in docs/setup-guide.md.

4. Chat

In Agent mode, try:

  1. Log in to org1. A browser opens once for approval.

  2. Find accounts with "prod" in the name.

  3. List stopped EC2 instances in us-east-1 across every prod account.

More ready-made phrasings, plus mistakes to avoid, are in docs/prompt-guide.md. To see the exact tool call behind each sentence, read docs/examples.md.

Safety model

Read this before pointing the server at real accounts.

Good to know: it works with exactly the permissions you have.

The server signs in through IAM Identity Center and inherits the permission set of the role you choose. It can see and do what that role allows in IAM, no more and no less, and IAM is the only gate. That is what keeps it simple and predictable. A few habits make it even safer:

  • Pick a read-only role. ViewOnlyAccess, ReadOnlyAccess or a custom least-privilege set is ideal for everyday questions, and is all most people need.

  • Know what the role can read. Read-only roles may still reach some sensitive data, such as S3 objects or Lambda environment variables. Output is redacted on a best-effort basis.

  • Treat AWS text as untrusted. Tags, names and log lines can contain odd instructions, so skim results before acting on them.

  • Glance at wide requests. The server asks you to confirm large selections.

Property

What it means

IAM is the only gate

The server does not block writes locally. If your SSO role may delete something, a request can delete it. Use read-only roles in your config unless you truly need more.

Your TOML is the only role selector

Neither the AI model nor a prompt can pick or override a role. Only your environment rules do, and an account that matches no rule is refused (see roles.py).

No surprise browser windows

Only the login tool starts a sign-in. Queries fail with clear instructions instead.

Blast-radius cap

A selection wider than max_accounts (default 200) is refused until you confirm it.

Secrets are redacted

Tokens and keys are scrubbed from logs and tool output (redact.py). Credentials are never written to disk.

Config cannot hold secrets

The loader rejects keys that look like secrets (config.py).

Everything is audited

One line per call in audit.jsonl, with writes flagged.

AWS response data (tags, names, log lines) is untrusted text that reaches the model. This server bounds its size but cannot remove prompt-injection attempts. Details in docs/architecture.md.

Documentation map

Document

Read it if you want to...

docs/how-it-works.md

Understand, step by step and in plain language, what happens from your sentence to the answer

docs/architecture.md

See connectivity diagrams, components, threads, storage and security layers

docs/internals.md

Contributor map of the code: who calls what, every cache and lock, known quirks, debugging cookbook

docs/prompt-guide.md

Learn what to say to your assistant, with copy-paste examples

docs/tools-reference.md

Look up every tool, argument and response shape

docs/configuration.md

Configure orgs, environments, roles and performance

docs/developer-guide.md

Set up, test, extend and release the project

docs/adr/README.md

Understand why a design decision was made, and what it cost

docs/troubleshooting.md

Fix a specific error message

CONTRIBUTING.md

Contribute code: setup, quality gates, commit conventions

SECURITY.md

Report a vulnerability, or review the threat model

examples/README.md

Install and wire up the server in various MCP clients

Project layout

src/aws_sso_mcp/
├── server.py        # MCP tools (thin entry points)
├── fanout.py        # the shared multi-account pipeline
├── planner.py       # which accounts? (selectors -> targets)
├── roles.py         # which role? (deterministic rules)
├── state.py         # per-process session state
├── config.py        # TOML loading and validation
├── audit.py         # append-only JSONL audit log
├── redact.py        # secret scrubbing
├── hardening.py     # output size caps + redaction in one pass
├── logging_setup.py # stderr + chat-UI logging
├── awscfg.py        # cached boto3 client factory
├── errors.py        # clean, user-facing exception types
├── sso/             # login (device_auth), credentials (broker), directory
├── cache/           # SQLite account/role cache
└── engine/          # catalog (one AWS call), dag (multi-step), executor (threads)

Requirements and status

  • Python 3.12 or newer, boto3, and the mcp SDK (see pyproject.toml).

  • Alpha (0.1.x): interfaces may change.

  • Transport is stdio. The server is started by your chat client, one process per session.

Security

Credentials are brokered, never stored on disk; see Safety model and the full threat model in SECURITY.md. Report vulnerabilities privately via GitHub Security Advisories, not public issues.

Releases are built by GitHub Actions with PyPI Trusted Publishing and carry a signed SLSA build-provenance attestation plus a CycloneDX SBOM:

gh attestation verify ./aws_sso_mcp-<version>-py3-none-any.whl --repo arnabdas24/aws-sso-mcp

Contributing

See CONTRIBUTING.md. Every change must pass ruff, mypy --strict, the test suite with its global and per-module coverage floors, and vulture.

License

Apache License 2.0. Releases up to and including 0.1.1 were published under the MIT License and remain available under it.

Available Tools

16 tools
aws_queryA

Run one AWS API call across every account a selector resolves to.

``service``/``operation``/``params`` follow the AWS CLI and botocore shape,
e.g. ``service="ec2"``, ``operation="DescribeInstances"``,
``params={"Filters": [{"Name": "instance-state-name", "Values": ["stopped"]}]}``.
Paginated operations are followed to completion automatically, so results
are complete rather than a silent first page. If a result is truncated,
raise ``max_items`` to collect more from each account.

``region`` is always required and never defaulted. Account selectors decide
which accounts are touched; with none given, every account in the active org
is targeted, subject to the configured blast-radius cap. The model never
picks which IAM role to assume, and neither does the caller: the role comes only
from the ``[portals.<org>.environments.*]`` rules in your TOML config.

Any operation the assumed role may perform is dispatched - reads, writes,
deletes, secret reads, all of it - with AWS IAM as the sole authorization
boundary. Nothing is refused locally; if the role lacks permission, AWS
returns AccessDenied and that is what comes back.

Operations that delete, terminate or detach something (``Delete*``,
``Terminate*``, ``Remove*``, ``Detach*`` and similar) first ask the user to
confirm, listing the accounts and parameters. If they decline, or the client
cannot ask, nothing runs. Do not retry a declined call.

Inputs: region (required), service (required), operation (required), org, +12 more. Returns: object

cache_statusA

Report cache freshness for one org, or every configured org.

Inputs: org. Returns: object

config_showA

Show the resolved, loaded configuration (it never contains secrets).

Inputs: no named parameters. Returns: object

config_validateA

Re-load and validate the config file, reporting errors without applying them.

Inputs: no named parameters. Returns: object

current_orgA

Report the currently active org, if any.

Inputs: no named parameters. Returns: object

explain_role_selectionA

Dry-run deterministic role resolution for one account; makes no service call.

Shows exactly which role the TOML environment rules would assume. The role is never chosen by the model or by the caller, and there is no override argument.

Inputs: account_id (required), account_name (required), org. Returns: object

list_rolesC

List the IAM roles granted to you on one account, via that account's SSO portal.

Inputs: account_id (required), org, force_refresh. Returns: object

loginA

Authenticate to an org via SSO device authorization (opens a browser for approval).

This is the only tool that will ever open a browser. Queries never start a sign-in on your behalf; they fail with a message telling you to call this first, so a browser window never appears unexpectedly mid-query.

Inputs: org. Returns: object

orgs_listA

List every org configured in the aws-sso-mcp TOML config.

Inputs: no named parameters. Returns: object

org_statusB

Report token validity and cache freshness for an org (defaults to the active org).

Inputs: org. Returns: object

perf_statsA

Report cache hit/miss counts and executor sizing for the running server.

Answers "why was that fan-out slow?" without turning on debug logging. A high credential-broker miss rate points to short-lived sessions or role churn; a high role-cache miss rate suggests the directory TTL is too short.

Inputs: no named parameters. Returns: object

refresh_accountsA

Force a refresh of the cached account directory for one org, or every configured org.

Inputs: org, all_orgs. Returns: object

run_dagA

Run a small declarative multi-step query DAG against every resolved account.

Each entry in ``steps`` is
``{"id": ..., "service": ..., "operation": ..., "params": {...},
"extract": "...", "depends_on": [...]}``.

A later step's ``params`` may reference an earlier step's extracted output
anywhere a literal would go, via ``{"$ref": "<step_id>"}``. There is no
expression language and nothing is ever evaluated as code.

Every resolved account runs its own independent copy of the whole DAG, with
one credential mint per account rather than one per step. As with
``aws_query``, AWS IAM is the sole authorization boundary for every step,
and ``max_items`` raises the per-account pagination ceiling for every step.
If any step deletes, terminates or detaches something, the user is asked to
confirm the whole DAG first; if they decline, nothing runs.

Inputs: steps (required), region (required), org, orgs, +10 more. Returns: object

search_accountsB

Search the cached account directory by exact name, substring, or account id.

The cache is refreshed automatically per org if it is missing or older than
the configured TTL. ``name_contains`` is a case-insensitive substring and
behaves identically to the ``name_contains`` selector on ``aws_query``, so
what you find here is exactly what a query will target.

Inputs: org, name, limit, all_orgs, +2 more. Returns: object

token_statusB

Check cached SSO token validity WITHOUT ever opening a browser.

Inputs: org. Returns: object

use_orgB

Set the active org for this session (one-off org= overrides still win).

Inputs: org (required). Returns: object

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    B3.4/5.0

    Scored across 16 tools

    Disambiguation4/5

    Most tools map to clearly distinct actions: aws_query vs run_dag (single call vs multi-step DAG), explain_role_selection vs list_roles (dry-run rule resolution vs listing actual roles), and login vs refresh_accounts are all unambiguous. The main blur is org_status, which duplicates the coverage of token_status and cache_status, leaving an agent unsure which of the three to call. orgs_list/current_org/use_org are clean enough despite being in the same area.

    Naming Consistency3/5

    Multiple conventions coexist: verb_noun (use_org, refresh_accounts, search_accounts, list_roles, run_dag), noun_noun (perf_stats, org_status, token_status, cache_status), noun_verb (orgs_list, config_show, config_validate), and bare nouns (login, current_org). The singular/plural mismatch (orgs_list vs list_roles) and status-noun shortcuts add to the inconsistency. Names remain individually readable, so it is mixed rather than chaotic.

    Tool Count4/5

    16 tools is just above the comfortable band, but the surface splits into coherent groups (auth, org selection, account cache, roles, querying, config, perf) where each group serves a real need. The one clearly redundant entry is org_status, which a trim could fold into token_status/cache_status. Slightly heavy but justified for an SSO + fan-out query server.

    Completeness4/5

    The lifecycle is well covered: login, token_status/org_status for auth health, org selection, account cache refresh/search, role inspection, aws_query and run_dag for reads and writes, plus config show/validate and perf stats. The notable gaps are no logout/token revocation and no config mutation tool, though config editing via a file is arguably out of scope. These are minor, workable omissions rather than dead ends.

    Maintenance

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      D
      maintenance
      Connects AI assistants to AWS infrastructure through AWS IAM Identity Center for secure, natural language management of cloud resources across multiple accounts. It enables users to execute AWS CLI commands, monitor resources, and run remote shell commands on EC2 instances via Systems Manager.
      446 npm
      15
      ISC
    • A
      license
      Not graded
      quality
      B
      maintenance
      Provides a sandboxed environment for AI coding agents to execute TypeScript AWS SDK queries securely, with cross-account aggregation and no host credential exposure.
      20 npm
      7
      Apache 2.0
    • A
      license
      Not graded
      quality
      D
      maintenance
      Enables natural language queries across AWS multi-account security scan history, including infrastructure configurations, public exposures, and organizational relationships.
      MIT