vortx-ai/emem/emem-document-evidence
Claude Code and agent skill by vortx-ai/emem
- community
Checking that the SKILL.md file is still in the repository.
Turns a scanned document into signed, checkable evidence with emem. It runs OCR on an image. Then it parses a pesticide-residue lab report: analytes, results in mg/kg, LOQ, printed MRL, exceedances. Or it parses a land record: owners, parcel ids, areas in hectares, places, dates. Each value carries its line and byte offset, and emem signs each step. Use when an agent must extract fields from a lab certificate, a 7/12 extract, a khasra or a CAR record. Use it when the agent must hand them on with proof of the image and text they came from. Ships an offline verifier.
Install
npx vskill@latest install vortx-ai/emem/emem-document-evidenceSKILL.md
emem-document-evidence
| Use it when | An agent must read fields from a residue lab report or a land record, and must show which image and text each field came from. | |
| You need | A PNG, JPEG, TIFF, WebP, GIF or BMP image of 8 MiB or less, or the document text. No key. | |
| You get | The OCR text with a signed receipt, the parsed fields with a second signed receipt, and the line and byte offset of each value. | |
| Calls | POST /v1/ocr (MCP emem_ocr), POST /v1/lab_report_parse, POST /v1/land_record_parse. |
Network use. The commands in this skill send JSON tohttps://emem.devand receive JSON from it.https://emem.devis the service that the MCP server of the plugin connects to. The commands do not execute what they download. The files underscripts/read local files and make no network calls.
Each call signs its output. A hash chains each call to the call before it:
| Call | What the receipt signs | Provenance | |
|---|---|---|---|
POST /v1/ocr | The image (BLAKE3), the source, the language, the engine and the text that the engine produced. | model_output: a reading, not a fact. | |
POST /v1/lab_report_parse | The text hash, and the hash of the fields that a deterministic parser read from that text. | deterministic_index | |
POST /v1/land_record_parse | The same, for a land record. | deterministic_index |
The responder does not store the document or the result. Each receipt is complete in itself. The verifier in step 3 needs only the response, and the document if you have it.
Limits
These limits come from crates/emem-api-rest/src/reader.rs. A responder operator can change the two quotas.
| Limit | Value | |
|---|---|---|
| Image size | 8 MiB (8 << 20 bytes). A larger image returns HTTP 400 too_large. | |
| OCR calls | 200 for each client IP each day (EMEM_OCR_DAILY_QUOTA). A parse that runs OCR counts against this quota. | |
Parse calls with text | 2000 for each client IP each day (EMEM_DOC_PARSE_DAILY_QUOTA). | |
| OCR at the same time | 2 for each responder. |
Step 1: run OCR on the image
Build the request body from the image:
The body has this shape:
Send ocr_req.json as the body of POST https://emem.dev/v1/ocr. Through MCP, call emem_ocr with the same object as its arguments. emem_ocr is not in the short tool list of the plugin. The server runs it by name through tools/call. Save the answer as ocr.json. Then read it:
You can send "url" instead of "image_b64". Send one of the two, not both. lang takes Tesseract language codes. Join two or more with +, for example eng+hin+mar. The default is eng.
Read the text before you trust a field parsed from it. The OCR receipt proves which image and engine produced the text. It does not prove that the reading is correct.
Step 2: parse the text
Each parse route takes one of these inputs:
| Input | What the parser does | |
|---|---|---|
ocr | Takes the full /v1/ocr response. The parser checks that this responder signed it and that text hashes to text_blake3_b32. This binds the parse to the signed reading. | |
text | Parses your text. No OCR receipt is in the chain. | |
url or image_b64 | Runs OCR and then parses, in one call. The OCR response is in the ocr field of the result. |
The parse routes have no MCP tool. Use REST. To parse the OCR result from step 1:
Send parse_req.json as the body of POST https://emem.dev/v1/lab_report_parse. Save the answer as lab.json. Then read it:
To parse your own text and compare each row with the EU MRL in force, add product:
The body has this shape:
Send text_req.json as the body of POST https://emem.dev/v1/lab_report_parse. Save the answer as lab_text.json. Then read it:
For a land record, send the same body to POST https://emem.dev/v1/land_record_parse. When the parse runs OCR for a land record, the default language is eng+hin+mar.
Lab report fields
Field in result | Content | |
|---|---|---|
rows[] | One row for each analyte: analyte, result (kind is value, not_detected or below_limit, with raw), result_mg_kg, loq_mg_kg, mrl_mg_kg, exceeds_printed_mrl, unit. | |
verdict | exceeds_printed_mrl, within_printed_mrls, undetermined_rows (a row has a printed MRL that the parser cannot compare), no_mrl_printed or no_rows_found. A product lookup that finds an exceedance sets exceeds_eu_mrl. | |
exceedances | The number of rows above the printed MRL. | |
sample_id, report_date | The labelled value, or null when no label says so. | |
dates[] | Each date on each line, with its label and a day-first iso reading. iso is null for a date that does not exist. | |
accreditation, methods, columns, header_line_no | What the report prints about the laboratory, the methods and the table header. | |
eu_mrl_lookup | Only with product. status is applied, product_not_found, product_ambiguous (with candidates) or unavailable. It also gives the product code, rows_matched and rows_unmatched. |
Without product, the parser compares each result only with the MRL that the report prints. With product, the responder reads the MRLs in force from the EU Pesticides Database (DG SANTE, Regulation (EC) No 396/2005). Then it adds eu_mrl to each matched row. The reading text in the response still says that the parser does not check the EU MRL database. That text is out of date for a call with product.
Land record fields
Field in result | Content | |
|---|---|---|
owners | Owner names. | |
parcel_ids | Survey, gat, khasra, CAR and matricula numbers. | |
areas | Each area with value.raw, value.unit and value.hectares. | |
places, village, taluka, district | One place for each label, and the three admin names as separate fields. | |
dates | Each date with its line. | |
fields_found, fields_missing | What the parser found, and what it did not find. It does not fill a missing field. |
The parser converts acres, gunthas and a three-part 7/12 area such as 1.20.50 (read as ha.are.m2) to hectares. It reports bigha raw, because the size of a bigha changes from place to place. It reads text in the Indian scripts, Portuguese, Spanish, French and Indonesian.
Each extracted value carries line, line_no and byte_offset. Use line to quote the exact line. line_no counts from 0.
Step 3: verify offline
scripts/verify_doc.py ships with this skill. Claude Code sets ${CLAUDE_SKILL_DIR}. The script makes no network calls. It gets BLAKE3 and Ed25519 from scripts/emem_crypto.py, which also ships with this skill. Both are plain Python with no third-party packages.
The second argument is optional. For an OCR response it is the image. For a parse response it is the text file.
| Line | What it checks | |
|---|---|---|
image MATCH | The image file hashes to image_blake3_b32. | |
text MATCH | The text hashes to text_blake3_b32. | |
result MATCH | The result object hashes to result_blake3_b32. | |
sig VALID | The Ed25519 signature verifies over the rebuilt preimage. | |
ocr VALID, ocr MATCH | For a parse that carries its OCR response: the OCR signature verifies, and the parsed text is the text that the OCR receipt signs. |
A failed check prints MISMATCH or INVALID. The exit code is 0 when every check passed, 1 when a check failed, and 2 for bad input.
The script checks the signature with the key in the response. Compare the signer that it prints with the published key of the responder:
Read responder.pubkey_b32 in the answer.
On 7 November 2026 this returned 777er3yihgifqmv5hmc2wwmyszgddzderzhsx6rex4yoakwomvka.
Example run on 7 November 2026
The test image was a rendered residue report with three analytes and a printed MRL of 0.01 mg/kg. It also had Test Report No: ER/2026/0912 and Sample ID: BAS-RICE-044. The text in report.txt was the same.
| Path | Result | |
|---|---|---|
OCR (tesseract 5.5.0), then parse with ocr | Tesseract put the LOQ and MRL columns in separate blocks. Each row came back with mrl_mg_kg: null, and the verdict was no_mrl_printed. All hashes and both signatures verified. | |
Parse with text and product: "rice" | Tricyclazole 0.012 mg/kg against the printed MRL 0.01: exceeds_printed_mrl: true. The EU lookup matched 3 of 3 rows to product 0500060 Rice, with MRL 0.01* for each. The verdict was exceeds_eu_mrl, with 1 exceedance. verify_doc.py printed text MATCH, result MATCH and sig VALID. |
Both paths gave sample_id BAS-RICE-044. A run on 28 September 2026 gave the report number there. The parser no longer makes that mistake.
The OCR path shows the purpose of this skill. The chain proved exactly which image and text gave the answer. It did not make the reading correct. When a verdict depends on a column, find that column in the OCR text.
After the run, a test changed one result value in lab_text.json. The script then printed result MISMATCH and exited with 1.
Errors
The error body has schema: "emem.error.v1". The top-level code is invalid_argument for the 400 and 422 rows, and source_fetch_failed for the 502 rows. Read the specific code in details.code.
This is the start of the file. Read the full SKILL.md on GitHub.
Security
- Security scanPassed, 90 of 100Pattern checks on the skill's files.
- Model reviewPassed
Quality evaluation
- Task evaluationNot runNo evaluation yet. Run one in Skill Studio.
Source
- SKILL.md in the repositoryCheckingplugins/emem/skills/emem-document-evidence/SKILL.md
Popularity
- 7-day trend score
- 0
Not refreshed yet.
Works with
Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.
- Claude Code
- Cursor
- GitHub Copilot
- Windsurf
- Codex
- Gemini CLI
- Cline
- Amp
- Roo Code
- Goose
- Continue
- Kilo Code
- Junie
- Trae
- OpenHands
- Qwen Code
- AI Agent
- Tabnine Chat
- Sourcegraph Cody
- Amazon Q
- Firebase Genkit
- Sweep
- Devin
- Phind
- Mentat
- Claude Desktop
- Aider
- GPT Pilot
- Plandex
- bolt.new
- v0
- Replit Agent
- Anse
- TypingMind
- BetterChatGPT
- LibreChat
- Warp AI
- Pieces
- Fig AI
Embed badge
[](https://verified-skill.com/skills/vortx-ai/emem/emem-document-evidence)