Skip to main content
Glama
Soumya26Agrawal

Expense Tracker MCP Server

README.md
# Expense Tracker MCP Server

A [Model Context Protocol](https://modelcontextprotocol.io/) (MCP) server that lets Claude track, list, and summarize personal expenses through natural conversation — no spreadsheet or app UI required. Built with [FastMCP](https://gofastmcp.com/), backed by SQLite, and deployable both locally and to FastMCP Cloud.

## Why this project

MCP is how LLMs like Claude connect to real tools and data sources instead of just generating text. This project implements a complete, working MCP server end-to-end: tool definitions, a structured resource, an async database layer, and a client-connection story — from local `stdio` transport all the way to a cloud-hosted HTTP deployment proxied back into Claude Desktop.

## What it does

Once connected, Claude can:
- **Log an expense** — "I spent ₹450 on groceries today" → written to the database with date, amount, category, subcategory, and note
- **List expenses** — "What did I spend last week?" → returns every expense in a date range
- **Summarize spending** — "How much did I spend on food this month?" → aggregates total amount and count per category over a date range
- **Discover valid categories** — Claude reads a structured category resource to know what categories/subcategories are valid before logging an expense, rather than guessing

## MCP Components

### Tools (`main.py`)
| Tool | Description |
|---|---|
| `add_expense(date, amount, category, subcategory, note)` | Inserts a new expense row; returns the new row's ID or a structured error |
| `list_expenses(start_date, end_date)` | Returns all expenses in an inclusive date range, most recent first |
| `summarize(start_date, end_date, category=None)` | Returns total amount and count per category in a date range, optionally filtered to one category |

### Resource
- **`expense:///categories`** — exposes `categories.json`, a structured taxonomy of **20 top-level categories** (food, transport, housing, utilities, health, education, family & kids, entertainment, shopping, subscriptions, personal care, gifts & donations, finance fees, business, travel, home, pet, taxes, investments, misc), each with realistic subcategories. Falls back to a sensible default category list if the file isn't found.

### Data layer
- SQLite database (`expenses.db`), with `WAL` journal mode for better concurrent read/write behavior
- Synchronous `sqlite3` used once at startup to initialize the schema and verify write access; all runtime tool calls use `aiosqlite` for non-blocking async I/O
- Database path resolved via the system temp directory, making the server safe to run in ephemeral/cloud filesystem environments

### Transport & Deployment (`proxy.py`)
- The server runs over **Streamable HTTP** when deployed (`mcp.run(transport="http", ...)`)
- A separate **proxy** (`proxy.py`) wraps the deployed FastMCP Cloud endpoint and re-exposes it over **STDIO**, which is the transport Claude Desktop expects for local MCP connections — bridging a cloud-hosted server into a local desktop client

## Architecture

```mermaid
flowchart LR
    subgraph Local["Local Machine"]
        CD[Claude Desktop] -->|STDIO| PX["proxy.py<br/>FastMCP.as_proxy"]
    end

    PX -->|Streamable HTTP| Cloud

    subgraph Cloud["FastMCP Cloud"]
        SRV["main.py<br/>FastMCP Server"]
        SRV --> T1[add_expense]
        SRV --> T2[list_expenses]
        SRV --> T3[summarize]
        SRV --> R1["expense:///categories<br/>resource"]
    end

    T1 --> DB[(SQLite<br/>expenses.db<br/>WAL mode)]
    T2 --> DB
    T3 --> DB
    R1 --> CAT[categories.json]
```

## Tech Stack

| Layer | Technology |
|---|---|
| Protocol | Model Context Protocol (MCP) |
| Server framework | FastMCP |
| Database | SQLite, `aiosqlite` (async), `sqlite3` (sync init) |
| Transport | Streamable HTTP (cloud), STDIO (local proxy → Claude Desktop) |
| Deployment | FastMCP Cloud |
| Package management | `uv` (`pyproject.toml` + `uv.lock`) |
| Language | Python 3.11+ |

## Getting Started

### Prerequisites
- Python 3.11+
- [`uv`](https://docs.astral.sh/uv/) for dependency management
- Claude Desktop (to connect via the local proxy)

### Installation

```bash
git clone <your-repo-url>
cd <repo-name>

uv sync
```

### Run the server locally

```bash
uv run main.py
```

The server starts on `http://0.0.0.0:8000` using Streamable HTTP transport, and initializes the SQLite schema on first run.

### Connect Claude Desktop via the proxy

Add the proxy to your Claude Desktop MCP config (`claude_desktop_config.json`):

```json
{
  "mcpServers": {
    "expense-tracker": {
      "command": "uv",
      "args": ["run", "python", "proxy.py"]
    }
  }
}
```

The proxy connects to the deployed FastMCP Cloud endpoint over Streamable HTTP and re-exposes it to Claude Desktop over STDIO — restart Claude Desktop after adding the config.

### Deploying your own instance

1. Push the repo to a Git provider
2. Deploy `main.py` on [FastMCP Cloud](https://fastmcp.cloud) (or any host that can run a Streamable HTTP server)
3. Update the URL in `proxy.py` to point to your deployed endpoint

## Project Structure

```
.
├── main.py               # MCP server: tools, resource, DB init
├── proxy.py               # STDIO proxy → deployed FastMCP Cloud server
├── categories.json         # Expense category/subcategory taxonomy
├── expenses.db              # SQLite database (created/used at runtime)
├── pyproject.toml            # Project metadata + dependencies (uv)
├── uv.lock                    # Locked dependency versions
└── .python-version              # Pinned Python version
```

## Design Notes

- **Sync init, async runtime.** Schema creation and a write-access check happen synchronously once at startup (fail fast, fail loud); all subsequent tool calls are fully async so the server doesn't block under concurrent requests.
- **Resource-driven category discovery.** Rather than hardcoding categories into the tool schema, Claude is expected to read the `expense:///categories` resource first — keeping category logic in one editable JSON file instead of scattered across tool code.
- **Cloud-safe file paths.** Using the system temp directory for the database avoids permission issues on read-only or ephemeral cloud filesystems.

## License

Add a license of your choice (e.g., MIT).

TDQS

B3.2/5.0

Scored across 3 tools

Disambiguation5/5

The three tools have clearly distinct purposes: add_expense creates a record, list_expenses retrieves raw records, and summarize aggregates by category. There is no overlap or ambiguity between them.

Naming Consistency4/5

All tools use lowercase snake_case and a verb-first pattern (add_, list_, summarize). The only minor inconsistency is that 'summarize' omits the explicit noun 'expenses' that the other two include, but it remains predictable and readable.

Tool Count4/5

Three tools is a reasonable size for a focused expense tracker. It is slightly minimal but each tool serves a distinct core function (insert, query, aggregate), so the count feels appropriate rather than inadequate.

Completeness3/5

The surface covers basic recording, viewing, and summarizing expenses, but lacks update and delete operations, which are common expected capabilities in a data management domain. This is a notable gap that agents cannot easily work around.

Maintenance

ActivitySlowing
ResponsivenessNo issues