ledger-mcp
This server provides double-entry ledger operations via MCP tools.
Create accounts with a name and an account type (asset, liability, equity, revenue, expense).
Record transactions with a transaction ID, optional memo, and an array of entries that include account, amount_cents, and side; debits must equal credits.
Get the balance of an account in cents.
Reconcile the ledger by checking that total debits equal total credits.
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., "@ledger-mcprecord a $50 transfer from checking to savings"
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.
ledger-mcp
I built this over two days to figure out where an MCP server's correctness actually lives — in the tool implementation, or in the schemas the model sees.
The domain is a double-entry ledger, because it has a hard definition of "wrong": debits have to equal credits, or the books don't reconcile. So there's no room to hand-wave that the agent did "something reasonable." Either the reconcile call comes back balanced or it doesn't.
What's in the repo
ledger.py is a small SQLite-backed double-entry ledger. Amounts are integer cents so nothing rounds. test_ledger.py covers it with 7 pytest cases.
server.py wraps the ledger as an MCP server with four tools: create_account, record_transaction, get_balance, reconcile.
Day 1 ended there — server running, the four tools discoverable from Claude Desktop, the sample scenario (record a $250 sale, an $80 supplies purchase on account, pay it off) ran end-to-end.
Day 2 is the eval work.
evals/cases.py— 10 test cases split across three buckets: five that should just work, three that probe specific design leaks I noticed in Day 1 (dollar-vs-cent ambiguity, no discovery tool for existing accounts, silent account creation on ambiguous prompts), and two restraint cases where the agent shouldn't touch the ledger at all (accounting theory question, advice question).evals/harness.py— runs each case through Claude via the Anthropic Messages API with the real MCP server spawned as a subprocess. Each case gets its own SQLite file (server.pyreadsLEDGER_DB_PATHfrom the env), so cases are fully isolated. Every tool call is captured, transcripts land inevals/transcripts/.evals/inspect_request.py— dry-runs a case and prints the exact request body the harness would POST. No API key, no cost. This turned out to be the most useful thing in the repo.
Related MCP server: Agent Ledger
What the schema audit found
Before running anything against the API I used inspect_request to look at the tool payload the model would actually receive. The docstrings and the JSON schema were telling different stories.
record_transaction.entries at baseline-v1:
"entries": {
"items": { "additionalProperties": true, "type": "object" },
"type": "array"
}The docstring said each entry needs account, amount_cents, and side. The schema said "array of arbitrary objects." Same pattern on side (no debit/credit enum), on account_type (no enum for the five valid values), and on amount_cents — no type constraint anywhere inside entries.items, so nothing prevents the model from passing 42.50 and getting silently truncated to 42 at the SQLite integer column.
Everything I cared about lived in prose.
Full baseline payload: evals/baseline_schemas/tools_payload_baseline.txt.
What I changed
Four edits to server.py, in expected-impact order:
Introduced a
LedgerEntrypydantic model withaccount: str,amount_cents: int (gt=0),side: Literal["debit","credit"].entries.itemsnow$refs a proper nested type in the schema.Literal["asset","liability","equity","revenue","expense"]onaccount_type. Now visible in the schema as an enum.Rewrote
get_balance's docstring to explain the sign convention — positive means "normal balance" for assets and expenses, the reverse for liabilities/equity/revenue. Signal change, not a schema change, but it's semantic detail types can't carry.Added a concrete example call inside
record_transaction's docstring.
Fixed payload: evals/baseline_schemas/tools_payload_fixed.txt. The two files are meant to be diffed.
7/7 unit tests still pass.
What I did not do
The harness is real. Running it against claude-sonnet-4-5-20250929 takes ~3 minutes and would cost a few cents. I did not run it against both tags for one honest reason: I hit my Anthropic API credit limit before I could. So this README doesn't quote a pass/fail delta — the schema diff is the evidence here. Anyone with a key can reproduce the behavioral run in one command.
Two things I deliberately left out of the M10 fix pass, even though NOTES.md argues for them:
A
list_accountsdiscovery tool. Right now the model has no way to check what accounts exist before referencing them.Structured error objects instead of raw
ValueErrorstrings.
Both are real design improvements, not schema tightening. I kept the fix pass to one thing so the before/after diff would be readable.
Running it
uv sync
uv run pytest -q # unit tests
uv run python -m evals._smoketest # verify MCP plumbing, no API
uv run python -m evals.inspect_request simple_sale # inspect the wire payloadFull agent eval:
export ANTHROPIC_API_KEY="sk-ant-..."
export EVAL_MODEL="claude-sonnet-4-5-20250929"
uv run python -m evals.harnessPer-case transcripts go to evals/transcripts/, and _summary.json collects the pass/fail table.
Tags
baseline-v1— Day 1 tools as originally shipped, schemas untouched.fixes-v1— after the M10 schema tightening.
git diff baseline-v1 fixes-v1 -- server.py is the source-level change. The two files in evals/baseline_schemas/ are the wire-level change.
NOTES.md
The raw build log lives in NOTES.md. It reads rough on purpose — it's the actual notes I wrote while building, not a cleaned-up narrative. If anything in this README seems too tidy, that file has the friction.
Available Tools
4 toolscreate_accountB
Create a new account in the ledger.
account_type must be one of: asset, liability, equity, revenue, expense.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | ||
| account_type | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden of behavioral disclosure. The description states it creates an account, implying a write operation, but doesn't disclose any side effects, permissions required, or whether the operation is reversible. It also doesn't mention what happens if an account with the same name exists, or any other behavioral traits. This is a significant gap for a mutation tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise, with two sentences. The first sentence states the purpose, and the second provides a critical constraint. It's front-loaded and efficient, with no wasted words. However, it could be slightly more structured by separating the constraint into a clearer format, but it's still well within acceptable conciseness.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given that there is no output schema and no annotations, the description should provide more context. It explains the account_type constraint but doesn't describe what the tool returns, any side effects, or error conditions. For a creation tool with two parameters, it's minimally adequate but lacks completeness in terms of behavioral and return-value context.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate. The description adds meaning to 'account_type' by listing valid values, which is helpful. However, it doesn't add any semantics for the 'name' parameter beyond what the schema provides (a string). The description partially compensates for the lack of schema descriptions but doesn't fully cover both parameters.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's purpose: 'Create a new account in the ledger.' It uses a specific verb ('create') and resource ('account in the ledger'), which distinguishes it from siblings like 'record_transaction' and 'get_balance'. However, it doesn't explicitly differentiate from potential sibling tools that might also create entities, but given the sibling list, it's clear enough.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides a clear constraint on the 'account_type' parameter, which is a usage guideline. However, it doesn't explicitly state when to use this tool versus alternatives, nor does it mention any prerequisites or context. The sibling tools are different enough that the purpose is clear, but no explicit when/when-not guidance is given.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_balanceB
Get the balance of an account, in cents.
| Name | Required | Description | Default |
|---|---|---|---|
| account_name | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden of behavioral disclosure. It states the operation and unit but does not mention error behavior, permission requirements, or whether the tool is purely read-only. The lack of any caveats or context makes it under-transparent for a tool with zero annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single sentence, perfectly front-loaded, and every word adds value (operation, resource, unit). There is no fluff or redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with one string parameter, no output schema, and no annotations, the description is adequate but thin. It covers the core purpose and return unit, but leaves out likely failure conditions, expected input format, and any behavioral caveats. It is minimally viable but not fully complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must explain the parameter meaning. It only says 'an account' without clarifying how account_name is formatted, validated, or what values are accepted. This adds little beyond the schema's own parameter name.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb and resource: 'Get the balance of an account' and adds the unit of measure 'in cents'. It clearly distinguishes from siblings like record_transaction or reconcile, as it is a read-only balance query.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description clearly implies a read-only balance retrieval but provides no explicit when-to-use guidance or mention of alternatives. For a simple getter this is minimally acceptable, but it does not address edge cases or distinguish from sibling operations like reconcile.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
reconcileA
Check that total debits equal total credits across the ledger.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description must carry the behavioral burden. 'Check' implies a non-mutating read-only operation and 'across the ledger' gives scope, but the description does not disclose return behavior or what happens when equality is not satisfied.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is one focused sentence with no filler. The verb and object are front-loaded, making the purpose immediately clear.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter tool with no output schema, the description captures the core function well. It could add return-value details, such as whether it returns a boolean or a report, but it is adequate for a simple reconciliation check.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters and the input schema is empty. There is no parameter ambiguity for the description to compensate for, so the baseline 4 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific action ('Check that total debits equal total credits') against a clear resource ('the ledger'). It is distinct from sibling tools like get_balance, create_account, and record_transaction.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The use case is implied: an agent would call this when verifying ledger balance. However, it does not explicitly say when to prefer reconcile over get_balance or other alternatives, nor does it provide when-not-to-use guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
record_transactionC
Record a transaction in the ledger.
entries is a list of dicts with keys: account, amount_cents, side.
Debits must equal credits.
| Name | Required | Description | Default |
|---|---|---|---|
| memo | No | ||
| entries | Yes | ||
| transaction_id | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries full responsibility for behavioral disclosure. It states the debit/credit balance requirement, but does not explain side effects, whether transactions are immutable, concurrency behavior, or error handling. The lack of annotation coverage makes this a significant gap.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise, with three short sentences and a clear list of keys for entries. It front-loads the core purpose and includes the critical balancing constraint without excess wording.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool has no output schema and no annotations, and the schema provides minimal type info with no descriptions. The description touches on the essenpurpose and one constraint, but does not cover side effects, validation errors, default memo behavior, or interaction with reconciliation. Given the complexity of a financial transaction tool, more guidance is needed.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate for undocumented parameters. It explains the 'entries' structure (keys: account, amount_cents, side) and the balancing rule, but provides no details on 'transaction_id' or 'memo' beyond their names. This partial coverage leaves some parameters under-specified.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool records a transaction in a ledger, using the verb 'Record' with the resource 'transaction'. It distinguishes from siblings by focusing on the ledger entry creation, but does not explicitly contrast with reconciliation or balance queries.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies usage for recording financial entries but does not provide explicit when-to-use or when-not-to-use guidance. It mentions the double-entry requirement (debits equal credits), which is a key usage constraint, but no alternatives are named.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.
4 tool updates
v0.1.0- First observed
create_account - First observed
get_balance - First observed
reconcile - First observed
record_transaction
TDQS
Scored across 4 tools
Each tool targets a distinct action—account creation, transaction recording, balance retrieval, and reconciliation—with no overlapping responsibilities. The purposes are clearly separated, leaving no ambiguity for an agent to misselect.
Most tool names follow the verb_noun pattern (create_account, record_transaction, get_balance), but 'reconcile' is a single verb without a noun object. This minor deviation is not confusing but breaks the otherwise consistent pattern.
Four tools is well-scoped for a focused ledger server, covering the essential operations of account creation, transaction recording, balance queries, and reconciliation. Each tool earns its place without redundancy.
The core workflow (create account, record transaction, get balance, reconcile) is present, but notable gaps exist: there is no way to list accounts or view transaction history, and no update/delete operations for accounts or transactions. These missing read/audit functions could hinder some workflows.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Personal finance for AI agents — onboard, import statements, categorize & budget over MCP.
Personal finance ledger for AI agents — query spending, track bills, forecast cash flow.
Personal-finance workspace for AI agents: accounts, spending, budgets, goals, and investments.
AI agents for bookkeeping, reconciliation, and financial close for SMBs.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceA personal financial management tool that enables AI assistants to record transactions, check balances, and provide monthly financial summaries via the Model Context Protocol. It allows users to manage their expenses and income through natural language interactions using standardized MCP tools and resources.-
- AlicenseNot gradedqualityAmaintenanceDouble-entry accounting ledger MCP server for autonomous agents that enables creating accounts, posting journal entries, and generating financial reports.MIT
- FlicenseNot gradedqualityCmaintenanceEnables AI agents to manage personal finances through MCP tools for transaction management, spending analytics, and goal tracking.1-
- AlicenseAqualityCmaintenanceEnables AI agents to interact with a double-entry ledger, offering tools for account management, balanced journal entries, balance queries, trial balance, and penny-perfect allocation. Built with safety by construction: no update/delete tools, idempotent posting, and an append-only journal.7MIT
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/ahmadadam97/ledger-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server