Skip to content

Commit 1c10c7d

Browse files
ReadycodeAIclaude
andcommitted
0.2.0: fixes from the independent review
- Calculation plans that leave part of the question unexplained ("above 150", "not at Acme") are never run; without a working check no calculation runs. Both return needs_calculation with suggested_calculate_args. - Name plus company lookups rank rows matching every named value first. - Short passages and short last lines are kept; plural/singular forms match; no shared words gives no_matching_text instead of not_in_document. - Headerless sheets keep their first record; header choice reported and overridable (headers: auto | first_row | none). - Incomplete checker answers count as unchecked; "enough" below 0.5 is low_confidence. - MCP server validates messages and negotiates the protocol version. - Demo: unique document per file choice and no stale loads, loading warnings, full results with CSV, one-click run of a checked suggestion, ReadyCode updates and early-access links. - PRIVACY.md states exactly what goes to OpenRouter; README credits related work; 18 tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 7cb1468 commit 1c10c7d

12 files changed

Lines changed: 569 additions & 115 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Changelog
2+
3+
## 0.2.0
4+
5+
Fixes from an independent review, each with a regression test:
6+
7+
- **Calculations never answer a different question.**
8+
- A plan that doesn't account for every part of the question ("salary above 150", "not at Acme", "work on solar panels") is not run, even if the checker approves it.
9+
- Without a working check, no calculation is run at all.
10+
- In both cases Reader returns `needs_calculation` with `suggested_calculate_args` for the AI to confirm, complete and run with `calculate`.
11+
- **A name and a company together find that person first.** Before, "Bob's phone at Acme" could return other Acme rows and miss Bob.
12+
- **Short text is kept.** A short page or last line ("The access code is 123456.") used to be dropped.
13+
- **Honest absence.**
14+
- A question that shares no words with a document now gets `no_matching_text` (the file may use other words), not `not_in_document`.
15+
- Plural and singular forms now match.
16+
- **Headerless spreadsheets keep their first record.**
17+
- A first row whose values reappear below is read as data.
18+
- `load_document` reports which row it used as column names.
19+
- `headers: "none" | "first_row"` overrides the choice.
20+
- **Checker answers are validated.** An incomplete check is treated as unchecked, not as a row of "no"s. An "enough" score below 0.5 is `low_confidence`.
21+
- **Sturdier MCP server.** Malformed messages (including JSON `null`) get JSON-RPC errors instead of stopping the server, and the protocol version is negotiated.
22+
- **Browser demo.**
23+
- Each chosen file is its own document, and a slower earlier load can't replace a later one.
24+
- Loading warnings are shown.
25+
- Result tables have "Show all", a CSV download and "Get the full list".
26+
- Suggested calculations can be run with one click after checking them.
27+
- It has clear links to ReadyCode updates and early access.
28+
- **Privacy.** A Reader-specific [PRIVACY.md](PRIVACY.md) states exactly what is sent to OpenRouter: the question and the candidate passages, not just the ones returned.
29+
30+
## 0.1.0
31+
32+
First release: PDF, Word and Excel through MCP and in the browser, with Jev evidence checks, citations and exact spreadsheet calculations.

‎PRIVACY.md‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# ReadyCode Reader: privacy
2+
3+
This covers ReadyCode Reader: the MCP server (`@readycode/reader`) and the browser demo. It does not cover other ReadyCode products, which have their own policies.
4+
5+
## In one line
6+
7+
Your file is read on your own computer, or in your own browser. ReadyCode never receives your file, your questions or your key. The only thing that leaves your machine is what goes to OpenRouter to check each question's evidence, sent with your own OpenRouter key.
8+
9+
## What stays on your computer
10+
11+
- **The whole file.** It is read and searched locally, and never uploaded to ReadyCode or anyone else.
12+
- **A cache of text documents** (PDF, Word and text files), so they open faster next time. It is stored in `%LOCALAPPDATA%\readycode-reader` (Windows) or `~/.cache/readycode-reader`, and you can delete it at any time. Spreadsheets are not cached. The browser demo keeps nothing after you close the page.
13+
- **A usage log**, only if you turn it on with `READER_LOG=1`. It records each question, its verdict and token counts, and stays in the same local folder.
14+
15+
## What is sent to OpenRouter, for each question
16+
17+
Reader checks each answer with TypeSafe's Jev decision model (`typesafe/jev-1.13`) through OpenRouter's decisions API, using **your** OpenRouter key. Each question sends:
18+
19+
- **the question**;
20+
- **the candidate passages the local search found**, before any are filtered: up to about 20, or 30 for "list everyone" questions. Each is about 1,200 characters of document text, or one spreadsheet row of up to about 3,000 characters (often fewer columns, when the question only needs some);
21+
- **for spreadsheet calculation questions**, a one-line description of the calculation Reader plans to run. When no row matched the question's words, the sheet's column names are sent instead of rows.
22+
23+
Reader sends nothing else from the file. Exact calculations (counts, totals, lists) and the explicit `calculate` tool run entirely on your computer and send nothing. OpenRouter's and the model provider's own terms and privacy policies apply to what they receive; see [openrouter.ai/privacy](https://openrouter.ai/privacy). If a document is confidential, check whether those terms allow it before asking questions about it.
24+
25+
Requests identify the app as "ReadyCode Reader" (standard OpenRouter attribution headers). They carry no information about you beyond your own key.
26+
27+
## Your key
28+
29+
- **MCP server:** the key is read from the `OPENROUTER_API_KEY` environment variable, or, on Windows, from your user environment settings. It is kept in memory only: never written to disk, logged, or sent anywhere except OpenRouter.
30+
- **Browser demo:** the key you paste stays in that page's memory. It is not saved, not put in the address bar, and sent only to OpenRouter.
31+
32+
## The browser demo's hosting
33+
34+
The demo is a static page hosted on GitHub Pages. GitHub may log standard web-server data (such as IP addresses) for visits; see GitHub's privacy statement. The page has no analytics or tracking scripts and sets no cookies. Links to readycode.ai take you to our website, which has its own privacy policy.
35+
36+
## Contact
37+
38+
Questions: readycodeai@gmail.com, or open an issue at [github.com/ReadycodeAI/readycode-reader](https://github.com/ReadycodeAI/readycode-reader/issues).

‎README.md‎

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@
66

77
[ReadyCode.AI](https://readycode.ai/reader) has contributed ReadyCode Reader - a free, open-source [MCP](https://modelcontextprotocol.io) server for Claude Code, Cursor, Codex and any other MCP client, and it also runs in a browser. It reads a file once on your computer. Then, for each question, it returns **only the passages that answer it**, with where they came from, plus checks on that evidence:
88

9-
- **Not in the document:** if the file can't answer, your AI is told `not_in_document` instead of getting passages to guess from.
9+
- **Not in the document:** if none of the passages checked supports an answer, your AI is told `not_in_document` instead of getting passages to guess from. If no passage even shares a word with the question, it is told `no_matching_text`, since the file may use other words.
1010
- **Passages disagree:** if two passages give different values, your AI is told to report both.
1111
- **Hidden instructions:** passages that try to instruct the AI reading them are flagged and treated as data only.
1212
- **Exact spreadsheet calculations:** "how many…", "which … appears most often", totals, averages and "list everyone who…" are computed by code over every row and come back as numbers, with the sheets, column and conditions used. A model never estimates them.
1313
- **Exact matches first:** a spreadsheet row holding the exact name you asked about comes before look-alikes ("Nat Becker" before "Prof. Nat Becker II"), and rows show only the columns the question needs.
1414

15+
**[Try it in your browser](https://readycodeai.github.io/readycode-reader/)** (your file stays on your computer) · **[Get Reader updates and early access to the hosted version](https://readycode.ai/reader)** · [Install](#install-mcp)
16+
1517
## Standard AI vs Reader
1618

1719
Same questions, same AI, fresh sessions: once the normal way (the AI reads the file with its own tools), once with Reader.
@@ -31,9 +33,9 @@ Every expected answer below was checked by code against the file, and every "not
3133

3234
| File | Size as text | Correct | Tokens returned | Time for all questions | Check cost |
3335
|---|---|---|---|---|---|
34-
| NASA *Earth at Night* (PDF, 200 pages) | 42,282 tokens | 8/8 | 3,487 | 1.6 s | $0.0027 |
35-
| Australian Universities Accord Final Report (Word) | 263,487 tokens | 11/11 | 14,991 | 1.1 s | $0.0044 |
36-
| Sample contacts workbook (Excel, 100 MB, 1.1 million rows) | 271,424,159 tokens (estimate) | 11/11 | 2,494 | 2.4 s | $0.0022 |
36+
| NASA *Earth at Night* (PDF, 200 pages) | 42,306 tokens | 8/8 | 4,150 | 1.9 s | $0.0028 |
37+
| Australian Universities Accord Final Report (Word) | 263,691 tokens | 11/11 | 15,336 | 1.2 s | $0.0044 |
38+
| Sample contacts workbook (Excel, 100 MB, 1.1 million rows) | 271,424,159 tokens (estimate) | 11/11 | 2,494 | 3.3 s | $0.0022 |
3739

3840
The Word report is larger than many AI context windows, and the spreadsheet is more than a hundred times larger than any. Each set includes questions the file cannot answer; Reader said `not_in_document` every time.
3941

@@ -87,7 +89,7 @@ Ask your AI something like:
8789
8890
Your AI loads the file, sees its contents, asks Reader specific questions and answers from the cited passages. You don't need to know the file or write special questions.
8991

90-
- `load_document(path)` reads a PDF, Word (.docx), Excel (.xlsx), TXT, Markdown, CSV or JSON file. It returns the file's size and **its contents**: a PDF's bookmarks with page numbers, the Word headings, or a spreadsheet's sheets, columns and an example row. That tells the AI what the file covers before it asks anything.
92+
- `load_document(path)` reads a PDF, Word (.docx), Excel (.xlsx), TXT, Markdown, CSV or JSON file. It returns the file's size and **its contents**: a PDF's bookmarks with page numbers, the Word headings, or a spreadsheet's sheets, columns and an example row. That tells the AI what the file covers before it asks anything. For spreadsheets it says which row it took as column names; `headers: "none"` reads every row as data, and `headers: "first_row"` always uses row 1.
9193
- `ask_document(questions: [...])` asks up to 20 questions in one call. Each answer carries its passages with page numbers (and printed page numbers), Word sections and paragraphs, or spreadsheet sheet and row.
9294
- `calculate(operation, column, where, sheet, unique_by, n)` runs an exact calculation over a loaded spreadsheet: `count`, `distinct`, `top` (most or fewest), `sum`, `average`, `min`, `max` or `list`. It takes conditions (equals, contains, not equals, above, below), one sheet or all, and can count people instead of rows (`unique_by: "Name"`). You rarely need it: `ask_document` turns plainly worded questions into these plans, has each plan checked against the question, and answers with verdict `calculated`.
9395
- `list_documents()` lists what is loaded.
@@ -104,6 +106,18 @@ A calculated answer looks like this:
104106
"result": 9, "unique": 1, "method": "Computed by code over every row; nothing estimated." } }
105107
```
106108

109+
Each answer has a verdict:
110+
111+
| Verdict | Meaning |
112+
|---|---|
113+
| `answer_from_passages` | The passages returned answer the question. |
114+
| `low_confidence` | The passages may not fully answer it; answer only what they state. |
115+
| `not_in_document` | None of the passages checked supports an answer. For a spreadsheet, no row holds the words asked about. |
116+
| `no_matching_text` | No passage shares a word with the question. Ask again with other words before concluding it isn't there. |
117+
| `calculated` | An exact number computed by code over every row, with the plan used and `calculate_args` to re-run or adjust it. |
118+
| `needs_calculation` | A calculation is needed but Reader could not confirm one that covers the whole question (for example "salary above 150" or "not at Acme"). It gives no number; it offers `suggested_calculate_args` for your AI to check, complete and run with `calculate`. |
119+
| `unchecked` | The check could not run (no key, or the service failed). Search results are returned as they are, and no calculation is run. |
120+
107121
Very large files keep reading in the background: `load_document` may answer `still_reading` with how far it has got, and `ask_document` waits for the file to finish. `load_document` also reports what was read (every sheet and row, and anything skipped), so a "not in the document" answer can be trusted. `document_tokens` is an estimate of the whole file's size as text (about 3.5 characters per token), for comparison; it is not model usage.
108122

109123
## Try it in your browser
@@ -133,15 +147,24 @@ The same engine runs in a web page: choose a file, add your OpenRouter key, ask.
133147
- **Spreadsheet calculations** cover counts, distinct values, rankings, totals, averages, lowest and highest values, and lists, with simple conditions. Formulas, pivot tables, date ranges and combining sheets with different columns are not supported yet. When Reader can't turn a question into a plan it is sure of, it answers `needs_calculation` rather than guess, and your AI can call `calculate` directly.
134148
- **The "passages disagree" check** now needs two relevant passages about the same thing, but it can still fire when passages don't really disagree. Treat it as "double-check these sources".
135149
- **Old formats** (`.doc`, `.xls`, `.xlsb`) aren't supported; save them as `.docx` or `.xlsx`.
136-
- **It depends on Jev.** If the decision model is unavailable, Reader still returns the top search results, marked `unchecked`.
150+
- **It depends on Jev.** If the decision model is unavailable, Reader still returns the top search results, marked `unchecked`, and runs no calculation on its own: it suggests one for your AI (or you) to confirm.
151+
- **Search is by words.** Plural and singular forms match, but synonyms don't ("car" won't find "automobile"). That's why a question sharing no words with the file gets `no_matching_text`, not `not_in_document`.
152+
- **Header rows are guessed.** A first row of short, distinct labels is taken as column names unless its values appear again below. `load_document` says which row it used, and you can override it.
137153
- **Check the passages.** Every answer comes with its sources so you can.
138154

139155
## Privacy
140156

141-
- The whole file is read on your computer (or in your browser) and never uploaded to ReadyCode.
142-
- Only the short passages a question needs (about 1,200 characters each) go to OpenRouter's decision model, using your own key.
143-
- Your key is read from your environment (or kept in the web page's memory) and is never written to disk, logged or sent to ReadyCode.
144-
- Documents are cached on your computer (`%LOCALAPPDATA%\readycode-reader` or `~/.cache/readycode-reader`). A local usage log is kept only if you set `READER_LOG=1`.
157+
- The whole file is read on your computer (or in your browser) and never uploaded to ReadyCode. ReadyCode never receives your file, your questions or your key.
158+
- For each question, the question and the candidate passages found by the local search go to OpenRouter's decision model with your own key. That's up to about 20 passages of about 1,200 characters, or spreadsheet rows. For calculation questions, a one-line description of the planned calculation (or the column names) goes too.
159+
- Exact calculations run entirely on your computer and send nothing.
160+
- Your key is read from your environment (or kept in the web page's memory) and is never written to disk, logged, or sent anywhere but OpenRouter.
161+
- Text documents are cached on your computer (`%LOCALAPPDATA%\readycode-reader` or `~/.cache/readycode-reader`). A local usage log is kept only if you set `READER_LOG=1`.
162+
163+
Full details: [PRIVACY.md](PRIVACY.md).
164+
165+
## Credits and related work
166+
167+
Reader combines established pieces rather than inventing a new retrieval method. The search-then-check pattern (a keyword shortlist, then Jev relevance judgments) follows TypeSafe's own guidance for Jev. Others have built Jev-based PDF search, MCP document retrieval and MCP spreadsheet operations. What Reader adds is one small, local tool covering PDF, Word and Excel. It combines evidence checks (relevance, enough to answer, disagreement, hidden instructions), citations, honest "not found" answers and exact spreadsheet calculations, through MCP or in a browser.
145168

146169
## Licence
147170

‎benchmark/README.md‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ For a question that asks for a complete list, the headings Reader returns in `ne
3131

3232
| File | Correct | Document tokens | Tokens returned | Load | All questions | Check cost |
3333
|---|---|---|---|---|---|---|
34-
| NASA PDF | 8/8 | 42,282 | 3,487 | under 1 s (cached) | 1.6 s | $0.0027 |
35-
| Word report | 11/11 | 263,487 | 14,991 | under 1 s (cached) | 1.1 s | $0.0044 |
36-
| Excel workbook | 11/11 | 271,424,159 (estimate) | 2,494 | 16.2 s | 2.4 s | $0.0022 |
34+
| NASA PDF | 8/8 | 42,306 | 4,150 | 0.6 s | 1.9 s | $0.0028 |
35+
| Word report | 11/11 | 263,691 | 15,336 | under 1 s (cached) | 1.2 s | $0.0044 |
36+
| Excel workbook | 11/11 | 271,424,159 (estimate) | 2,494 | 14.7 s | 3.3 s | $0.0022 |
3737

3838
On the workbook, the 8 lookups return only the columns each question needs (13–40 tokens each). The 3 calculations are exact:
3939

@@ -43,7 +43,7 @@ On the workbook, the 8 lookups return only the columns each question needs (13
4343

4444
The list is 1,934 of the 2,494 tokens.
4545

46-
Earlier results on the same day, before exact calculations, column trimming and the list change: Word 15,790 tokens returned; Excel 2,323 tokens returned, with the 3 calculation questions declined (`needs_calculation`) rather than answered.
46+
Since these runs Reader keeps short passages it used to drop (a short last line, a short page) and matches plural and singular forms, so the PDF and Word files return a little more text (3,487 → 4,150 and 14,991 → 15,336 tokens) with the same scores. Earlier results on the same day, before exact calculations, column trimming and the list change: Word 15,790 tokens returned; Excel 2,323 tokens returned, with the 3 calculation questions declined (`needs_calculation`) rather than answered.
4747

4848
## Standard AI vs Reader (NASA PDF)
4949

‎package-lock.json‎

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@readycode/reader",
3-
"version": "0.1.0",
3+
"version": "0.2.0",
44
"description": "Ask huge PDFs, Word documents and spreadsheets specific questions: your AI gets only the passages that answer, with citations. A local MCP server for Claude Code, Cursor and Codex, plus a browser version.",
55
"bin": {
66
"readycode-reader": "src/server.cjs"
@@ -10,7 +10,9 @@
1010
"src",
1111
"README.md",
1212
"LICENSE",
13-
"NOTICE"
13+
"NOTICE",
14+
"PRIVACY.md",
15+
"CHANGELOG.md"
1416
],
1517
"engines": {
1618
"node": ">=20"

0 commit comments

Comments
 (0)