Skip to main content
Glama
evidence-ledger-mcp

evidence-ledger-mcp

evidence-ledger-mcp

PyPI Release Schema

Under active development. Both the schema and the server may change in breaking ways between versions until v1.0. Pin the version you use.

An MCP server through which agents keep an evidence ledger for the citations in a work: for each claim, the source that backs it, where in the source, a short quote and why the citation is needed. Co-authors, scripts or a second model can then check every citation against its source.

It is built for academics: authors and co-authors who write with LLMs and want every citation to stay checkable. The ledger format is an open schema, so a graduate school could require it for theses, and a journal could adopt it for submissions or build its own standard on it.

Supported schema: v0.5.1 · standalone schema repository (how schema versions are pinned).

The MCP server and its associated documentation use the MIT licence. The bundled schema uses CC BY 4.0, with attribution to Matthias Kloft. These licences do not cover ledger contents or third-party source material.

What is a ledger?

A ledger is a running record, like an accounting book, where each entry is written down once and can be checked later. An evidence ledger is such a record for claims and their evidence: one entry for each claim in a text, saying which source backs it and where. A source can be any document, such as an article, a book, an internal report, a protocol or a dataset description.

Related MCP server: Witness

The evidence ledger schema

Every ledger this server writes follows the evidence ledger schema (JSON Schema, draft 2020-12), currently v0.5.1. The schema is the contract: any tool, script or person can read, validate and check a ledger with it, with or without this server. Its README documents the purpose, the file shapes, every record field, quote hashing, a minimal example and the sources the fields draw on. The server bundles a pinned copy in schema/ and reports the supported version through schema_info.

A ledger is a header (schema_version, work and optional notes) followed by records. Records can link claims to reference passages, analysis outputs, or source code (see supported evidence). Each record links one claim in the work to its evidence:

Field

Required

Content

id, claim_id

yes

Record and claim IDs; the server assigns them. Records about the same claim share a claim_id.

claim, claim_type, contestable

yes

The claim, its type (empirical, definition, method, background) and whether it needs full evidence; false needs a contestable_reason.

anchor

yes

Where the claim sits in the work: its exact text (with optional prefix/suffix) or a marker.

key

yes

The evidence's key; references also need doi, isbn or url, or a no_identifier_reason.

evidence_type

reference (default), analysis_output, or source_code.

analysis, source_code

by type

Report or implementation artifact: path or HTTPS URL, exact-byte hash and locator; analysis may also identify a generating script.

derived_from

Analysis dependencies as existing record IDs; cycles are refused.

locator

references

Where in the source: page, section, figure or table.

quote, quote_focus

A short quote from the source, and the phrase within it that carries the claim; quote_hash is computed by the server.

relation, justification

relation

How the source bears on the claim (supports, partially_supports, background, method, contrasts) and why.

retrieved_via, provenance

yes

How the passage was found, and who assessed it (person or agent and model), when, and against which revision of the work.

withdrawn

The reason a citation was withdrawn; records are never deleted.

A minimal record, as passed to add_record (the server adds the IDs, hash and date):

{
  "claim": "Retrieval practice improves long-term retention.",
  "claim_type": "empirical",
  "contestable": true,
  "anchor": {"exact": "testing improves later recall"},
  "key": "example2020",
  "doi": "10.1234/example",
  "locator": {"page": 12},
  "quote": "participants who were tested recalled more after one week",
  "relation": "supports",
  "justification": "Direct experimental evidence for the claim.",
  "retrieved_via": "pdf"
}

with provenance such as {"by_type": "agent", "by": "Claude Code", "model": "claude-opus-5-5"}. Ledgers are stored as JSON Lines; see docs/schema.md.

What it does

  • Write ledgers safely. Records are validated against the schema before they are appended. The server assigns record and claim IDs, computes quote hashes and dates the provenance instead of leaving them to the agent.

  • Keep the history. The server never deletes records; a withdrawn record keeps its reason.

  • Find gaps. Search by claim, source key or text, and list contestable claims still missing evidence.

  • Share without leaking sources. Exports and summaries come at three levels: full (private), masked (short excerpts) and locator (no quoted text). A publication check refuses to write a file that carries more source text than its level allows.

  • Read it as a report. A standalone HTML summary, grouped by claim, prints to PDF from a browser.

  • See the evidence in the work. The claims are marked in the work's own HTML and linked to their evidence, as end notes or in a side panel.

  • Check it against the work and its sources. Find anchors that no longer appear in the work, records assessed against another revision, quotes that the source text no longer holds, and citation keys missing from the bibliography. Source text can be passed in, or the server fetches it from public URLs (web pages, plain text and PDFs with a text layer).

Installation

Requires Python 3.11 or newer. The server speaks MCP over stdio. With uv, run the released version from PyPI:

uvx --from evidence-ledger-mcp==0.4.0 evidence-ledger-mcp

For example, to add it to Claude Code:

claude mcp add evidence-ledger -- uvx --from evidence-ledger-mcp==0.4.0 evidence-ledger-mcp

Other MCP clients take the same command and arguments. To run a source checkout instead, use uv sync, then configure the client to run uv with arguments run, --directory, the absolute path to the checkout, and evidence-ledger-mcp.

Pass absolute ledger paths to tools; relative paths resolve against the server's working directory.

Tools

Tool

What it does

schema_info

Reports the server version and the supported schema version.

create_ledger

Creates a JSON Lines ledger with a header; work needs title and type.

import_ledger

Converts a ledger of any older schema version, or a pre-schema hand-built list (YAML, JSON or JSON Lines), to a new JSON Lines ledger at the current version, keeping ids, provenance dates and quote hashes, and reports all that fails validation.

add_record

Validates and appends a record, refusing one whose claim or importance differs from another record of the same claim; takes the record and a separate provenance object and returns the stored record. With related, IDs are numbered past those of the other ledgers and the claim must agree with its records there.

update_record

Changes fields of a record (changes is merged in, a null value removes a field) and recomputes the quote hash. provenance records the original assessment and is not changed by edits. A change to claim or importance is copied to every record of the claim, with related also in the other ledgers. Runs the checks of add_record on every affected ledger; nothing is written when one fails.

update_records

The batch form of update_record (also with related): updates is a list of {"id", "changes"} applied in order (later items see earlier ones). Every affected record is checked, all refusals are listed together with the item index and record id, and the changed ledgers are rewritten together: all files or none (on a write error the files already replaced are restored from their original content).

move_record

Moves a record, withdrawn ones too, to another ledger of the project, keeping its ID, claim ID and provenance. Refused when the ID already exists in the target or the record fails the checks of the target or the related ledgers.

get_record

Returns one record by its ID.

find_records

Finds records by claim_id, source key or claim text, or contestable records with missing_evidence. Filters combine; withdrawn records are left out unless include_withdrawn is set.

validate_record

Checks one record against the schema without writing it.

validate_ledger

Checks a whole ledger and lists the problems found, including records of one claim that disagree on claim text or importance and, with related, IDs also used in another ledger and claims whose records disagree across the ledgers.

withdraw_record

Marks a record withdrawn, with a reason, instead of deleting it.

export_ledger

Writes the ledger as JSON or JSON Lines at a chosen level to a new file.

summarize_ledger

Writes a standalone HTML summary grouped by claim to a new file.

embed_ledger

Marks each claim in the work's rendered HTML and links it to its evidence, as end notes or in a side panel, in a new file.

check_ledger

Reads the ledger and reports gaps, anchors missing from the work, records from another revision and quotes not found in source text you pass in or the server fetches from public URLs. Writes nothing.

resolve_links

Matches each source to a PDF in a sources/ folder of only the cited PDFs (best named <citation key>.pdf; else by DOI, ISBN or title from your bibliography) or in a link_map from your reference library, finds the quoted passage by its hash and writes a link file named .private.json with the file and page.

relink_html

Turns a summary or embedded HTML you received into a private copy (.private.html) whose evidence cards open your own PDFs at the passage or cited page (and, with a link map, in your library).

Several ledgers per project

A project may keep more than one ledger, for example one for the schema and one for the server. The schema fixes IDs as R-0001 and C-0001, so per-ledger prefixes would need an upstream schema change; instead the IDs stay unique across the project's ledgers. Pass the other ledgers' paths as related to add_record, update_record, move_record and validate_ledger every time, and use move_record to move a record from one ledger to another. A moved ID is not reused as long as related lists every ledger of the project each time. A claim may then span related ledgers (a claim split by a move has records in both) and stays consistent as long as related is passed: add_record and move_record refuse a record whose claim text or importance differs from the claim's records in any related ledger, update_record copies a claim-level change to all of them, and validate_ledger reports a claim whose records disagree across ledgers.

Typical workflow

  1. create_ledger once per work, next to the manuscript or code. For a ledger kept in an older YAML, JSON or JSON Lines file, import_ledger once instead; it never changes the source.

  2. add_record whenever a source is cited, with the claim, its anchor, the source key, locator, quote, relation and justification. The server fills in IDs, the quote hash and the provenance date.

  3. update_record, or update_records for many corrections at once (for example fixing anchors), to correct records (a quote, locator or claim wording) instead of withdrawing and re-adding them. Provenance keeps the original assessment.

  4. find_records with missing_evidence and validate_ledger before submission, to find claims still lacking evidence.

  5. check_ledger with the rendered work, its bibliography and the sources (as text or public URLs), to catch anchors, keys and quotes that no longer match.

  6. export_ledger, summarize_ledger or embed_ledger at masked or locator level to share the ledger with co-authors, reviewers or a journal.

The tool reference covers export levels, masking, embedding and every check in detail.

Available Tools

18 tools
add_recordA

Append a record. The server assigns id, claim_id (unless given), quote_hash and the provenance date, validates the result and returns the stored record. Mark the phrase carrying the claim (a number, term or short statement) with optional quote_focus. Source text belongs only in quote, quote_prefix, quote_suffix and quote_focus: write justification, locator (including a section heading) and note in your own words, since they are exported with masked and locator ledgers; a record that repeats 24 or more letters and digits of its own quote in another field is refused, naming the field. A project with several ledgers keeps ids unique across them: pass the other ledgers' paths as related every time: the new id and claim_id are numbered past those of all of them, and claim and importance must agree with the records of the same claim in them.

Inputs: path (required), record (required), provenance (required), related. Returns: result

check_ledgerA

Check a ledger without changing anything; the result holds record and claim ids, keys and counts, never quote or source text. gaps is always returned: contestable records lacking a locator, quote or justification, claims whose records are all withdrawn, and, when a bibliography is known, record keys missing from it. Optional checks, each returned only when its input is given: work reports anchors not found in it, or with only a marker, under anchors (HTML is matched on its visible text; other files on their source text, so markup inside an anchor reports not found: prefer the rendered HTML); revision lists records assessed against another or an unknown provenance.manuscript_rev; sources maps a record key to the source's full text and re-checks each quote against it (matched, not_found, no_source); source_urls maps a record key to a public http(s) URL whose text (HTML, plain text or a PDF with a text layer, up to 10 MB; hyphenation across PDF lines can make a quote not_found) the server fetches itself and adds to sources (a key in both is an error). Hosts that are not public, including via redirects, are refused; fetches that fail are listed with a short reason under quotes.fetch_failed, and their records count as no_source. bibliography is a .bib or CSL JSON file; for an HTML work without one, its ref-<key> entries serve. Withdrawn records are skipped unless include_withdrawn.

Inputs: path (required), work, sources, revision, +3 more. Returns: result

create_ledgerB

Create a ledger file (JSON Lines) with a header; work needs title and type.

Inputs: path (required), work (required), notes, description. Returns: result

embed_ledgerB

Write a copy of the work's HTML (work, rendered by Quarto, Pandoc or any tool) with each claim's anchor marked and linked to its evidence, to a new file. Both layouts end with an "Evidence" section holding one card per claim; a button switches the reader between that section and a side panel that opens the clicked claim's card (a bottom sheet on narrow screens), and layout picks the view shown first; if no anchor is located, panel falls back to endnotes and the result's layout says so. Anchors that are only a marker, not found in the text or overlapping another claim's marked text are listed under "Not located in the work" and returned in not_located (overlapping maps a claim to the one it overlaps); a record key links to #ref-<key> when the work has that bibliography entry. Levels and the publication check work as in summarize_ledger, and out is never overwritten and must be in the work's folder. A full result holds whole quotes: keep it private.

Inputs: out (required), path (required), work (required), level, +2 more. Returns: result

export_ledgerB

Export the ledger to a new file. masked keeps the first and last words of each quote, locator no quoted text; a publication check runs before writing, and out is never overwritten. max_words is allowed only for masked, from 1 to 25; None keeps the default masking. With a budget, quote_focus centres the excerpt. A full export holds whole quotes: keep it private.

Inputs: out (required), path (required), level, format, +2 more. Returns: result

find_recordsA

Find records by claim_id, source key, text in the claim, or contestable records lacking evidence (all filters combine); withdrawn records are left out unless requested.

Inputs: path (required), key, text, claim_id, +2 more. Returns: result

get_recordC

Return one record by its id.

Inputs: id (required), path (required). Returns: result

import_ledgerA

Import a ledger of any older schema version, or a pre-schema hand-built list, as a new JSON Lines ledger at path, which must not exist. The source is YAML, JSON ({"ledger": header, "records": [...]} or a plain list of records) or JSON Lines (header on the first line, one record per line). A plain list needs work (title and type); a source with a header must not be given work. The header's schema_version is upgraded step by step to this server's (a newer or unknown version is refused); records keep their ids, claim ids, provenance dates and quote hashes (an older sha256-n1 hash stays valid), and the server assigns missing ids and claim ids and the hash of a quote lacking one. Records that fail validation are still written so that they can be fixed; the result holds path, records, from_version (null for a pre-schema list), steps (such as "0.2.0→0.3.0") and problems (all found at once, as validate_ledger reports them, empty if valid).

Inputs: path (required), source (required), work. Returns: result

move_recordA

Move a record (withdrawn ones too) from the ledger at path to the ledger at to, keeping its id, claim_id and provenance, and return it. Refused when both are the same file, the id is missing from path or already used in to, or the record fails the checks of add_record against to and the related ledgers (header text, claim text or importance of its claim). The claim may then span both ledgers: pass the project's other ledgers as related to later calls so it stays consistent. Both files are rewritten together, to first, all or none (on a write error the one already replaced is restored). The id is not reused as long as related lists every ledger of the project each time.

Inputs: id (required), to (required), path (required), related. Returns: result

schema_infoA

Report the server version and the evidence ledger schema version it supports.

Inputs: no named parameters. Returns: object

summarize_ledgerA

Write a standalone HTML summary of the records, grouped by claim, to a new file. Levels and the publication check work as in export_ledger, and out is never overwritten. A full summary holds whole quotes: keep it private. For a PDF, print the HTML from a browser; its print style shows link targets and avoids splitting records where possible.

Inputs: out (required), path (required), level, include_withdrawn. Returns: result

update_recordA

Change fields of one record and return it. changes is merged into the record; a null value removes a field. id, withdrawn, quote_hash and provenance cannot be changed, and a withdrawn record cannot be edited. A new quote gets a new quote_hash; a removed one drops it. The record's provenance is not touched: it records the original assessment (who, which model, which manuscript revision, when), not later edits. A change to claim or importance is copied to every record with the same claim_id; a claim may span related ledgers, so pass their paths as related (the same list every time) to update its records there too. The same checks as add_record apply to every affected ledger, and nothing is written when one fails.

Inputs: id (required), path (required), changes (required), related. Returns: result

update_recordsA

Change fields of several records in one call and return each item's record in its final state (two items on one record return the same, fully updated record). updates is a list of {"id": "R-0001", "changes": {...}}; each changes works as in update_record, and provenance stays as it was. Updates apply in order, so a later one sees an earlier one (a duplicate id is allowed), and claim-level changes are copied to the claim's other records, also in the related ledgers (pass the same list every time). Every affected record is checked against the final state of all ledgers. If any item is refused, the error lists all refusals, each prefixed with the item index and record id, and nothing is written; otherwise the changed ledgers are rewritten together, related ones first and the edited one last: all files or none (on a write error the files already replaced are restored).

Inputs: path (required), updates (required), related. Returns: result

validate_ledgerA

Validate a whole ledger; returns the problems found, empty if valid. Pass the other ledgers of the project as related (the same list as for add_record) to also report ids used in more than one of them (R-0001: id also used in <ledger>) and claims whose records disagree on claim text or importance across them (C-0001: records disagree on claim text across related ledgers).

Inputs: path (required), related. Returns: result

validate_recordA

Validate one ledger record against the schema; returns the violations, empty if valid.

Inputs: record (required). Returns: result

withdraw_recordB

Mark a record withdrawn with a reason instead of deleting it.

Inputs: id (required), path (required), reason (required). Returns: result

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.6/5.0

    Scored across 18 tools

    Disambiguation4/5

    Most tools target clearly distinct operations across record lifecycle, ledger lifecycle, validation, and output/linking. The main overlaps are check_ledger vs validate_ledger and the export_ledger/summarize_ledger/embed_ledger family, but the descriptions provide enough differentiation.

    Naming Consistency4/5

    Names are consistently snake_case and overwhelmingly follow verb_noun patterns such as add_record, update_records, and validate_ledger. schema_info is the lone noun-only exception, which is a minor deviation.

    Tool Count4/5

    At 18 tools, the server is slightly above the typical 3–15 well-scoped band. However, the evidence-ledger domain is rich and each tool maps to a distinct operation, so the set does not feel excessively bloated.

    Completeness4/5

    Coverage is strong: create/import ledgers, add/get/find/update/move/withdraw records, validate/check, export/summarize/embed, and source-link resolution are all present. Minor gaps remain around ledger-level metadata updates or deleting an entire ledger, but core workflows have no major dead ends.

    Maintenance

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      C
      maintenance
      An MCP server providing an append-only, hash-chained evidence ledger for agent actions, where every record is a tamper-evident receipt cryptographically bound to all prior records and persisted as human-readable JSONL local state. It exposes tools to append records, verify chain integrity (pinpointing tampering), query records by actor/action/target/time, and fetch ledger stats—with no update or delete capabilities by design.
      1
      Apache 2.0
    • A
      license
      Not graded
      quality
      B
      maintenance
      Provides evidence-oriented MCP service for cryptographically identified agents, bounded public contracts, privacy-preserving records, and append-only audit.
      Apache 2.0
    • A
      license
      A
      quality
      B
      maintenance
      Free seven-tool proof loop for MCP agents: scoped sessions, bounded authorization, typed outcomes, evidence-bound claims, and tamper-evident local closure. Upgrade to Complete Local for durable memory, recovery, signed traces, release verification, and multi-agent workflows.
      7
      Apache 2.0