finance-mcp

finance-mcp

A read-only personal finance MCP server for analyzing transaction CSVs, providing tools to filter transactions, summarize spending, categorize charges, and flag unusual activity.

Category
Visit Server

README

finance-mcp

A small personal finance MCP server. Built in a weekend to learn MCP properly, not to maximize features.

What it does

This server lets Claude Desktop work with personal transaction data. The data is a single CSV file with 111 fake transactions covering six months. There is no database, no UI, and no auth beyond SDK defaults. The CSV is the data layer.

The data is generated by a seeded script, so it is reproducible. Four anomalies are planted in it on purpose. That way the anomaly tool can be graded against a known answer key.

The server is read-only. That is a design decision, not a limitation. It is explained below.

The four tools and why these four

Each tool maps to one question a person would actually ask. Not to an implementation step.

get_transactions(start_date, end_date, category?) "What did I spend on X between these dates?" A filtered read from the CSV. The raw material for everything else.

summarize_spending(period) "Where did my money go this month?" Totals by category for one month, largest first. The period is a strict YYYY-MM string. The model translates phrases like "last month" into that format before calling. Language is the model's job. Arithmetic is the server's.

categorize_transaction(description, amount) "What category is this new charge?" The server searches history for similar descriptions and returns a suggestion with a confidence level and the matching evidence. It says "unknown" when it has no history. It never bluffs. The model is free to override with common sense, and the docstring invites it to.

flag_unusual_activity() "Is anything weird going on?" A full audit of all history. Two checks: amount outliers versus the category median, and possible duplicate charges within 14 days. It returns flags with reasons. A flag is a request for human review, not an accusation.

I considered splitting anomaly detection into two composable tools, one for stats and one for flagging. I decided against it. A tool whose output exists only to feed another tool has no independent use. It just gives the model more ways to call things wrong. Tools should be shaped by user intent, not by internal plumbing.

Design decisions

Read-only is the security model. Capabilities that are never built cannot be abused. The entire disk-touching surface of this project is one line that opens the CSV for reading. The entire launch surface is one entry in the Claude Desktop config file. Delete that entry and the server cannot start. On top of that, the host asks the user for permission before tool calls.

Judgment lives in the model. Evidence lives in the server. The server does only deterministic work: parsing, sums, medians, match counts. It returns evidence, not verdicts. The model interprets: it translates natural language into strict parameters, overrides "unknown" categories with sensible guesses, and triages flags into "look at this" versus "probably fine". This split showed up in testing. The model spotted a duplicate charge on its own before the anomaly tool existed. But model observations are incidental. The tool makes detection systematic and repeatable.

Full audit instead of recent-window alerting. My spec first said "compare recent spending to history". That design absorbs old anomalies into the baseline and never flags them. I changed to a full-history audit so a large one-off charge from months ago is caught too. The cost is more false positives on legitimate one-off purchases, like a flight. The tool's wording accepts that cost openly.

Medians, not means. Real transaction history contains old anomalies. A mean lets a single huge charge inflate what "normal" looks like and hide future outliers. The median ignores it. Dirty history cannot poison the baseline.

A 14-day duplicate window. Wide enough to catch a double charge. Narrow enough that a legitimate monthly subscription about 30 days apart never triggers it.

Sign convention. Expenses are negative, income is positive. Total spending becomes a simple filter and sum, with no category-dependent logic.

Docstrings are the product surface. The model decides whether and how to call a tool by reading its name and docstring. So the docstrings state the date formats, the exact category names, and the sign convention. Writing them is UX copy for an audience of one AI.

What building this taught me

The model skipped my tool when it could answer from its own knowledge. "What category is Chipotle" got a generic answer with no tool call. Tools are offers, not commands. Discovery depends on the docstring competing with what the model already knows.

Permission fatigue is real and I felt it. I clicked "Always allow" once and never saw a security prompt again. Narrow, read-only tools are what make that click a sane choice.

Setup is the hard part. My config file lived in a different place because I installed from the Microsoft Store. The app rewrote my config on quit, so edits only stick when the app is closed. A lazy tool-loading mode hid my new tools from the model. And typing into a terminal where the server is running sends your keystrokes to the server as broken JSON. Every one of these cost me time and taught me how the pieces actually connect.

Code changes need a host restart. The server is launched by Claude Desktop and read once at startup.

What I'd build next

preview_import(raw_csv_text). A dry-run importer for real bank exports in any format. It would propose a column mapping, normalized rows, and suggested categories, and list the rows it could not parse. It proposes and never commits. Nothing is written to disk, so read-only survives. This is planned as v1.1.

Result visuals. MCP supports servers that return interactive UI with results, so a spending summary could render as a chart instead of text. Today the host draws charts on request, which is presentation, not a server concern.

A connector audit tool. Something that reports what servers a host can launch and what each one's code can touch. The config file is the inventory, but reading each server's code is still manual.

Real data. Production finance products pull normalized data from aggregator APIs instead of parsing user CSVs. Parsing belongs in deterministic code. Categorization belongs in the model.

Run it

Requires uv.

uv sync
uv run server.py

The server speaks stdio and prints nothing. Silence is success. Register it in claude_desktop_config.json:

{
  "mcpServers": {
    "finance": {
      "command": "path\\to\\uv.exe",
      "args": ["run", "--directory", "path\\to\\finance-mcp", "server.py"]
    }
  }
}

Then fully quit and reopen Claude Desktop, and ask it something like "is there anything weird going on with my money lately?"

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured