
Research
/Security News
77 Firefox Extensions Linked to Crypto Wallet and Credential Theft
Socket uncovered 77 linked Firefox extensions, including 40 that steal wallet secrets or credentials and 37 deceptive sports-score shells.
omitly-mcp
Advanced tools
Model Context Protocol server exposing Omitly's local, verifiable PDF redaction to AI agents. Redaction runs on-device — documents are never uploaded.
A Model Context Protocol server that exposes Omitly's local, verifiable PDF redaction to AI agents (Claude Code, Claude Desktop, and any other MCP client).
The point of difference: an agent can redact a document without uploading it anywhere. Redaction runs on-device through the Omitly engine and returns a signed audit log proving the data was removed — the opposite of pasting a confidential file into a chat model.
Five of the eleven tools (find_sensitive_regions, locate_text,
check_redaction, verify_redaction, extract_pdf_text) work out of the
box — npm install, no Rust toolchain, no native binary, no desktop app.
They run on a wasm-bindgen build of the same detector that powers the web
leak-checker at omitly.app, bundled directly in this package. create_pdf,
the two write tools (redact_pdf, redact_by_entity), and the two
seal-verification tools (verify_seal, verify_document) still need a
configured native engine — see "Build & run" below. Neither seal tool has a
wasm fallback yet: there is no wasm seal-verification path (tracked in
issue #113), so both always require the native engine, even though checking
a seal needs no licence.
| Tool | What it does |
|---|---|
find_sensitive_regions | Scans a PDF on-device and returns PII candidates — email/SSN/phone/card plus Australian identifiers (TFN, ABN, ACN, Medicare, Centrelink CRN, IHI, BSB; check-digit validated where a published algorithm exists) — with page + exact coordinates, so the agent selects by entity and never guesses geometry. Best-effort pattern matching, not a compliance assessment. Optional regions (generic/us/au) narrows the listed kinds. |
locate_text | Resolves literal strings the model supplies (names, addresses — anything regex can't catch) to their page + coordinates. The model does the recognition; the engine does the geometry. |
check_redaction | Audits an ALREADY-redacted PDF and reports whether sensitive text still survives underneath the redaction marks, in prior incremental-update revisions, metadata, AcroForm fields, or attachments — the "did my black boxes actually remove the data?" check, with a coverage report scoping what was inspected. Free tier (wasm) is EVALUATION-marked and capped to a monthly number of free checks; a configured licensed engine is not capped. |
extract_pdf_text | Extracts a PDF's full text, page by page, PII-MASKED BY DEFAULT so raw sensitive values never flood the model's context window. Each page's spans report the CHAR offset (not byte offset) and kind of every masked value, so an agent can still reason about position without seeing the raw value. masked: false is a documented, explicit opt-in to raw text. Free, no licence, works out of the box on the bundled wasm engine — a native engine is preferred when available (also enables the regions filter; wasm ignores it and scans every pattern). Never renders pages to images. |
redact_by_entity | One-shot: find + filter by kind (email/ssn/phone/card/tfn/abn/acn/medicare/crn/ihi/bsb) and/or regions + redact + verify. The "just scrub the obvious PII" shortcut. |
redact_pdf | Removes the underlying data from given regions of a PDF, verifies nothing survives, writes the redacted file, and returns the audit log. |
verify_redaction | Re-scans an already-redacted PDF and returns the verification verdict — the redaction-completeness check. |
verify_seal | Cryptographically checks a PDF's embedded Omitly audit report and trailing Ed25519 tamper-evidence seal — the tamper-evidence check, distinct from verify_redaction. Integrity, not identity: the signing key is per-install and rides inside the file, so a valid seal means "unchanged since sealed by the holder of this key", never "produced by Omitly" — compare sealFingerprint out-of-band for origin. Requires a native engine; no wasm fallback exists. |
verify_document | Recipient trust-verification (omitly#113): the same seal/report check as verify_seal — not a survivor re-scan — aimed at someone who received a PDF from someone else and wants to confirm it's authentic and unaltered, without paying or licensing anything. Free, no licence. Currently requires a native engine like verify_seal (no wasm seal-verification path yet). |
create_pdf | Generates a clean PDF from Markdown/HTML on-device, rendered through a real browser engine so it looks printed — instead of writing a throwaway reportlab/LaTeX script. |
check_license | Reports the current licence or trial state — tier, trial days left, the vendor-signed licensee name, which resolution step supplied the licence, and whether it is bound to this machine. Free, takes no arguments, reads no document, and is re-resolved on every call so buy → save licence → call again works without a restart. Never returns the device fingerprint or the licence file's contents — device binding is a yes/no. Requires a native engine: the wasm free tier has no licence concept. |
create_pdf)create_pdf is served by a separate binary, omitly-pdf (in
crates/omitly-pdf), kept apart from the redaction engine because generation is
a different trust model from verifiable redaction. It renders Markdown (or raw
HTML) through a headless Chromium-family browser (Chrome/Chromium/Edge/Brave;
override with OMITLY_BROWSER_BIN) — the same engine family the Omitly app's
webview uses, so output looks printed rather than script-generated. Build it and
point OMITLY_PDF_BIN at the binary:
cargo build -p omitly-pdf --release # → target/release/omitly-pdf
// stdin
{ "command": "create", "outputPath": "/abs/out.pdf",
"source": "# Hello\n\nBody **markdown**", "format": "markdown", "title": "Hello" }
// stdout
{ "ok": true, "output": "/abs/out.pdf" }
Typical agent flows:
redact_by_entity (find + redact + verify in one call).find_sensitive_regions / locate_text → review → redact_pdf → verify_redaction.
Coordinates from find/locate drop straight into redact as its regions argument.See DEMO.md for a full Claude Code walkthrough.
The MCP surface (eleven tools, schemas, transport), the native engine binary
(crates/omitly-cli, built as omitly-redact), and the bundled wasm engine
(crates/leakcheck-wasm, covering the four free tools without a native
binary) are all implemented and pass end-to-end tests. find_sensitive_regions
is a first-pass detector (ASCII patterns, per-show-operator matching): treat
its hits as candidates for review, not a completeness guarantee. An LLM can
always supply additional regions directly.
Privacy of findings. Detection results are returned with a masked
preview (e.g. •••-••-6789), never the raw value. The file isn't uploaded and
the secret detected inside it isn't sent back through the model — redaction is
driven entirely by page + coordinates, so the plaintext stays on the machine.
crates/omitly-cli)The server spawns OMITLY_REDACT_BIN, writes a JSON request to stdin, and reads
a JSON response from stdout. Any failure returns { "ok": false, "error": "..." }
(the process still exits 0, so the caller reads ok rather than the exit code).
// stdin
{ "command": "find", "pdfPath": "..." }
// stdout
{ "ok": true, "count": 2, "regions": [
{ "page": 0, "x": 250.4, "y": 610.4, "width": 79.2, "height": 14.4, "kind": "ssn", "preview": "•••-••-6789" } ] }
// `preview` is masked — the raw value never leaves the process; redaction is driven by coordinates.
// stdin — "masked" omitted ⇒ true (the default); pass "masked": false for the
// documented raw-text opt-in. "regions" narrows detected kinds (generic
// kinds like email/card always apply).
{ "command": "extract_text", "pdfPath": "..." }
// stdout — "spans" offsets are CHAR (not byte) offsets into "text", valid
// against either the masked or the raw text of the same page (masking never
// changes a page's character count). A page that could not be decoded
// reports "contentDecoded": false with empty text/spans rather than being
// silently skipped.
{ "ok": true, "masked": true, "pages": [
{ "page": 0, "contentDecoded": true,
"text": "Sensitive sample line: SSN •••-••-6789",
"spans": [ { "kind": "ssn", "start": 24, "end": 35 } ] } ] }
// stdin
{ "command": "redact", "pdfPath": "...", "outputPath": "...",
"regions": [{ "page": 0, "x": 72, "y": 700, "width": 200, "height": 14, "reason": "PII.SSN" }] }
// stdout — also writes "<outputPath>.audit.json" beside the file
{ "ok": true, "output": "...", "audit": { "verdict": "pass", "regions": [ ... ], "warnings": [], "metadataScrubbed": true } }
// stdin — recovers the redacted regions from "<pdfPath>.audit.json"
{ "command": "verify", "pdfPath": "..." }
// stdout — hiddenContent re-checks thumbnails / document actions / embedded
// files on the delivered bytes (omitly#171); any fail flips the verdict
{ "ok": true, "verdict": "pass", "regions": [ ... ], "metadataScrubbed": true,
"hiddenContent": [ { "class": "thumbnails", "verification": { "result": "pass" } }, ... ] }
// stdin — checks the embedded audit report + trailing Ed25519 seal, not
// redaction completeness (that's "verify" above)
{ "command": "verify_seal", "pdfPath": "..." }
// stdout — verdict is one of: no_report | seal_invalid |
// seal_unsupported_version | incomplete | verified. seal_unsupported_version
// means this verifier is too old to check the seal at all — sealValid is
// `null` (checked nothing), never true or false; carriesAuditReport flags
// whether the file also carries an Omitly audit report (escalation signal).
{ "ok": true, "verdict": "verified", "sealValid": true, "sealFingerprint": "...",
"allPassed": true, "metadataScrubbed": true, "regionCount": 2, "pageCount": 4,
"warnings": [], "licenseProvenance": null,
"inputSha256": "...", "outputSha256": "...",
"sourceFilename": "...", "outputFilename": "..." }
The MCP tool verify_document (omitly#113) shells out to the exact same
verify_seal engine command above — it is the recipient-facing name/wording
for the same seal/report integrity check, not a separate engine command.
Free tools only (find_sensitive_regions, locate_text, check_redaction, verify_redaction, extract_pdf_text) — no native engine needed:
cd omitly-mcp
npm install # published releases ship the wasm build already bundled
npm run build # local/dev only: also runs wasm-pack (needs Rust + wasm-pack)
node dist/index.js
npm install omitly-mcp from the registry gets a package with wasm/
already built — a published install never needs Rust. Building from
source (this repo) does need wasm-pack (cargo install wasm-pack) and
the wasm32-unknown-unknown target, since npm run build regenerates the
wasm bundle from ../crates/leakcheck-wasm via npm run build:wasm.
Everything, including create_pdf, verify_seal, verify_document, and
the two write tools (redact_pdf, redact_by_entity): neither seal tool
has a wasm fallback — unlike the five free tools above, they always need the
native engine configured, even though checking a seal carries no licence
requirement (see "Licensing" below).
# 1. Build the local engine binaries (from the repo root)
cargo build -p omitly-cli -p omitly-pdf --release # → target/release/{omitly-redact,omitly-pdf}
# 2. Build and start the MCP server
cd omitly-mcp
npm install
npm run build
OMITLY_ENGINE_DIR=/abs/path/to/target/release node dist/index.js
One env var covers both binaries: OMITLY_ENGINE_DIR is the directory holding
omitly-redact and omitly-pdf. Per-binary overrides (OMITLY_REDACT_BIN,
OMITLY_PDF_BIN) win over the directory when set. When OMITLY_ENGINE_DIR
(or OMITLY_REDACT_BIN) isn't set, find_sensitive_regions, locate_text,
check_redaction, and extract_pdf_text transparently use the bundled wasm
engine instead — same detector, no native binary (extract_pdf_text's
optional regions filter is native-only; wasm scans every pattern and notes
that the filter was ignored). verify_redaction does too, but with a
narrower check: without a native engine there's no <path>.audit.json
sidecar to verify specific regions against, so it falls back to a general
re-scan of the whole file (still useful — a non-empty result still means the
file isn't clean — just not the same rigor as the sidecar-based check).
find/redact need qpdf for the redaction pipeline (QPDF_BIN overrides the
PATH lookup). find alone (native or wasm) is read-only and works without it.
Every path in a tool call comes from the model, so the server confines all reads and writes to one allowed directory:
OMITLY_ALLOWED_DIR — set it in the MCP config (recommended). Without
it, the directory the server was started in is used..audit.json sidecar);
the agent is asked to pick a fresh name instead.OMITLY_ENGINE_TIMEOUT_MS (default 120000) — a wedged engine process is
killed at the deadline instead of hanging the agent's tool call.These are guardrails against confused-deputy mistakes, not a sandbox against a
hostile local user — see docs/THREAT-MODEL.md.
redact_pdf/redact_by_entity are the write surface, enforced inside the
engine binary (not in this server, and not bypassable by the bundled wasm
fallback — wasm never touches these two tools): a Pro or Personal licence
(OMITLY_LICENSE_FILE, or the Omitly desktop app's activated licence on the
same machine) runs unmarked; otherwise the shared 14-day trial applies and
the audit output is permanently marked as evaluation output. The redaction
itself is never degraded, and licence checks never touch the network.
find_sensitive_regions, locate_text, check_redaction, verify_redaction,
verify_seal, verify_document, and extract_pdf_text are free — and
verify_redaction/verify_seal/verify_document/extract_pdf_text are free
forever with no cap and no marking (recipient-side verification and
on-device extraction are the point, not a metered funnel). The two free
detection tools (find_sensitive_regions, check_redaction) on the wasm
tier — i.e. with no native engine configured — are metered (omitly#226):
results carry an evaluation: true flag plus an EVALUATION banner, and after
a monthly number of free checks (default 10, OMITLY_FREE_CAP to tune) the
tool returns a structured { blocked: true, reason: "free-cap" } refusal
until the month rolls over. The count lives in ~/.omitly/usage.json
(override the directory with OMITLY_STATE_DIR; written 0600) and is
local-only — nothing ever phones home; deleting the file resets the free
count, which is accepted (the no-network doctrine makes it unavoidable), and
the counter is deliberately never consulted by any paid write path. Calls
served by a configured native engine are not metered here — that user is in
the engine funnel, where the licence rules above apply. verify_seal and
verify_document (both native-only) stay free by the same design in
crates/omitly-cli (no Pro/Personal licence check on that command path
either — both tools shell out to the identical verify_seal engine command).
claude mcp add omitly -- env \
OMITLY_ENGINE_DIR=/path/to/engine-dir \
OMITLY_ALLOWED_DIR=/path/agents/may/touch \
node /abs/path/to/omitly-mcp/dist/index.js
Or in Claude Desktop's claude_desktop_config.json:
{
"mcpServers": {
"omitly": {
"command": "node",
"args": ["/abs/path/to/omitly-mcp/dist/index.js"],
"env": {
"OMITLY_ENGINE_DIR": "/path/to/engine-dir",
"OMITLY_ALLOWED_DIR": "/path/agents/may/touch"
}
}
}
}
mcpb/ packages the four free/diagnosis tools (check_redaction,
find_sensitive_regions, locate_text, verify_redaction) — never the write
tools — as a self-contained MCPB .mcpb
extension: no Node/npm/Rust toolchain on the end user's machine, just
"Install Extension…" in Claude Desktop. This is a deliberately smaller,
separate server (mcpb/server/index.js) from dist/index.js above, so the
bundle can never expose redact_pdf/redact_by_entity/create_pdf even by
accident.
Download: releases.omitly.app/mcp/omitly-leak-check.mcpb
— always the current version (published by publish-npm.yml on every real
omitly-mcp release; a versioned copy + checksum also live at
mcp/latest.json). Drag the
downloaded file into Claude Desktop, or use "Install Extension…".
Or build it yourself from source:
npm run mcpb:pack # build:wasm + copy into mcpb/wasm + npm install + mcpb pack
# → dist-mcpb/omitly-leak-check.mcpb
mcpb:pack needs the same Rust + wasm-pack toolchain as npm run build
(builds the shared wasm detector once, then copies it into mcpb/ — see
mcpb/scripts/copy-wasm.mjs). The packed .mcpb itself needs nothing but
Node, already bundled inside Claude Desktop.
Not signed (mcpb sign needs a code-signing cert we don't have yet — same
gate as desktop app signing). mcpb info on the packed file confirms
WARNING: Not signed. Whether Claude Desktop's "Install Extension…" flow
blocks or just warns on an unsigned .mcpb has NOT been confirmed against the
real Desktop app in this change (no Desktop GUI in this environment) — that
check is still open, tracked in omitly#225.
FAQs
Model Context Protocol server exposing Omitly's local, verifiable PDF redaction to AI agents. Redaction runs on-device — documents are never uploaded.
The npm package omitly-mcp receives a total of 700 weekly downloads. As such, omitly-mcp popularity was classified as not popular.
We found that omitly-mcp demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
/Security News
Socket uncovered 77 linked Firefox extensions, including 40 that steal wallet secrets or credentials and 37 deceptive sports-score shells.

Security News
NIST disclosed an unreleased AI tool called V-etalon and opened a broad inquiry into NVD modernization after years of automation plans produced no public enrichment system.

Security News
In his AI Council 2026 talk, Feross Aboukhadijeh covers recent package compromises, vulnerability discovery, and a more automated security model.