Repository navigation
Releases: matthiaskloft/evidence-ledger-mcp
Release list
v0.5.0
Added
import_ledgerconverts a ledger of any older schema version, or a pre-schema hand-built list, kept as YAML, JSON or JSON Lines into a new JSON Lines ledger at the current version (the result listsfrom_versionand the upgradesteps; a newer or unknown version is refused), keeping ids, claim ids, provenance dates and quote hashes, and reports the problemsvalidate_ledgerfinds in it (#49). PyYAML is now a runtime dependency.update_recordsapplies a list of{id, changes}updates in order and rewrites the ledger once: later updates see earlier ones, every affected record is checked, all refusals are listed together (prefixed with item index and record id) and nothing is written if any fails.update_recordis the single-record form (#48). Both takerelated: claim-level changes also reach the claim's records in the other ledgers (see below).update_recordchanges fields of a record in place (#48):changesis merged into the record and a null value removes a field.id,withdrawn,quote_hashandprovenancecannot be changed, and a withdrawn record cannot be edited. A newquotegets a newquote_hash, a removed one drops it, and an unchanged one keeps its hash. The record's provenance is not changed: it records the original assessment, not later edits. A change toclaimorimportanceis copied to every record with the sameclaim_id, withdrawn ones included, which keep their provenance. The checks ofadd_recordapply and nothing is written when one fails.- Several ledgers per project (#50):
add_recordtakesrelated, a list of other ledger paths, and numbers the newidandclaim_idpast those of this ledger and all of them.add_recordalso checksclaimandimportanceagainst the records of the same claim in the related ledgers.validate_ledgertakesrelatedtoo and reportsR-xxxx: id also used in <ledger>andC-xxxx: records disagree on claim text across related ledgers(oron importance).update_recordandupdate_recordstakerelatedand copy aclaimorimportancechange to the claim's records there; every check runs before any file is written, then the changed ledgers are rewritten together (related ones first, the edited one last): all files or none, and on a write error the files already replaced are restored from their original content. The newmove_record(also withrelated) moves a record (withdrawn ones too) to another ledger, keeping itsid,claim_idand provenance; it refuses the same file, an unknown id, an id already in the target and a record that fails the checks of the target or the related ledgers, and writes the target first and the source second with the same all-or-none rewrite, so a failed source write leaves no duplicate. Per-ledger id prefixes would need an upstream schema change, so ids are kept unique across related ledgers instead; pass arelatedlist with every ledger of the project every time, otherwise an id can be reused.
Changed
add_recordrefuses a record whoseclaimdiffers from another record on the sameclaim_id, as it already did forimportance, andvalidate_ledgerreportsC-xxxx: records disagree on claim text(#51).- Server instructions let an agent file a bug report or comment on an existing issue itself, but only after the user has reviewed the full draft and approved it, and after searching the open and closed issues and the roadmap for a duplicate. Before, they said never to file it.
Published by the repository's GitHub Actions release workflow.
v0.4.0
Added
resolve_linksandrelink_htmltakelink_map, the path of a.private.jsonfile that maps citation keys to PDFs (and optionally to a link that opens the item in the reader's library), as an alternative or addition tofolder(#57). The map is validated againstlink-map.schema.json, shipped in the package. Keys missing from the map are reportednot_in_mapunlessfolderis also given. Cards in a relinked copy get an "Open in library" button, with the page appended.- A folder file named
<citation key>.pdfis now tried before DOI, ISBN and title matching (#57). - Summaries and embedded endnotes open with a list of the claims, each linked to its card with a dot per record coloured by relation (#57).
- An embedded work can be switched between endnotes and the side panel with a button in its hint line; the reader's choice is remembered and
layoutonly sets the view shown first (#57). - Tool descriptions, server instructions and
docs/tools.mdrecommend asources/folder of only the cited PDFs, kept out of version control, warn against pointingfolderat a whole library, and describe building the map with Zotero and Better BibTeX (#57).
Changed
- Summary and embedded evidence cards share one calmer style: a 70-character reading width, two text sizes below the headings, the passage in the work styled like the quote with only its focal text underlined (also
quote_focusin quotes), and the record details, including who added it, in one label/value list. The "Your copy" line of a relinked copy has small matching "Open PDF" and "Open in library" buttons that open in a new tab, with the file and page in their tooltip and a note only when the passage or PDF was not found (#57).
Published by the repository's GitHub Actions release workflow.
v0.3.1
Fixed
resolve_linksmarks a whole quoteacross_pageswhen it runs cleanly across a page break, not only when a running head, caption or sidebar sits between its two halves (#43). Theresolve_linkstool description says so too.- The server instructions no longer repeat the fragment "server's working directory." after the source-text paragraph (#44).
Published by the repository's GitHub Actions release workflow.
v0.3.0
Fixed
validate_ledgerreads the headerlevel: it accepts the masked and locator exportsexport_ledgerwrites (a masked quote no longer fails the hash comparison, a locator record needs noquote) and reports source text a hand-edited export reintroduces (quote,quote_prefix,quote_suffixorquote_focusin a locator export, a masked quote over its word budget or another source field in a masked one). Validation cannot catch source text copied intoclaimorjustification; the publication check at export time remains the guard.
Added
- Server instructions tell agents to stop and tell the user when a tool fails or misleads, to suggest, never file, a bug report reproduced with an invented record, and to keep workflow failures in the ledger. A bug report issue template collects the details.
schema_infoalso reports the server version.
Changed
- Source text stays in the quote fields: the server instructions and the
add_recorddescription say thatjustification,locator,noteand the header'sdescriptionandnotesare written in the agent's own words, andadd_recordnow refuses a record whose fields outside the quote fields (and the work'sclaimandanchor) repeat 24 or more letters and digits of its ownquote,quote_prefix,quote_suffixorquote_focus, naming the field; the header'sdescriptionandnotesare checked against the new record too, andvalidate_ledgerreports the same overlaps, header included, for existing records. A section heading inlocatorthat repeats the quote is refused as well, consistent with the export. The same rule already blocked masked and locator exports. The vendored schema carries no field descriptions for these; they belong upstream.
Published by the repository's GitHub Actions release workflow.
v0.2.0
Fixed
- Fetching refuses IPv6 addresses that carry a non-public IPv4 address (6to4, local-use NAT64, IPv4-compatible and Teredo forms), as it already did for IPv4-mapped and well-known NAT64 ones.
- Link resolution: reading PDFs stops after 300 seconds per call, reported under
time_limit, so a very large folder no longer blocks the server for hours; DOIs with adoi:orhttp://dx.doi.org/prefix are matched;relink_htmlnames the copy of a file without a suffix<name>.private.htmlinstead of a hidden.private.html; and neither tool replaces an existing file it did not write. - PDF text extraction no longer hangs inside the running MCP server on Windows: the extraction process now gets its own standard streams instead of inheriting the server's stdin, which the server is blocked reading. This affected
check_ledgerwith PDFsource_urls.
Added
- Link resolution is tested against three real open-access PDFs, fetched from their publishers and cached locally (never committed) by tests marked
network, covering two-column layouts, ligatures, running heads, sidebars and captions at page breaks, printed page labels. Whole quotes split by a page break are now found (across_pages), and short bibliography titles match together with the first author's family name. - Private relinked copies (roadmap 0.6):
relink_htmlreads the records a summary or embedded HTML file carries and writes<name>.private.htmlnext to it, whose evidence cards link to the reader's own PDFs, at the page where the passage was found or else at the citedlocator.page(through the PDF's printed page labels when it has them). The copy is marked private in its name, title, a banner and anoindextag; links are relative when the PDFs are inside the copy's folder, and absolutefile://links otherwise.summarize_ledgerandembed_ledgernow add a non-executable JSON block with each record'sid,key, identifiers,locator,quote(at the export's level) andquote_hash, and mark each card withdata-record.resolve_linksnow requires a.private.jsonlink file and records the cited page where no passage is found (page_from). - Link resolution, PDF folders (roadmap 0.5):
resolve_linksmatches each record's source to a PDF in a folder the reader names (by DOI, ISBN or bibliography title on its first pages, whatever the file is named; as last resorts, a file named after thekey, or one named after the first author and year, such asSmith 2020.pdf), finds the quoted passage byquote_hash, from a whole quote or one masked to its first and last words (not a focus window from the middle of a quote, nor a masked quote across a page break), and writes a local link file with the file and page per record. Records are reported asfound,passage_not_found,source_not_foundornot_searchable; unreadable PDFs are listed with a short reason. No source text is returned. - Embedded rendering, HTML part (roadmap 0.4):
embed_ledgerwrites a copy of the work's HTML in which each claim's anchor text is marked and linked to an evidence card, asendnotes(an Evidence section at the end) orpanel(the same end section plus, with JavaScript, a side panel that opens the clicked claim's card: a sidebar with the first claim open on wide screens, a bottom sheet on narrow ones; it falls back toendnotes, reported inlayout, when no anchor is located). The output sets its own page-wide light/dark theme and adds a hint line, claim-ID superscripts and the end section. Anchors are matched after normalising typography and preferring word boundaries; unlocated anchors, including those overlapping another claim (overlapping), are listed without work text at the masked and locator levels; record keys link to#ref-<key>bibliography entries when the work has them. Levels and the publication check work as insummarize_ledger; an existing file is never overwritten. - Checks (roadmap 0.5):
check_ledgerreads a ledger and reports, as record IDs, claim IDs, keys and counts only, the gaps (contestable records lacking locator, quote or justification, claims with all records withdrawn, record keys missing from the bibliography), anchors not found in the work or with only a marker (HTML, Markdown, Quarto, LaTeX or text), records assessed against another or an unknown manuscript revision, and quotes not found in source text passed in by the agent. The bibliography is a.bibor CSL JSON file, or theref-<key>entries of an HTML work. Anchors in non-HTML works are matched against the source text, so markup inside an anchor reports not found; the rendered HTML is the better check. It writes nothing. - Fetching sources (roadmap 0.5):
check_ledgertakessource_urls, mapping a recordkeyto a public http(s) URL whose text (HTML or plain text, up to 10 MB) the server fetches itself and re-checks quotes against, alongsidesources. Hosts resolving to non-public addresses are refused, including on every redirect (at most 5); failures are reported per key underquotes.fetch_failedwith a short reason, and never end the check. Fetched text is neither returned nor stored. A key in bothsourcesandsource_urlsis an error. The tests run offline; one test that fetches a real page is markednetworkand runs in its own non-blocking CI job. - Fetching PDFs (roadmap 0.5):
source_urlsalso accepts a URL that serves a PDF; its text layer is extracted withpypdf(new dependency, 6.21.4 or later) in a child process stopped after 15 seconds, up to 2000 pages and 20 million characters.application/x-pdfand octet-stream responses with a PDF header count as PDFs. PDFs needing a password, PDFs without text (scans), unreadable, too slow and too long ones are reported underquotes.fetch_failed(encrypted pdf,no text in pdf,unreadable pdf,pdf too slow,pdf too long). A word hyphenated across lines can make its quotenot_found.
Changed
- Move canonical schema development and future schema releases to
evidence-ledger-schema. Keep a pinned vendored schema for offline builds and validation, with its upstream revision and file hashes recorded inschema/UPSTREAM.json. Original schema tags remain historical records.
Published by the repository's GitHub Actions release workflow.
v0.1.0
Added
- MCP server with the tools
schema_infoandvalidate_record, linting (ruff), type checking (mypy, strict), tests, and CI and release workflows. - Core ledger tools (roadmap 0.1):
create_ledger,add_record,get_record,find_records,validate_ledgerandwithdraw_record, storing ledgers as JSON Lines. The server assigns record and claim IDs, computesquote_hashand fills in the provenance date. - Export (roadmap 0.2):
export_ledgerwrites the ledger as JSON or JSON Lines at the levelfull,maskedorlocator. Every level dropslocator.chunk. For masked and locator exports, a publication check runs before writing and rejects metadata sharing a run of 24 normalized letters or digits with a quote, prefix, suffix or focus, exempting the claim, anchor and permitted masked quote; an existing file is never overwritten. - Optional
quote_focuson records: validated as a unique phrase covering whole whitespace-delimited words, with at most 25 words. - Optional
max_words(1–25) for masked exports: keeps a window aroundquote_focus, or the start and end without a focus. Export headers recordlevelandmax_words; masked and locator exports removequote_focus. - Ledger summary (roadmap 0.3):
summarize_ledgerwrites one self-contained HTML page of the records grouped by claim, at the levelfull,maskedorlocator, with the same publication check asexport_ledger. All annotations are visible text, and a print stylesheet makes it printable to PDF from a browser; the server writes no PDF. - MIT licence for the MCP server and its documentation; the bundled schema is separately licensed under CC BY 4.0.
Changed
- New records use
sha256-n2quote hashes (NFKC, casefold, NFKC again, letters and digits only). Validation still checks legacysha256-n1hashes. The server now supports schema v0.4.0. - The publication and repository privacy checks also inspect
quote_focusfor copied source text.
Published by the repository's GitHub Actions release workflow.
— Codex (GPT-6)
Evidence ledger schema v0.4.0
-
Define n2 quote hashing: Unicode NFKC, casefold, NFKC again, then retain letters and digits. Legacy n1 hashes remain checkable.
-
Add optional
quote_focus, requiring aquote. The focus identifies a unique phrase of at most 25 whole whitespace-delimited words. The server checks these semantic constraints in addition to JSON Schema validation. -
Add export header fields
levelandmax_words. An integer word budget is limited to 1–25 and requires the masked level; null preserves default masking. -
License the schema under CC BY 4.0; see
LICENSEin this directory.
The MCP server v0.1.0 supports this schema. This release does not change the schema version beyond the v0.4.0 already recorded in its identifier and title.
— Codex (GPT-6)
The canonical schema repository and current release page are now at evidence-ledger-schema. This original release remains a historical record.