Skip to main content
Glama
santimentasti

mcp-server-billing

README.md
# mcp-server-billing

MCP server that gives an AI agent guarded access to a B2B SaaS billing database
(customers, subscriptions, invoices, credit notes) and live ECB exchange rates. Six
tools: five reads covering the whole billing surface, one carefully fenced write.
Runs locally over stdio against docker-compose Postgres, or on AWS behind a Lambda
Function URL for about **$7.67/month**.

Full design rationale, protocol-version notes, and every decision's trade-off:
**[SPEC.md](./SPEC.md)**. Implementation conventions and the code review checklist:
**[CODING_STYLE.md](./CODING_STYLE.md)**.

## Tools

| Tool | Purpose | Read-only | Idempotent |
|---|---|:--:|:--:|
| `billing_search_customers` | Find customers by name, email, domain, status, country | ✓ | ✓ |
| `billing_get_customer` | One customer with subscriptions and open balance | ✓ | ✓ |
| `billing_list_invoices` | Invoices filtered by customer, status, date range | ✓ | ✓ |
| `billing_revenue_summary` | Monthly MRR/ARR, new vs. churned, by plan or country | ✓ | ✓ |
| `billing_convert_currency` | Convert an amount via live ECB rates ([frankfurter.dev](https://frankfurter.dev)) | ✓ | ✗ |
| `billing_issue_credit_note` | **The one write.** Credit an open invoice, capped and idempotency-keyed | ✗ | ✓ |

Plus one resource (`billing://schema`, the live DDL) and one prompt (`revenue_review`,
a guided monthly review). Full schemas, annotations, and example calls: [SPEC.md §5](./SPEC.md#5-tool-inventory).

## Run it (60 seconds)

```bash
docker compose up -d && npm run db:seed
npm run build
cp .env.example .env   # fill in MCP_BEARER_TOKEN if you'll also try http/lambda
npm run inspect
```

The Inspector opens in your browser against the six tools, live, against seeded data
(~200 customers, ~2,000 invoices). **Requires Node ≥22.19** for the Inspector itself
(this repo's own code targets Node ≥20 — the gap is the Inspector CLI, not the
server; see [SPEC.md §9](./SPEC.md#9-testing) for what was verified without it).

## Wire it to Claude Desktop

```json
{
  "mcpServers": {
    "billing": {
      "command": "node",
      "args": ["/absolute/path/to/mcp-server-aws/dist/stdio.js"],
      "env": {
        "DATABASE_URL_READER": "postgres://mcp_reader:reader_local_pw@localhost:5434/billing",
        "DATABASE_URL_WRITER": "postgres://mcp_writer:writer_local_pw@localhost:5434/billing"
      }
    }
  }
}
```

Use an absolute path — Claude Desktop does not resolve `~` or relative paths.

## Deploy to AWS

```bash
npm run lambda:prepare                 # builds + stages dist/ + prod deps + db/init/*.sql
cd terraform
cp terraform.tfvars.example terraform.tfvars   # fill in mcp_bearer_token
terraform init && terraform apply
terraform output -raw migrate_command | bash   # applies schema, rotates role passwords
```

| | $/month |
|---|---|
| RDS `db.t4g.micro`, 11h/day (power-scheduled) | 5.35 |
| gp3 storage, 20 GB | 2.30 |
| Lambda + Function URL, VPC, SSM | ~0.02 |
| **Total** | **≈ 7.67** |

No NAT Gateway: Lambda reaches [frankfurter.dev](https://frankfurter.dev) over IPv6
through an egress-only internet gateway (verified live — Frankfurter has AAAA records)
instead of a ~$32.85/month NAT Gateway. `terraform destroy` takes it to $0. Full cost
breakdown and the architecture diagram: [SPEC.md §3](./SPEC.md#3-architecture).

## Eval questions

Five questions an agent should answer using only these tools — multi-tool, verifiable
against the seed data, one deliberately negative (does the agent respect the credit
cap, or try to split it?). [SPEC.md §9](./SPEC.md#9-testing).

## Development

```bash
npm test              # unit + integration (needs docker compose up -d)
npm run lint           # eslint
npm run typecheck      # tsc --noEmit
npm run format:check   # prettier
```

Before opening a PR, run the [code review checklist](./CODING_STYLE.md#12-code-review-checklist).