Command Palette

Search for a command to run...

UnylyUnyly
Весь каталог

Ledger

БесплатноНе проверен

Enables renters to cross-examine their rent ledger against their lease using deterministic tools, uncovering discrepancies in charges, fees, and rent amounts.

GitHubEmbed

Описание

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:

  1. Extraction is heuristic but honest. Regex/keyword rules pull the common fields; each extracted value carries a Confidence (high/medium/low) and the verbatim excerpt it came from. Whatever isn't matched is simply left unset.
  2. The full lease text is retained, so a client can read anything the heuristics miss via get_lease_text (pet policies, subletting, maintenance).
  3. 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_text rather 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_text and 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.

from github.com/luke-nielsen/ledger-mcp

Установка Ledger

У этого сервера нет опубликованного пакета — он собирается из исходников. Открой репозиторий и следуй инструкции в README.

▸ github.com/luke-nielsen/ledger-mcp

FAQ

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

Compare Ledger with

Не уверен что выбрать?

Найди свой стек за 60 секунд

Автор?

Embed-бейдж для README

Похожее

Все в категории development