Skip to main content
Glama
sahaniaditya

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).