Docs

Quickstart: the free MCP server

spendsaw-mcp runs on your machine with your own AWS profile. No hosted backend, no account, and the server makes no model call of its own.

What it does

The server scans the account behind your AWS profile, prices what it finds, and hands the result to the assistant you already use. It never changes anything in AWS. When you ask how to fix something, it prints the commands as text for you to review and run yourself.

Savings from the MCP server are estimates, so it says "about" and never "verified". Verified savings come from the ledger in the full product, which is not in the hosted service yet.

ToolWhat it returns
list_findingsFindings, largest monthly saving first. Ask "what are my top 5 savings?"
explain_findingEvidence, the cost and where that number comes from, the risk tier, and the exact AWS CLI commands to fix it
get_costsBilled cost from Cost Explorer by service, account, region or usage type
bedrock_usageBedrock tokens by model, billed cost by usage type, and which Lambda functions and ECS services can call Bedrock

Install

Not yet published. spendsaw-mcp is not on PyPI yet, so uvx spendsaw-mcp and pipx install spendsaw-mcp will not find it. The source repository is not public yet either. Until one of them is, the server can only be installed from a checkout; write to hello@spendsaw.com for early access.

You need Python 3.10 or newer and working AWS credentials.

From a checkout of the repository, run this in the repository root:

pip install ./spendsaw_mcp

To build a wheel you can install on another machine, build the wheel only. A source distribution cannot be built, because the package bundles the scanner code that sits beside it in the repository.

uv build spendsaw_mcp --wheel

Once the package is on PyPI:

uvx spendsaw-mcp          # or: pipx install spendsaw-mcp

Either way the command on your path is spendsaw-mcp.

Connect your assistant

The examples use uvx spendsaw-mcp, which needs the published package. If you installed from a checkout, use spendsaw-mcp as the command with no arguments.

Claude Code

claude mcp add spendsaw --env AWS_PROFILE=default --env AWS_REGIONS=us-east-1 -- uvx spendsaw-mcp

Or as .mcp.json in a project:

{
  "mcpServers": {
    "spendsaw": {
      "type": "stdio",
      "command": "uvx",
      "args": ["spendsaw-mcp"],
      "env": { "AWS_PROFILE": "default" }
    }
  }
}

Claude Desktop and Cursor

Claude Desktop (claude_desktop_config.json) and Cursor (~/.cursor/mcp.json or .cursor/mcp.json) use the same block:

{
  "mcpServers": {
    "spendsaw": {
      "command": "uvx",
      "args": ["spendsaw-mcp"],
      "env": {
        "AWS_PROFILE": "default",
        "AWS_REGIONS": "us-east-1,us-west-2"
      }
    }
  }
}

First run

  1. Ask: "What are my top 5 savings?"
  2. The first call scans the account. With every enabled region this takes a few minutes; set AWS_REGIONS to make it faster.
  3. Ask the assistant to explain a finding by its id to see the evidence and the commands.

Settings

The server reads the standard AWS credential chain.

VariableDefaultMeaning
AWS_PROFILEdefault chainWhich credentials to scan with
AWS_REGIONSall enabledRegions to scan, comma-separated
SPENDSAW_MCP_CACHE_TTL_SECONDS21600How long a scan is reused, in seconds
SPENDSAW_MCP_CACHE_DIR~/.cache/spendsaw-mcpWhere scans and prices are cached

If you used the earlier package name, the old COST_MCP_CACHE_TTL_SECONDS and COST_MCP_CACHE_DIR variables still work; the new names win when both are set.

Caching and cost

  • A scan is cached on disk per account for six hours. Pass refresh: true to a tool to scan again.
  • The cache holds resource ids, names and tags, and stays on your machine.
  • get_costs and bedrock_usage call Cost Explorer, which AWS bills at $0.01 per request.

IAM policy

The scanner only reads. This is the full set of actions it calls. Attach it to the profile you run the server with. A scanner whose permission is missing is skipped and the rest still run, so you can start narrower.

{
  "Version": "2012-10-17",
  "Statement": [{
    "Sid": "SpendsawMcpReadOnly",
    "Effect": "Allow",
    "Action": [
      "sts:GetCallerIdentity",
      "ce:GetCostAndUsage",
      "ce:GetDimensionValues",
      "pricing:GetProducts",
      "tag:GetResources",
      "cloudwatch:GetMetricStatistics",
      "cloudwatch:ListMetrics",
      "cloudtrail:LookupEvents",
      "ec2:DescribeRegions",
      "ec2:DescribeInstances",
      "ec2:DescribeVolumes",
      "ec2:DescribeSnapshots",
      "ec2:DescribeImages",
      "ec2:DescribeAddresses",
      "ec2:DescribeNatGateways",
      "elasticloadbalancing:DescribeLoadBalancers",
      "elasticloadbalancing:DescribeTargetGroups",
      "elasticloadbalancing:DescribeTargetHealth",
      "rds:DescribeDBInstances",
      "s3:ListAllMyBuckets",
      "s3:GetBucketLocation",
      "s3:GetLifecycleConfiguration",
      "s3:GetIntelligentTieringConfiguration",
      "logs:DescribeLogGroups",
      "lambda:ListFunctions",
      "elasticache:DescribeCacheClusters",
      "es:ListDomainNames",
      "es:DescribeDomain",
      "dynamodb:ListTables",
      "dynamodb:DescribeTable",
      "redshift:DescribeClusters",
      "ecs:ListClusters",
      "ecs:ListServices",
      "ecs:DescribeServices",
      "ecs:DescribeTaskDefinition",
      "kinesis:ListStreams",
      "kinesis:DescribeStreamSummary",
      "glue:GetJobs",
      "glue:GetJobRuns",
      "sagemaker:ListEndpoints",
      "sagemaker:ListNotebookInstances",
      "bedrock:ListProvisionedModelThroughputs",
      "bedrock:ListCustomModels",
      "bedrock:ListModelCustomizationJobs",
      "bedrock:ListGuardrails",
      "bedrock:ListAgents",
      "bedrock:ListKnowledgeBases",
      "cloudfront:ListDistributions",
      "apigateway:GET",
      "route53:ListHostedZones",
      "secretsmanager:ListSecrets",
      "iam:ListAttachedRolePolicies",
      "iam:ListRolePolicies",
      "iam:GetRolePolicy"
    ],
    "Resource": "*"
  }]
}

What the assistant sees

Resource names, tags and descriptions are written by whoever can create resources in your account. Before they leave the server they are flattened, stripped of control characters and truncated, and the server tells the client to treat them as data. The results go to the assistant you connected, so that assistant's provider sees them. Review any command before you run it.