The usual way to keep a business expense ledger is a spreadsheet you update days later from a pile of receipts, or an app you open, log into and tap through. The MCP Expense Tracker puts the ledger in the chat window: say "61.50 euros at Adobe, software, 23% VAT, billable to Acme" and it is logged with the VAT already split out, categorised from a rule you set once, and marked ready to rebill. Everything is a plain JSON file on your own machine.
One command for Claude Code:
claude mcp add expense-tracker -- npx -y @theluckystrike/mcp-expense-tracker
Cursor reads the same kind of config. Add this to ~/.cursor/mcp.json (or the project-level
.cursor/mcp.json), then restart Cursor:
{
"mcpServers": {
"expense-tracker": {
"command": "npx",
"args": ["-y", "@theluckystrike/mcp-expense-tracker"]
}
}
}
Claude Desktop uses claude_desktop_config.json with the same block. There is no account, no
API key and no login step: the server runs locally over stdio and writes to
~/.local/share/mcp-servers/expense-tracker/.
Every amount you say is the gross figure printed on the receipt. vat_rate does the rest:
net = round(gross * 100 / (100 + rate)) and vat = gross - net, so net plus VAT
is always exactly the gross, to the cent. Take the line above: 61.50 EUR gross at 23% VAT splits to
net 50.00 EUR and VAT 11.50 EUR. Money is stored as integer minor units in the expense's own currency, so
that split has no floating point residue and a summary of many expenses adds up to the same total you
would get by hand.
An empty category is not left blank. If you have told the server that Adobe means software, the same
sentence without a category still files it under software, because expense_add checks the
stored merchant rules before it gives up and leaves the field empty.
category_rules holds a list of match-to-category pairs. Each match is tried first as a
case-insensitive regular expression and, if that is not valid regex, as a plain substring, so "adobe" or
"amazon.*office" both work without escaping. The first rule that matches wins. Call
category_rules with no arguments to see what is stored, or replace the whole list at once:
no machine learning, no confidence score, just the rule you wrote read back to you exactly.
"Log a 45 km trip in Poland for a client meeting in Krakow, billable to Acme" prices itself:
mileage_add multiplies distance by the region's rate and rounds once. The built-in table is
PL 1.15 PLN per km, UK 0.45 GBP per mile, US 0.70 USD per mile and EU 0.30 EUR per km. 45 km at the Polish
rate is 45 x 1.15 = 51.75 PLN, stored as an expense in PLN with no VAT rate, because mileage allowances are
not usually VAT-bearing. With no region given, kilometres default to the EU rate and miles to the US rate;
rate_per_km overrides the table entirely if your own employer or tax authority sets a
different figure. These are convenience defaults, not tax advice: check the rate your own authority allows
for the year before you rely on it.
receipt_attach takes a path to the receipt file, checks that it exists, and stores the path
together with a sha256 of its bytes. That hash is the part worth having: if the file on disk is ever
edited or replaced, the stored hash no longer matches it, so an audit later has a way to prove the receipt
attached to an expense is the same file that was there when the expense was logged.
"How much did I spend on software this quarter, and what is still billable to Acme" runs
expense_summary grouped by category, project, month or merchant, reporting the gross, the net
and the VAT for each group. Currencies are never mixed: an expense in PLN and one in EUR appear as two
lines under the same category rather than one wrong total. expense_list answers narrower
questions the same way, with totals per currency for whatever date range and filters you gave it.
The point of marking an expense billable is to get it onto a client's invoice, and the wiring to
the invoice server is the part worth being precise about.
expense_to_invoice takes a project and a date range and returns line items shaped exactly as
invoice_create expects: {description, quantity, unit_price, tax_rate}. The
unit_price it sends is the net amount, not the gross, and tax_rate
carries the VAT rate along with it. The invoice server then computes its own tax line from
unit_price and tax_rate, which lands on the same 11.50 EUR of VAT the expense
already split out. If the rebill sent the gross 61.50 EUR as the unit price instead, the invoice would add
23% VAT on top of an amount that already included VAT once, over-charging the client by the whole VAT
figure. Because one invoice carries one currency, expenses in more than one currency come back grouped by
currency, and you pass one group per invoice. Expenses are marked rebilled once sent, so asking again for
the same project and range does not bill the client twice; include_rebilled and
mark_rebilled exist for the times you want to override that.
expense_export writes a date range to csv, xlsx or json and never writes a partial file: if
a limit would be exceeded, nothing is created at all and the tool says why. Free covers unlimited logging
of expenses, mileage and receipts, a 30-day window on expense_list and
expense_summary, 3 projects, 5 category rules, csv and json export up to 200 rows, and
expense_to_invoice for up to 20 items at cost price. Pro ($19 once) opens full history,
unlimited projects and rules, xlsx export, and a markup_percent on the rebill. Full comparison
in free versus Pro. Product page:
MCP Expense Tracker, which pairs with
MCP Invoice for the rebill above and
MCP Time Tracker for the hours on the same project, covered in
the time tracking guide.
The amount you give is the gross figure on the receipt. net = round(gross * 100 / (100 + vat_rate)) and vat = gross - net, so net plus VAT always equals the gross exactly. 61.50 EUR at 23% VAT splits to net 50.00 EUR and VAT 11.50 EUR.
The built-in table is PL 1.15 PLN/km, UK 0.45 GBP/mile, US 0.70 USD/mile, EU 0.30 EUR/km, with EU used by default for kilometres and US for miles. rate_per_km overrides the table with your own figure and currency. These are convenience defaults, not tax advice.
No. expense_to_invoice sends the net amount as unit_price and the original rate as tax_rate, so invoice_create computes the same VAT figure once. Sending the gross amount instead would tax an already-taxed figure a second time, which is why the server sends net.
receipt_attach stores the file's path together with a sha256 hash of its bytes at the moment you attach it. If the file is later edited or swapped, the stored hash no longer matches it, which is what an audit checks.
No. All data is stored in a plain JSON file under ~/.local/share/mcp-servers/expense-tracker/, and the server makes no network calls at all. Exports write to a local path you choose or the server's own data directory.