Forward a receipt to your inbox and it's filed automatically.
git clone https://github.com/assaf/expense{
"mcpServers": {
"expense": {
"command": "node",
"args": ["/path/to/expense/dist/index.js"],
"env": {
"DATABASE_URL": "<database_url>",
"RESEND_API_KEY": "<resend_api_key>",
"INBOUND_EMAIL_WEBHOOK_SECRET": "<inbound_email_webhook_secret>",
"DEEPSEEK_API_KEY": "<deepseek_api_key>"
}
}
}
}DATABASE_URLRESEND_API_KEYINBOUND_EMAIL_WEBHOOK_SECRETDEEPSEEK_API_KEYResumen de MCP Servers
# Expense
> Expense is a receipt tracker with no friction: receipts arrive by email and file themselves, mileage prices itself from IRS rates, and an AI assistant does the data entry you'd rather skip — capture, log, and ask, all through MCP.
## Screenshots


What it does:
- Log expenses two ways: receipt-based (upload/scan an image, add date, report,
category, merchant, amount) and mileage (drive route on a map — Leaflet + OSRM
— with configurable per-year mileage rates)
- **Receipts by email**: forward a receipt to your inbox address and it's
parsed (merchant/amount/category) and added automatically — see below
- Organize into reports and categories; every receipt image is stored and
auto-renamed to a convention (YYYY-MM-DD_Report_Name.jpg)
- Export: PDF per report (with embedded receipt images) and a ZIP of everything
- Settings: reports, categories, mileage rates, home location for mileage routes
Stack:
- React Router v8 (framework mode) + Tailwind v4, TypeScript
- Postgres via Prisma — accounts, users, expenses, reports, categories,
settings, mileage, image blobs
- Images: Postgres BYTEA (prod and dev/test) — no external store
- Deployed on Vercel + Neon (git push to main auto-deploys)
Auth & accounts (the recent work):
- Email/password login (scrypt-hashed), sessions via signed cookies
- Multi-user accounts: each account has its own data, users in the same account
share everything, other accounts fully isolated
- New users join an account via an 8-char invite code (shown in Settings,
regenerable); anyone can self-signup into a fresh account
- Image keys are namespaced per account so two accounts can never collide
## Accounts & sharing
Login is email/password (scrypt-hashed) — the email is the login name, stored
lowercase and unique, format-validated at signup/join. Every expense, report,
category, and setting belongs to an **account**; everyone in an account shares
them, and accounts are fully isolated from each other.
- **Sign up** → creates a brand-new account (starts empty).
- **Join** → enter an account's invite code (Settings → Account) to share
that account's data.
- The first account/user is bootstrapped from `APP_EMAIL`/`APP_PASSWORD`
when the database is empty; pre-email accounts get their login backfilled
from `APP_EMAIL` on first start (initStore).
## SEO & AI discovery
The public marketing pages double as the site's AI-search surface: when
someone asks an assistant for a free expense tracker, GPTBot / OAI-SearchBot /
ClaudeBot / PerplexityBot crawl and quote them. The copy is written as
standalone, quotable answers that name the app and its URL, and it lives in
ONE place — `app/lib/seo-content.ts` — which renders every surface:
| Page | Purpose |
| ---------------------------------------- | -------------------------------------------------------------- |
| `/` | Landing page (SoftwareApplication JSON-LD) |
| `/about` | Full feature/benefit list (AboutPage JSON-LD) |
| `/faq` | 13 Q&As matching real AI queries (FAQPage JSON-LD) |
| `/alternatives` | Expense vs Expensify comparison (WebPage + FAQPage JSON-LD) |
| `/llms.txt` | The llmstxt.org file — the curated overview AI assistants read |
| `/about.md` `/faq.md` `/alternatives.md` | Markdown mirrors per the llms.txt convention |
Supporting plumbing: `public/robots.txt` explicitly allows the AI crawlers
(GPTBot, OAI-SearchBot, ChatGPT-User, ClaudeBot / Claude-SearchBot /
Claude-User, PerplexityBot / Perplexity-User, Google-Extended,
Applebot-Extended, meta-externalagent) while app routes stay blocked, and
`public/sitemap.xml` lists the public pages.
These routes are public (see the root loader in `app/root.tsx`); everything
else still requires a session.
## What it does
- Track **receipt** expenses (date, merchant, amount, image, category, report)
and **mileage** expenses (date, 2+ addresses, distance, amount, report).
- Mileage routes run **Home → stops → Home**; distance is computed via OSRM and
the amount from a per-year mileage rate. Maps use Leaflet + OpenStreetMap —
**no API keys required**.
- Incomplete expenses are highlighted so they're easy to finish.
- Paste (⌘V) or upload an image anywhere to start a new receipt.
- **Export**: each report as a PDF (grouped by category, with all receipt
images), or everything as a ZIP (CSV + images named `YYYY-MM-DD_REPORT_FILE.ext`).
- **AI assistants (MCP)**: any MCP client — Claude Code, Claude Desktop,
Cursor — connects by signing in with your account (OAuth; no API keys).
See [AI assistants](#ai-assistants-mcp) below.
## AI assistants (MCP)
The app speaks the Model Context Protocol at `https://expense.labnotes.org/mcp`
(auth: OAuth 2.1 authorization-code + PKCE — sign in with your account and
approve the connection; no API keys). An assistant connected to your account
can:
- **Capture a receipt** — a photo or PDF in the chat runs the same OCR +
extraction pipeline as the web app and is categorized from your own
merchant history.
- **Log a drive** — give it stops in plain English; it geocodes, routes, and
prices the trip at the year's IRS rate.
- **Answer spending questions** — "how much did I spend on flights last
quarter?" gets the exact total from your data.
- **Build reports** — create/close reports, move expenses into them, export
a report PDF.
- **Reconcile** — paste a bank statement CSV; it finds every charge with no
matching receipt (read-only).
Connect any MCP client:
```json
// Claude Code — .mcp.json (no headers needed: the client discovers OAuth)
{
"mcpServers": {
"expense": {
"type": "http",
"url": "https://expense.labnotes.org/mcp"
}
}
}
```
The client opens your browser; you sign in and click Allow. Manage
connections (per-token delete, full disconnect) in **Settings → Agents &
API (MCP)**. See [`docs/mcp.md`](docs/mcp.md) for the full reference and
[`docs/mcp-directories.md`](docs/mcp-directories.md) for the directory
listings.
## State
Storage is Postgres-only via **Prisma** (`prisma/schema.prisma` is the
single schema source of truth; the client is generated to
`prisma/generated` by `pnpm build:prisma`). `DATABASE_URL` is required at
startup (the app exits with a clear error otherwise). Receipt images live in
Postgres BYTEA (`image_blobs`) in prod and dev — no separate storage service.
| Data | Images |
| --------------------------- | ------------------------------ |
| `accounts` / `users` / | Postgres BYTEA (`image_blobs`, |
| `expenses` / `reports` / | prod and dev) |
| `categories` / `settings` / | |
| `mileage` / `image_blobs` | |
All reads/writes go through `app/lib/store.server.ts` (→
`app/lib/database.ts`, Prisma queries scoped by `accountId`); image storage
is behind `app/lib/images.server.ts` (Prisma `imageBlob`).
Keys are `images/{accountId}/...` pathnames on every backend — namespaced
per account so two accounts can never collide on the same filename.
Schema changes: edit `prisma/schema.prisma`, then `prisma migrate dev --name …` locally and
`pnpm db:push` (or `pnpm db:migrate`) before deploying.
### `data/` — removed
The file-era migration source (`data/*.csv` + `data/images/*` and the
`pnpm migrate-data` one-off) was deleted in the Jul 2026 cleanup — data now
lives in the database, and importing from Expensify happens via
`scripts/import-expensify.ts`. Cloning prod uses `scripts/clone`
(`prisma/backup.sql`).
## Quick start
## Environment variables
Load order: real `process.env` (Vercel dashboard, or inline) wins; a local
`.env` file fills the gaps. `DATABASE_URL` is required; `.env` is gitignored. if
(!hasDatabase()) {
**dev / test — local `.env`:**
```bash
# .env (project root, gitignored)
DATABASE_URL=postgres://assaf@localhost/expense_dev # include the local user
SESSION_SECRET=… # signs the session cookie (random hex)
APP_EMAIL=… # bootstrap: first account's email (empty DB only)
APP_PASSWORD=… # bootstrap: first account's password (empty DB only)
# Receipts by email (all optional):
# RESEND_API_KEY=re_… INBOUND_EMAIL_WEBHOOK_SECRET=whsec_…
# INBOUND_EMAIL_ADDRESS=receipts@example.com # forwarding + reply sender
# DEEPSEEK_API_KEY=sk-… RECEIPT_OCR_MODE=auto
```
On an empty database the first account + user are bootstrapped from
`APP_EMAIL`/`APP_PASSWORD` (fail-closed if missing); afterwards users are
created through the app's signup/join flow. `SESSION_SECRET` is always
required. `APP_EMAIL`/`APP_PASSWORD` can be removed from `.env` once you
have at least one user.
Accounts created before email login (username era) keep their old username
as the stored email until `APP_EMAIL` is set — `initStore` then backfills
that address onto the bootstrap (oldest) user, so the configured credentials
keep working.
Tests intentionally hardcode `expense_test` (Postgres incl. image blobs),
ignore the local database, and reset the schema from Prisma on each run
(`pnpm test:db:push` in the test setup).
**prod — Vercel:** set env vars in the project dashboard (Settings →
Environment Variables): `DATABASE_URL` (Vercel Postgres / Neon pooled URL),
`SESSION_SECRET`,
and (only until the first user exists) `APP_EMAIL` / `APP_PASSWORD`.
Vercel injects them at runtime; `.env` never exists there.
## Receipts by email
Forward a receipt email to your inbox address and it's parsed and added
automatically: the merchant, amount, and caLo que la gente pregunta sobre expense
¿Qué es assaf/expense?
+
assaf/expense es mcp servers para el ecosistema de Claude AI. Forward a receipt to your inbox and it's filed automatically. Tiene 1 estrellas en GitHub y se actualizó por última vez today.
¿Cómo se instala expense?
+
Puedes instalar expense clonando el repositorio (https://github.com/assaf/expense) o siguiendo las instrucciones del README en GitHub. ClaudeWave también te ofrece bloques de instalación rápida en esta misma página.
¿Es seguro usar assaf/expense?
+
assaf/expense aún no ha sido auditado por nuestro agente de seguridad. Revisa el repositorio original en GitHub antes de usarlo en producción.
¿Quién mantiene assaf/expense?
+
assaf/expense es mantenido por assaf. La última actividad registrada en GitHub es de today, con 0 issues abiertos.
¿Hay alternativas a expense?
+
Sí. En ClaudeWave puedes explorar mcp servers similares en /categories/mcp, ordenados por popularidad o actividad reciente.
Despliega expense en tu cloud
Lleva este repo a producción en minutos. Cada plataforma genera su propio entorno con variables de entorno editables.
¿Mantienes este repo? Añade un badge a tu README
Pega el badge en tu README de GitHub para mostrar que está auditado por ClaudeWave. Cada badge enlaza de vuelta a esta página y muestra el Trust Score actual.
[](https://claudewave.com/repo/assaf-expense)<a href="https://claudewave.com/repo/assaf-expense"><img src="https://claudewave.com/api/badge/assaf-expense" alt="Featured on ClaudeWave: assaf/expense" width="320" height="64" /></a>Más MCP Servers
Fair-code workflow automation platform with native AI capabilities. Combine visual building with custom code, self-host or cloud, 400+ integrations.
User-friendly AI Interface (Supports Ollama, OpenAI API, ...)
An open-source AI agent that brings the power of Gemini directly into your terminal.
The fastest path to AI-powered full stack observability, even for lean teams.
Real-time global intelligence dashboard. AI-powered news aggregation, geopolitical monitoring, and infrastructure tracking in a unified situational awareness interface
🕷️ An adaptive Web Scraping framework that handles everything from a single request to a full-scale crawl!