Expense MCP
by sahaniaditya
README.md
# Expense MCP
Open-source expense tracker with two parts:
1. A [FastMCP](https://gofastmcp.com) server that stores expenses in Postgres (Supabase).
2. An Expo Android app that acts as an **MCP host**. You can speak or type an expense; Claude calls the MCP tools for you.
Live MCP endpoint:
```
https://indirect-olive-catshark.fastmcp.app/mcp
```
Repo: [github.com/sahaniaditya/expense-mcp](https://github.com/sahaniaditya/expense-mcp)
## Features
- `add_expense` — save an amount, category, date, and optional description
- `get_expenses` — list expenses in a date range
- `data://categories` — groceries, transport, education, entertainment, health, other
- Voice input on Android (“I spent 250 on groceries”)
- Horizon / FastMCP Cloud OAuth for the expense server
- Optional Google Calendar and Gmail from the Android app (draft-only Gmail; Meet via Calendar)
- GitHub Actions workflow that builds a standalone APK
## Architecture
```
Android app (MCP host)
→ Claude (Anthropic API)
→ expense MCP (FastMCP Cloud / Horizon)
→ Supabase Postgres
→ Google Calendar API (on-device, after Google sign-in)
→ Gmail API (on-device, draft-only)
```
The phone app does not talk to the database directly. Claude decides which tool to call; the app runs expense tools against FastMCP Cloud, and Calendar/Gmail tools against Google’s REST APIs on the phone.
## MCP server
### Requirements
- Python 3.14+
- [uv](https://docs.astral.sh/uv/)
- A Postgres database (Supabase works)
### Database
Create an `expenses` table:
```sql
create table if not exists expenses (
id uuid primary key,
price integer not null,
category text not null,
date timestamptz not null,
description text
);
```
### Environment
Copy `.env.example` to `.env` and set your connection string:
```bash
SUPABASE_URL=postgresql://postgres:YOUR_PASSWORD@db.YOUR_PROJECT.supabase.co:5432/postgres
```
Never commit `.env`.
### Run locally
```bash
uv sync
uv run python main.py
```
The HTTP MCP endpoint is `http://127.0.0.1:8000/mcp`.
Inspector:
```bash
uv run fastmcp dev inspector main.py
```
### Deploy
Push this repo to GitHub, then create a project on [Prefect Horizon](https://horizon.prefect.io) / FastMCP Cloud with entrypoint `main.py:mcp`. Add `SUPABASE_URL` as a secret in the deployment settings.
Clients connect to:
```
https://YOUR-PROJECT.fastmcp.app/mcp
```
Horizon authentication is on by default. Use a Horizon API key (`fmcp_...`) or complete the OAuth sign-in from a client.
## Android app
Source lives in [`mobile/`](mobile/).
### Try with Expo Go (development)
Play Store Expo Go must match **SDK 54**.
```bash
cd mobile
npm install
npx expo start
```
You still need a computer running Metro. For a phone-only install, use the APK.
### App setup
1. Open **Connect**.
2. Confirm the FastMCP Cloud URL (already prefilled for the hosted server).
3. Paste an [Anthropic API key](https://console.anthropic.com/).
4. Tap **Sign in and connect** and finish Horizon login (or paste a `fmcp_` API key).
5. Optional: tap **Sign in with Google** for Calendar and Gmail tools.
6. Use **Speak** or **Add**.
The app stores the session and reconnects on the next launch.
### Google Calendar and Gmail
Google’s official Calendar/Gmail MCP servers are a Workspace developer preview and often blocked for personal Gmail. The Android app instead calls the **Calendar and Gmail REST APIs** on the phone with the signed-in Google token. Expense tools stay on FastMCP Cloud. Calendar/Gmail tools appear after Google sign-in (prefixed `calendar__` and `gmail__`).
**Limits**
- Gmail can search, read, and **create drafts only**. It cannot send. After a draft is created, send it from Gmail.
- There is no separate Meet API in the app. Ask to create an event with a Meet link (`calendar__create_event` with `with_meet`).
**Google Cloud (one-time)**
1. Create a GCP project (or reuse one).
2. Enable the product APIs:
```bash
gcloud services enable \
calendar-json.googleapis.com \
gmail.googleapis.com \
--project=YOUR_PROJECT_ID
```
3. Configure the OAuth consent screen (External is fine for a personal app). Add yourself as a test user.
4. Add scopes:
- `https://www.googleapis.com/auth/calendar.calendarlist.readonly`
- `https://www.googleapis.com/auth/calendar.events.freebusy`
- `https://www.googleapis.com/auth/calendar.events.readonly`
- `https://www.googleapis.com/auth/calendar.events`
- `https://www.googleapis.com/auth/gmail.readonly`
- `https://www.googleapis.com/auth/gmail.compose`
5. Create an OAuth client of type **Web application**.
6. Do **not** use `auth.expo.io` or `expensemcp://google`. Gmail and Calendar are sensitive scopes, so Google blocks Expo’s proxy (`expo.io has not completed the Google verification process`).
- Authorized JavaScript origins: `https://sahaniaditya.github.io`
- Authorized redirect URIs: `https://sahaniaditya.github.io/expense-mcp/oauth-callback.html`
7. On the OAuth consent screen **Audience**, add your Gmail (`adityasahani93@gmail.com` or whatever you sign in with) as a **test user**. The app stays in Testing until Google verifies it.
8. Enable GitHub Pages on this repo (Settings → Pages → Deploy from `main`, folder `/docs`) so that callback URL exists. The page is [`docs/oauth-callback.html`](docs/oauth-callback.html).
9. `expo.extra.googleRedirectUri` in [`mobile/app.json`](mobile/app.json) must match the Google Cloud redirect URI exactly.
10. In the app **Connect** tab, paste the Google web client ID and secret, then tap **Sign in with Google**. They are stored on the phone, not in git.
OAuth apps left in **Testing** expire refresh tokens after 7 days. Re-sign in, or publish the OAuth app.
**App setup (Google)**
1. Connect tab → paste Google web client ID and secret.
2. Tap **Sign in with Google**.
3. Connect tab → **Sign in with Google**.
4. Confirm Calendar and Gmail show as connected and their tools appear in the list.
### Build an APK
GitHub Actions produces a release APK (JS bundle included, no Metro required):
1. Push to `main` or `apk-github-actions`, or run the **Android APK** workflow from the Actions tab.
2. Download the **expense-mcp-apk** artifact.
3. Unzip and install `app-release.apk`.
4. Uninstall any previous debug build first.
Local EAS (optional, needs an Expo account):
```bash
cd mobile
npx eas-cli login
npx eas-cli build -p android --profile preview
```
## Project layout
```
main.py Expense MCP tools and resource
connection.py Supabase / Postgres connection
mobile/ Expo SDK 54 Android MCP host
mobile/src/mcpHost.ts Routes tools to expense, Calendar, and Gmail MCP servers
.github/workflows/ APK build
```
## License
MIT. See [LICENSE](LICENSE).
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues