Caudal
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Caudalrecord a new expense of $150 for software"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Caudal
Ask, don't guess.
Caudal (Spanish: both water flow and wealth) — the finance OS for a bootstrapped SaaS.
Excel wasn't going to hold up as the finances of the business grew, and a nicer spreadsheet wasn't the fix — that's just the same manual entry with better colors. Caudal was built from day one as something else: a navigation map for Vertex and whatever it ships next, not a chore that demands data entry as the price of admission. So it lives inside the tools already in use — Hermes Agent, Vertex's day-to-day operating agent, for quick capture and checks, and Claude as the interface for decisions that go beyond what a daily agent should carry (scenario modeling, quarterly reviews, what's next). Concretely, that's two doors into one source of truth: a server-side web interface, and an MCP server exposing the same capabilities as tools.
What this is
Money is stored as integer minor units (never floats), every write is idempotent and audit-logged.
Single-currency ledger by construction: USD amounts are converted to COP at the official daily TRM (Banco de la República, via datos.gov.co) at record time (
core/fx.py) — aggregates never mix currencies.Beyond recording what happened, the system computes forward-looking projections (cash-flow, runway, MRR) and runs a proactive alert engine (budget overruns, runway thresholds) on a schedule.
Ships with CI, structured logging/tracing/metrics, backups, and a tested restore path — production hygiene, not a script.
Domain background (SaaS accounting fundamentals, category taxonomy, metric formulas) lives in finanzas-saas.md.
Why MCP, not A2A: Hermes has no Google A2A support but has first-class MCP client support, and MCP's elicitation capability (elicitation/create) lets a tool call pause and ask a clarifying question mid-conversation — exactly the "don't guess, ask" behavior this needs.
Related MCP server: Expense Tracker MCP Server
Architecture
flowchart LR
subgraph Chat
H[Hermes Agent<br/>Telegram / Slack / CLI]
end
subgraph Caudal
M[MCP server<br/>stdio]
C[core/<br/>validation · repository<br/>projections · alerts]
S[scheduler<br/>proactive digests & alerts]
W[Internal web UI<br/>FastAPI]
end
PG[(Postgres)]
LF[Langfuse Cloud<br/>optional, via OpenRouter Broadcast]
H <--MCP tools--> M
M --> C
S --> C
W --> C
C --> PG
M -. traces .-> LF
W -. traces .-> LFBoth entry points — MCP tools and the internal UI — go through the same core/ validation and storage layer, so a transaction created by chat and one entered by hand follow identical rules.
Tech stack
Concern | Choice |
Language / packaging | Python, |
MCP server | official |
Web UI | FastAPI + Jinja2, server-rendered — CSS design system + inline SVG charts, no JS framework, no build step |
Database | Postgres, SQLAlchemy, Alembic |
Scheduler | APScheduler (fallback when Hermes cron isn't set up) |
Logging / tracing / metrics |
|
Agent observability & cost | Langfuse Cloud via OpenRouter's native Broadcast + per-key spending cap |
Lint / types / security |
|
Tests |
|
Containers | Docker, Docker Compose |
Repository layout
caudal/
core/ # domain layer: repository, validation, reporting, projections, alerts, fx (TRM), logging/tracing
mcp_server/ # MCP tool definitions (thin wrappers over core/)
web/ # FastAPI app, Jinja templates, static design system, server-side SVG charts
scheduler/ # proactive digest/alert runner (no-Hermes fallback)
config.py # environment-driven settings, fail-fast on missing values
tests/
alembic/ # database migrations
planning/ # roadmap — one doc per phase, statuses tracked there
docker/ # compose profile support files (hermes-dev config, etc.)
.github/workflows/
Dockerfile
docker-compose.ymlHow to run
make all # core app + pre-build the caudal venv Hermes needs
make chat # interactive Hermes chat session (set OPENROUTER_API_KEY in .env first)
make help # every target: up / hermes-warm / chat / ps / logs / down / down-all / restore-drillOr directly with Docker Compose (just the core app):
cp .env.example .env
docker compose up -d
curl http://localhost:8000/healthzThis brings up Postgres, runs migrations, the web UI (:8000), the proactive scheduler, and a daily-backup service (docker/backups/, RETENTION_DAYS default 14). Restore a backup with scripts/restore.sh <dump> or make restore-drill.
Local dev without Docker:
uv sync --all-groups
cp .env.example .env
uv run pytest --cov=caudal --cov-report=term-missing # 85% coverage gate, real Postgres via testcontainers
uv run pre-commit installCI (.github/workflows/ci.yml) runs lint → type-check → SAST → dependency audit → tests on every push, plus a separate gitleaks job.
Core concepts
core/is the single implementation both MCP tools and the UI call into:validation.py(pure input validation),repository.py(idempotent CRUD + audit log),reporting.py/projections.py(SQL aggregates, deterministic forecast/runway math — no LLM),alerts.py(deduplicated proactive rules).MCP tools (
caudal/mcp_server/server.py, 8 tools):record_transaction,update_transaction,list_transactions,get_totals,list_categories,get_projections,get_digest,check_alerts.record_transactiontries MCP elicitation for missing/invalid fields before falling back to a structuredclarification_neededresult. Try it without Hermes viauv run mcp dev src/caudal/mcp_server/server.py.Internal UI (
caudal/web/,uv run uvicorn caudal.web.app:app --reload): dashboard (net cash flow hero, runway meter, expense/infra breakdowns), transaction/budget CRUD, alert history with human-readable payloads, projections (forecast + assumptions), reports (net by month, MoM deltas, category breakdowns with date filters),/healthz,/metrics. Light/dark, mobile bottom-tab navigation. No auth in v1 — single-user, localhost/private-network use only (seeplanning/03-remote-access.md).Scheduler (
uv run caudal-scheduler): daily alert check + weekly digest, delivered via webhook (NOTIFIER_WEBHOOK_URL) or logged if unset. This is the fallback path — once Hermes is available, its own cron callingget_digest/check_alertsis the primary delivery path (see below).
Connecting to Hermes
On the machine where Hermes actually runs:
hermes mcp add caudal --command "/app/.venv/bin/caudal-mcp"or the equivalent in config.yaml:
mcp_servers:
caudal:
command: "/app/.venv/bin/caudal-mcp"
env:
DATABASE_URL: "postgresql+psycopg://finance:finance@<host>:5432/finance"
tools:
include:
[record_transaction, update_transaction, list_transactions, get_totals,
list_categories, get_projections, get_digest, check_alerts]Then set up Hermes cron for proactive delivery: "Every Monday at 9am, call the caudal get_digest tool and post the result to Telegram."
Optional: local Hermes chat (--profile hermes-dev)
docker-compose.hermes-dev.yml runs a local Hermes instance for testing the chat → MCP flow without Telegram/Slack, routed directly to OpenRouter (docker/hermes/config.yaml). Set OPENROUTER_API_KEY in .env; budget cap and LLM tracing are OpenRouter dashboard settings (per-key spending cap, "Broadcast to Langfuse") — see docs/observability.md.
Verified end-to-end: a chat message correctly triggers record_transaction, the elicitation flow corrects an invalid field, and the row lands in Postgres. Two notes if you're touching this profile:
Hermes writes its own
config.yamlinto its data volume on first launch —scripts/patch_hermes_config.py(run automatically bymake chat) merges this repo's provider/MCP config into it.A freshly-registered MCP server's tools aren't auto-enabled for the
cliplatform — also handled bymake chatviahermes tools enable.
Engineering log
CHANGELOG.md— what changed, per release (Keep a Changelog).docs/adr/— why it changed: Architecture Decision Records for every decision that shapes the data model, an invariant, or the architecture — including the bugs that earned one (e.g. ADR-0008, a forecast double-count found with real data).planning/— where it's going, one doc per phase.
Status & roadmap
Core platform: complete. Bootstrap, data model, core layer, MCP tools + elicitation, internal UI (redesigned: shadcn-style design system, SVG charts, mobile nav), automatic USD→COP conversion at the daily TRM, proactive scheduler, observability, CI, containerization, Hermes dev integration.
Where it's going next, in plain terms: registering receipts and invoices without typing them in by hand, keeping track of what clients owe and chasing that down automatically, and getting flagged the moment a spend spike or an unexpected charge shows up — before it's just a number buried in next month's report. Full detail per phase in planning/:
Phase | Scope | Status |
Clients, plans, subscriptions, invoices/AR (cartera), payroll category, real MRR | In progress | |
Auto-registering invoices from email (with a review queue, never blind), narrated digests, client payment/collections automation, spend-spike and unexpected-charge alerts | Planned | |
Web UI auth, MCP over streamable HTTP, channel roles (Hermes = capture, Claude = analysis) | Planned |
License
MIT — see LICENSE.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/RookieCol/finance-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server