MCP server and Helm chart for the Lunch Money personal-finance API.
  • Python 84.9%
  • Just 5.2%
  • Shell 4.4%
  • Go Template 4.1%
  • Dockerfile 1.4%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
coilyco-ops 879a15cc48
All checks were successful
build-publish / helm (push) Successful in 3s
build-publish / test (push) Successful in 6s
build-publish / publish (push) Successful in 14s
ci: give build-publish a retry trigger, because it had none
Run 75 got test and helm green on ea739b6, then publish failed in the
Docker build:

    Step 2/12 : COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
    invalid from flag value ghcr.io/astral-sh/uv:latest:
      Head "https://ghcr.io/v2/astral-sh/uv/manifests/latest":
      net/http: TLS handshake timeout

That reads as transient: publish last succeeded on run 73, and the commit
itself is fine. The problem is I could not retry it. This Forgejo serves
neither POST actions/runs/{id}/rerun nor rerun-failed-jobs (both 404, by run
index and by run id), and `on:` carried push alone, so the only way to
re-run a green commit was to push another commit. That is the wall this
change removes.

`workflow_dispatch:` restores the manual retry path the fleet convention
already assumes exists.

Not fixed here, filed as #46: the build reaches ghcr.io for `uv:latest` on
every run, with `docker build --pull` re-resolving it each time. Both the
reachability and the reproducibility problems come off that one line, and
neither can be validated without a Docker daemon, which the host I am on
does not have.

Refs #46

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Agent-Role: platform
Co-authored-by: Kai Siren <coilysiren@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Agent-Role: platform
2026-08-29 08:12:51 -07:00
.agents/skills/repo-lunch-money-k8s fix: correct repository pointer owner 2026-07-30 10:27:04 -07:00
.claude chore: scrub coily references for coily retirement 2026-07-04 17:54:44 +00:00
.forgejo/workflows ci: give build-publish a retry trigger, because it had none 2026-08-29 08:12:51 -07:00
.github ci: unblock the pin advance, and land it 2026-08-27 20:31:30 -07:00
.ward feat(just): retire ward exec, move the verb table to a justfile 2026-08-17 13:39:32 -07:00
chart feat(chart): restore generic Helm chart 2026-07-26 19:39:27 -07:00
docs ci: unblock the pin advance, and land it 2026-08-27 20:31:30 -07:00
examples feat(chart): restore generic Helm chart 2026-07-26 19:39:27 -07:00
lunch_money_mcp fix HTTP host allowlists for MCP 2026-07-08 16:22:13 +00:00
scripts ci: run the format check locally, so it stops landing broken on main 2026-08-29 08:07:42 -07:00
tests fix public ingress authority allowlist 2026-07-08 16:32:41 +00:00
.dockerignore Bootstrap the containerized, Helm-deployable MCP server 2026-05-22 02:09:19 -07:00
.gitattributes ci: unblock the pin advance, and land it 2026-08-27 20:31:30 -07:00
.gitignore Bootstrap the containerized, Helm-deployable MCP server 2026-05-22 02:09:19 -07:00
.pre-commit-config.yaml ci: run the format check locally, so it stops landing broken on main 2026-08-29 08:07:42 -07:00
AGENTS.md ci: unblock the pin advance, and land it 2026-08-27 20:31:30 -07:00
CLAUDE.md chore(pre-commit): adopt context-load-points hooks (v0.11.1) 2026-05-30 00:44:14 -07:00
Dockerfile fix: run the venv console script directly instead of uv run 2026-05-22 03:23:06 -07:00
justfile feat(just): retire ward exec, move the verb table to a justfile 2026-08-17 13:39:32 -07:00
LICENSE Bootstrap the containerized, Helm-deployable MCP server 2026-05-22 02:09:19 -07:00
pyproject.toml chore(docs): declare the small documentation band 2026-08-15 23:39:24 -07:00
README.md feat(just): retire ward exec, move the verb table to a justfile 2026-08-17 13:39:32 -07:00
rules.example.yaml chore(code-comments): clean YAML comment violations, bump agentic-os v0.16.0 2026-06-08 16:51:30 -07:00
uv.lock Refresh uv.lock for the renamed project 2026-05-22 02:09:59 -07:00

lunch-money-k8s 🍱☸️

An MCP server for the Lunch Money personal-finance API, packaged as a container image and generic Helm chart, then served over streamable HTTP. Point Claude or any MCP client at it and ask about your spending in plain language.

Who this is for

  • Homelab folks running k3s who want Claude on their phone to answer "what did I spend on groceries this week" without keeping a laptop awake.
  • People who want one MCP endpoint shared across desktop, mobile, and scheduled jobs, instead of stdio-per-device.
  • Anyone who'd rather install a Helm chart than learn each server's bespoke deployment shape.

Why you might try this one

For read/write API coverage, this is feature parity with the other Lunch Money MCP servers. Reach for it when you want the deployment shape - three things stdio-on-a-laptop can't do:

  • Mobile access - dictation-based shorthand queries from the phone. "What did I spend on groceries this week" against a Tailscale-reachable MCP works the same on the train as at the desk, no laptop awake required.
  • Scheduled dumps and analysis - k3s is the homelab's general-purpose scheduler, and anything in k3s inherits tailnet reach. A daily routine pulls the trailing 7 days, flags the credit-card balance, enriches opaque payees, feeds uncategorized transactions back into an opinionated rules.yaml format, and writes the digest into an Obsidian vault inbox.
  • Credential isolation - the Lunch Money API token lives in a k8s Secret materialized from AWS SSM via ExternalSecrets. The pod gets it as an env var, the MCP exposes tool calls, and the LLM never sees the underlying key. Access control sits at the tailnet boundary.

Tools

  • list_transactions - transactions in a date range, optional uncategorized filter
  • list_categories / create_category - read and create categories
  • categorize_transaction - assign a category to a transaction
  • spending_summary - total spend per category
  • list_budgets - budget detail per category

Quick start (local, stdio)

Grab an API token from the Lunch Money developers page:

export LUNCH_MONEY_TOKEN=...
uv sync
uv run lunch-money-mcp

Then register uv run lunch-money-mcp with your MCP client.

Run on Kubernetes

The chart supports a managed token or existing Secret:

helm install lunch-money ./chart \
  --set lunchMoney.token="$LUNCH_MONEY_TOKEN"

Ingress, existing-secret wiring, categorization rules, probes, autoscaling, and production overrides are covered in docs/chart.md and docs/deploy.md.

Fleet deploy

Kai's live deployment remains in coilyco-bridge/deploy/services/lunch-money-mcp/. That bundle owns fleet-specific secret wiring, tailnet exposure, and rollout. The chart in this repo remains the portable install surface for other clusters.

The server speaks streamable HTTP at /mcp on port 8080. Deployment notes are in docs/deploy.md.

License

AGPL-3.0. See LICENSE.

See also

Cross-reference convention from coilysiren/agentic-os#59.