UsageWall
README.md
# UsageWall
> **Live LLM pricing as an MCP server.** Ask Claude (or any MCP client)
> "how much does this prompt cost?" and get real numbers from a hand-checked
> pricing table for every major LLM. Free, read-only, no auth.
[](https://usagewall.vercel.app/mcp)
[](https://usagewall.vercel.app/api/mcp)
[](https://usagewall.vercel.app/#pricing)
[](LICENSE)
[](https://github.com/agenciabakucl-coder/usagewall/actions/workflows/test.yml)
## TL;DR
- **MCP endpoint:** `https://usagewall.vercel.app/api/mcp`
- **Install docs:** [usagewall.vercel.app/mcp](https://usagewall.vercel.app/mcp)
- **Pricing JSON API:** [usagewall.vercel.app/api/pricing](https://usagewall.vercel.app/api/pricing)
- **Live status:** [usagewall.vercel.app/status](https://usagewall.vercel.app/status)
- **What's coming (waitlist):** a hosted hard-cap proxy so a runaway loop can't drain your AI budget overnight.
Quick test:
```bash
curl -s https://usagewall.vercel.app/api/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
```
Install in Claude.ai:
> Settings → Connectors → Add custom connector → paste `https://usagewall.vercel.app/api/mcp`
Install in Claude Desktop / Cursor / Windsurf / Zed / Cline: see
[/mcp](https://usagewall.vercel.app/mcp).
---
## The MCP server
Six read-only tools, two raw-JSON resources, JSON-RPC 2.0 over Streamable HTTP.
Zero dependencies; ~280 LOC; auditable in one sitting.
**Tools** — `list_models`, `get_model`, `compare_models`, `estimate_cost`,
`cheapest_for`, `list_providers`.
**Resources** — `usagewall://pricing/full`, `usagewall://pricing/<provider>`.
**Source** — [`api/mcp.js`](api/mcp.js). **Tests** — [`tests/mcp.test.js`](tests/mcp.test.js)
(18 cases). **Smithery descriptor** — [`smithery.yaml`](smithery.yaml).
---
## What else is in this repo
Production-grade waitlist landing for **usagewall.dev**, plus the local proxy
prototype. Static HTML + 5 Vercel serverless functions. No build step. No framework.
---
## Stack (100% free tier)
| Layer | Service | Free tier |
|---|---|---|
| Frontend hosting | Vercel Hobby | 100GB/mo bandwidth |
| Database | Supabase | 500MB DB + 50k MAU |
| Email confirmation | Resend | 3k emails/mo, 100/day |
| Anti-bot (optional) | Cloudflare Turnstile | 1M challenges/mo |
| CI | GitHub Actions | 2k min/mo (private) / unlimited (public) |
| Domain | Porkbun / Cloudflare | $10–15 / year (only paid item) |
---
## Layout
```
usagewall/
├── public/
│ ├── index.html # Landing — hero, problem, how, why, pricing, waitlist, footer
│ ├── privacy.html # Plain-English privacy page
│ ├── styles.css # Brand: Fraunces + Switzer + JetBrains Mono, dark warm
│ ├── app.js # Vanilla form-binding, fetch to /api/waitlist
│ ├── favicon.svg
│ ├── og.svg / og.png # 1200x630 social preview
├── api/
│ ├── waitlist.js # POST signup endpoint
│ └── unsubscribe.js # GET unsubscribe with HMAC token
├── lib/
│ ├── email.js # RFC-aware email validation + throwaway block
│ └── security.js # rate limit, IP hash, HMAC tokens, safe JSON parse
├── tests/
│ ├── email.test.js # 12 unit tests
│ ├── security.test.js # 23 unit tests
│ └── api.e2e.test.js # 13 end-to-end tests with mocked req/res
├── scripts/
│ ├── validate-html.js # CSP-friendly HTML linter (no inline handlers, alt, lang, etc.)
│ └── smoke-api.js # Live HTTP smoke against waitlist handler
├── supabase/
│ └── schema.sql # waitlist table + RLS deny-all + stats view
├── launch/
│ └── COPY.md # Twitter thread, Show HN, Reddit, IndieHackers, dev.to
├── .github/workflows/
│ └── test.yml # CI: tests + HTML validation + secret-leak grep
├── vercel.json # Security headers, CSP, cache rules
├── package.json # `npm test`, `npm run smoke`, `npm run ci`
├── .env.example
└── README.md
```
---
## First-time setup
### 1. Supabase
- Create a project at [supabase.com](https://supabase.com) (free tier).
- SQL editor → paste `supabase/schema.sql` → run.
- Settings → API → copy `URL` and `service_role` key.
### 2. Generate two secrets (required)
```bash
openssl rand -hex 32 # for IP_HASH_SALT
openssl rand -hex 32 # for UNSUBSCRIBE_SECRET
```
Both must be ≥32 chars. The handler refuses to sign tokens with shorter secrets.
### 3. Resend (optional but recommended)
- Sign up at [resend.com](https://resend.com).
- Add `usagewall.dev` as a domain (DNS records via your registrar).
- API keys → create one. Copy.
### 4. Vercel deploy
```bash
cd usagewall
vercel link # creates .vercel/, do not commit
vercel env add SUPABASE_URL
vercel env add SUPABASE_SERVICE_ROLE_KEY
vercel env add IP_HASH_SALT
vercel env add UNSUBSCRIBE_SECRET
vercel env add RESEND_API_KEY # optional
vercel env add RESEND_FROM # optional
vercel env add PUBLIC_URL # https://usagewall.dev
vercel env add ALLOWED_ORIGIN # https://usagewall.dev
vercel deploy --prod
```
### 5. DNS for usagewall.dev
Buy at [Porkbun](https://porkbun.com) (~$15/yr for `.dev`). Point at Vercel:
- A record `@` → `76.76.21.21`
- CNAME `www` → `cname.vercel-dns.com`
---
## Local dev
```bash
cp .env.example .env.local
# Fill in at least: SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY,
# IP_HASH_SALT, UNSUBSCRIBE_SECRET
npx vercel dev
# → http://localhost:3000
```
To test without Vercel CLI, use the smoke server:
```bash
npm run smoke
# Hits the handler with stubbed Supabase, prints pass/fail per case.
```
---
## Tests
```bash
npm test # unit + E2E (48 + 13 = 61 cases, no network)
npm run validate:html # HTML structure + CSP + a11y checks
npm run smoke # live HTTP smoke vs handler with stubbed Supabase
npm run ci # all of the above
```
CI runs on every push that touches `usagewall/**` (see `.github/workflows/test.yml`).
---
## Security & privacy
- **No raw IPs stored.** Only `HMAC-SHA256(ip, IP_HASH_SALT)` first 64 bits.
- **No PII in production logs.** Email addresses are redacted to `email_domain` only.
- **CSP at meta + headers.** `default-src 'self'`, scripts only from self.
- **HSTS preload.** All TLS, no opt-out.
- **HMAC-signed unsubscribe tokens.** No DB lookup needed to verify.
- **Honeypot field** on every form — invisible to humans, irresistible to bots.
- **Rate limit** 5 req/min per IP-hash (in-memory; Turnstile handles the rest).
- **Body cap** 8KB on the API. `safeJsonParse` never throws.
- **GitHub Actions** pin actions to commit SHA + grep for leaked secrets.
Read `public/privacy.html` for the user-facing policy.
---
## Validation goals (5 days)
| Day | Action | Channel |
|---|---|---|
| 0 | Buy domain + deploy | — |
| 1 | Twitter thread (see `launch/COPY.md`) | X / Twitter |
| 2 | Show HN + Reddit (×3 subs) | HN, r/SaaS, r/SideProject, r/IndieHackers |
| 3 | IndieHackers post | IndieHackers |
| 4 | dev.to long-form article | dev.to |
| 5 | **Measure** | — |
GO if **≥100 emails** and **≥1 reply** asking "when can I pay?"
NO-GO if **<30 emails** after the launch thread.
---
## Brand notes
This is **not** the Baku brand — it's UsageWall, a product *by* Baku.
Inherits the design DNA (dark warm, editorial serifs, no flashy gradients,
generous spacing) but lives independently with its own identity.
Footer credits Baku.
Palette:
- bg `#0A0A0B` (warm near-black)
- text `#F5F4F0` (cream off-white)
- accent `#E8DFC7` (warm cream — highlights only)
- code bg `#0E0E10`
Type:
- Display: Fraunces 400/500, often italic
- Body: Switzer 400/500
- Mono: JetBrains Mono 400/500/600
---
## Files NOT to commit
The `.gitignore` covers it, but be paranoid about:
- `.env`, `.env.local`, `.env.*.local`
- `.vercel/` (project link)
- `node_modules/`
The CI grep checks for accidental commits of `sk_live_…`, `re_…`, `sbp_…`.
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues