A self-hosted Wallos instance, served to Claude and any other MCP client. List, create, edit and delete subscriptions; manage categories, payment methods, household members and currencies.
Running Wallos 5.0 or newer, reachable over HTTPS? Point a client at this address and sign in.
claude mcp add --transport http wallos https://wallos-mcp.mkpo.li/mcp
In claude.ai it is Settings → Connectors → Add custom connector with https://wallos-mcp.mkpo.li/mcp. Leave any client ID and secret fields empty — MCP clients register themselves. Any single-segment label after /mcp/, such as /mcp/household, is a separate connection with its own grant.
The sign-in page asks for two things: the address you open Wallos at, and an API key. The key lives on your own profile page, at /profile.php on your instance — open the menu beside your name, go to Profile, and copy the key under API Key. If the field is empty, Regenerate fills it.
The key is the whole credential: everything this server can read or change, it changes as you. Wallos lets you regenerate it whenever you like, which invalidates the old one and every connection holding it.
You need a Cloudflare account and bun. Without a custom domain the Worker answers on workers.dev.
git clone https://github.com/mkpoli/wallos-mcp && cd wallos-mcp bun install bun run setup
bun run setup asks which domain to answer on, creates the KV namespace, takes ALLOWED_HOSTS, generates a cookie key, and deploys. Re-running it to rotate one secret is safe.
ALLOWED_HOSTS is the list of Wallos hosts a connection may name. The MCP endpoint is public and clients register themselves, so this is what stops a stranger pointing a deployment at somebody else's instance.
| Value | Meaning |
|---|---|
wallos.example.com | that one instance |
wallos.example.com, money.example.org | either of them |
*.example.com | any host in the domain |
* | any host at all, which is what a shared deployment sets |
| empty | nobody signs in |
Whatever a sign-in names, the Worker fetches, so the URL must be https and must not point anywhere private: RFC1918, CGNAT, link-local, IPv6 unique-local and .local / .internal names are refused, and global_fetch_strictly_public enforces the same at the platform. Redirects are refused rather than followed, since a 307 would replay the request — key included — at a host the allowlist never saw.
ADMIN_TOOLS to 1 and the bound key passes an admin read. They rewrite how everyone signs in to the instance.