Ledger
БесплатноНе проверенEnables renters to cross-examine their rent ledger against their lease using deterministic tools, uncovering discrepancies in charges, fees, and rent amounts.
Описание
Enables renters to cross-examine their rent ledger against their lease using deterministic tools, uncovering discrepancies in charges, fees, and rent amounts.
README
Keep an eye on your rent ledger — and cross-examine it against your lease — through your AI assistant.
LedgerMCP is a Model Context Protocol server built for renters. Import your property/rent-portal ledger (CSV, TSV, Excel) and your lease (PDF), and LedgerMCP gives an AI assistant a set of deterministic tools to answer questions like:
- "Am I being charged the right rent every month?"
- "Is this 'Valet Trash' fee actually in my lease?"
- "Did they charge me the late fee my lease specifies — or more?"
- "What have I paid this year, and what's my current balance?"
The assistant answers by calling tools that do exact arithmetic in Python (every amount is a Decimal, never a float) and by comparing your ledger line-by-line to your lease — instead of eyeballing a table in its context window and guessing.
Why
Ask an LLM to "total my fees for Q2" or "check my rent against my lease" from a pasted statement and you get plausible, confidently-wrong numbers. LedgerMCP moves the numbers and the comparisons out of the prompt and behind tools:
- The model decides what to ask (
check_rent_charges,find_unexpected_charges, …). - LedgerMCP decides what the answer is, deterministically, in Decimal — and shows its work by quoting the exact lease text it relied on.
Features
- Renter-first workflow — import a rent ledger and a lease, then cross-examine.
- Lease parsing (PDF) — heuristically extracts rent, deposit, term dates, due day, late-fee policy, recurring fees and parties, each with a confidence level and the source excerpt. The full lease text is retained so the assistant can read anything the heuristics miss.
- Deterministic cross-examination — rent checks, unexpected-charge audits, deposit and late-fee comparisons, all exact.
- Pluggable ledger parsers — CSV/TSV and the Excel "Full Ledger" rent-portal export out of the box; add QuickBooks/OFX/QIF/Xero by writing one small class.
- SQLite storage with lossless Decimal (amounts stored as integer cents).
- Classic analytics too — balances, category/monthly rollups, income statement, balance sheet.
- MCP server (FastMCP) + a Typer CLI over one shared service layer.
- Idempotent ledger imports, high test coverage, Ruff, Pyright, GitHub Actions CI.
MCP tools
Lease cross-examination
| Tool | Description |
|---|---|
lease_ledger_report |
One-call cross-examination: lease terms + every check + human-readable flags. |
check_rent_charges |
Compare each month's rent charges to the lease's monthly rent. |
find_unexpected_charges |
Classify every charge category as expected / referenced / mismatch / unexpected. |
check_security_deposit |
Compare the deposit charged to the lease deposit. |
check_late_fees |
Compare late fees charged to the lease late-fee policy. |
get_lease_summary |
Extracted lease terms, each with confidence + source excerpt. |
get_lease_text |
Read the raw lease, or just the lines matching a query (pet rules, subletting…). |
Ledger analytics
| Tool | Description |
|---|---|
search_transactions |
Filter by date, category, type, account, source, text, amount range. |
get_account_balance |
Running balance for an account or the whole ledger, optionally as-of a date. |
sum_transactions |
Net / inflow / outflow totals for a filtered set. |
monthly_spending |
Spending grouped by calendar month. |
transactions_by_category |
Per-category rollup, ranked by spend. |
get_income_statement |
Accrual-basis P&L for a period. |
get_balance_sheet |
Assets / liabilities / equity as of a date. |
list_accounts |
The chart of accounts and each account's type. |
Installation
LedgerMCP uses uv.
git clone https://github.com/luke-nielsen/ledger-mcp.git
cd ledger-mcp
uv sync
Quickstart
# 1. Import your rent ledger
uv run ledger-mcp import examples/sample_ledger.csv --db ledger.db
# 2. Import your lease
uv run ledger-mcp import-lease examples/sample_lease.pdf --db ledger.db
# 3. See what the lease says
uv run ledger-mcp lease --db ledger.db
# 4. Cross-examine ledger vs lease
uv run ledger-mcp lease-report --db ledger.db
The bundled example intentionally shows a clean rent match, a late-fee discrepancy (lease says $75, ledger charged $50), and coverage gaps (a deposit and pet rent that were never charged):
LEASE vs LEDGER
Findings:
• 'Late Fee' charged $50.00 but lease specifies $75.00.
• Lease items not seen in the ledger: Security Deposit, Pet Rent.
• Late fees charged $50.00 but lease late fee is $75.00.
Rent check:
2026-04 charged $1,450.00 expected $1,450.00 [match]
2026-05 charged $1,450.00 expected $1,450.00 [match]
2026-06 charged $1,450.00 expected $1,450.00 [match]
You can also import a real Excel rent-portal statement and your real lease:
uv run ledger-mcp import "full_ledger.xlsx" --db ledger.db
uv run ledger-mcp import-lease "my_lease.pdf" --db ledger.db
Use it with an AI assistant (the point)
Run the MCP server and point any MCP client at it. For Claude Desktop, add to
claude_desktop_config.json:
{
"mcpServers": {
"ledger": {
"command": "uv",
"args": ["run", "ledger-mcp", "serve"],
"cwd": "/absolute/path/to/ledger-mcp",
"env": { "LEDGER_MCP_DB": "/absolute/path/to/ledger.db" }
}
}
}
Then ask: "Does my ledger match my lease?" — the assistant calls
lease_ledger_report, relays the flags, and can drill in with check_rent_charges
or get_lease_text to quote the exact clause.
Programmatic use
from ledger_mcp import LedgerService, Settings
service = LedgerService.from_settings(Settings.from_env(db_path="ledger.db"))
service.import_file("examples/sample_ledger.csv")
service.import_lease("examples/sample_lease.pdf")
report = service.lease_report()
for flag in report.flags:
print("•", flag)
How lease cross-examination works
Lease PDFs are unstructured and vary wildly, so LedgerMCP splits the problem:
- Extraction is heuristic but honest. Regex/keyword rules pull the common
fields; each extracted value carries a
Confidence(high/medium/low) and the verbatimexcerptit came from. Whatever isn't matched is simply left unset. - The full lease text is retained, so a client can read anything the
heuristics miss via
get_lease_text(pet policies, subletting, maintenance). - The comparisons are deterministic. Every check below is exact Decimal arithmetic over stored data — the terms may be fuzzy, but the math isn't:
| Check | What it does |
|---|---|
| Rent | Sums each month's rent charges and compares to the lease's monthly rent (match / over / under / missing). |
| Charge audit | Matches each charge category to base rent, the deposit, a named lease fee, or a mention in the lease text; anything else is unexpected. Flags amount mismatches. |
| Deposit | Compares the deposit charged in the ledger to the lease deposit. |
| Late fees | Compares late fees charged to the lease late-fee amount and grace period. |
Notes:
- National Apartment Association (NAA) leases — the most common US apartment lease — are handled by a dedicated extractor. Signed NAA packets are flattened forms whose fill-in values are detached from their labels in the text, so LedgerMCP reads the reliably-anchored fields (base monthly rent, landlord, tenants, address, effective date) and leaves the positionally ambiguous ones (deposit, late fee) for
get_lease_textrather than guessing.- Scanned/image-only PDFs have no text layer; OCR is out of scope and such files are reported with a clear error. Export a text-based PDF if you can.
- For unusual leases, lean on
get_lease_textand the confidence levels.
The ledger data model
Entry types and the sign convention
Every source line normalizes to one EntryType, signed by a single rule:
EntryType |
Meaning | Sign on balance |
|---|---|---|
charge |
Billed to you | + (increases what you owe) |
payment |
You paid | − |
credit |
Concession/discount in your favour | − |
refund |
Money returned to you | − |
adjustment |
Manual correction | + |
A positive ledger balance means money is owed.
Classification (chart of accounts)
The default ClassificationRuleset reflects a renter's perspective: charges →
one expense account per category, concessions → income, payments/refunds
→ a liability (Accounts Payable). Override per category — e.g. treat a
security deposit as an asset:
from ledger_mcp import ClassificationRuleset, AccountType
ruleset = ClassificationRuleset(category_overrides={"security deposit": AccountType.ASSET})
Architecture
Layers are decoupled so each is independently testable and swappable:
ledger files ─▶ parsers ─▶ RawEntry ─▶ validate + normalize + classify ─▶ Transaction ─┐
▼
lease PDF ─────▶ lease.pdf ─▶ lease.extract ─▶ LeaseTerms ─────────────────▶ SQLite (SQLAlchemy)
│
┌────────────────────────────────────────────────────────────── ┘
▼
analytics + lease.review (cross-examination)
│
▼
LedgerService ← shared by the MCP server & CLI
| Module | Responsibility |
|---|---|
parsers/ |
Read ledger formats → RawEntry. Registry auto-selects by file type. |
models.py |
Canonical Pydantic models, enums, sign convention. |
classification.py / normalize.py |
Map entries onto a chart of accounts and apply signs. |
validation.py |
Semantic checks + reconciliation against reported balances. |
ingest.py |
The parse → validate → normalize → persist pipeline. |
db/ |
SQLAlchemy schema, engine, repositories. |
lease/ |
PDF text extraction, heuristic term extraction, storage, and cross-examination. |
analytics/ |
Deterministic Decimal queries and statements. |
service.py |
Application facade owning session lifecycle. |
mcp/ |
FastMCP server exposing the tools. cli.py |
Adding a ledger parser
from pathlib import Path
from ledger_mcp.models import RawEntry
from ledger_mcp.parsers import ParseResult, default_registry
class QifParser:
name = "qif"
def can_parse(self, path: Path) -> bool:
return path.suffix.lower() == ".qif"
def parse(self, path: Path) -> ParseResult:
entries: list[RawEntry] = ... # read the file
return ParseResult(entries=entries, source_name=path.name, parser=self.name)
default_registry.register(QifParser())
Validation, classification, storage, analytics and lease cross-examination are all format-agnostic, so nothing else changes.
Development
uv sync
uv run pytest # tests
uv run ruff check . # lint
uv run ruff format . # format
uv run pyright # type check
CI (.github/workflows/ci.yml) runs lint, type-check and tests on every push and PR.
Configuration
| Variable | Meaning | Default |
|---|---|---|
LEDGER_MCP_DB |
SQLite path or SQLAlchemy URL | ./ledger.db |
The CLI --db flag overrides the environment.
Privacy
Everything runs locally. Your ledger and lease live in a local SQLite file; no
data leaves your machine. The bundled examples/ are synthetic.
License
MIT — see LICENSE.
Установка Ledger
У этого сервера нет опубликованного пакета — он собирается из исходников. Открой репозиторий и следуй инструкции в README.
▸ github.com/luke-nielsen/ledger-mcpFAQ
Ledger MCP бесплатный?
Да, Ledger MCP бесплатный — установка в пару кликов через Unyly без оплаты.
Нужен ли API-ключ для Ledger?
Нет, Ledger работает без API-ключей и переменных окружения.
Ledger — hosted или self-hosted?
Self-hosted: сервер запускается локально на твоей машине командой из раздела установки.
Как установить Ledger в Claude Desktop, Claude Code или Cursor?
Открой Ledger на unyly.org, выбери вкладку своего клиента (Claude Desktop, Claude Code, Cursor) и нажми Install — конфиг сгенерируется автоматически, без правки JSON.
Похожие MCP
GitHub
PRs, issues, code search, CI status
автор: GitHubFilesystem
Secure file operations with configurable access controls.
Memory
Knowledge graph-based persistent memory system.
Template MCP Server
A CLI tool to create a new Model Context Protocol server project with TypeScript support, dual transport options, and an extensible structure
автор: mcpdotdirectCompare Ledger with
Не уверен что выбрать?
Найди свой стек за 60 секунд
Автор?
Embed-бейдж для README
Похожее
Все в категории development
