omitly-mcp 0.0.0 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,34 @@
1
+ Omitly MCP server — Proprietary License
2
+ Copyright (c) 2026 Innisfallen Pty Ltd. All rights reserved.
3
+
4
+ This software is provided to let AI agents and MCP clients use Omitly's
5
+ local, on-device PDF tools: free functionality (detection, leak checking,
6
+ verification, document generation) at no charge, and paid functionality
7
+ (verifiable redaction and any other features gated by an Omitly licence)
8
+ subject to a valid, current Omitly licence.
9
+
10
+ Permission is granted to install and use this package to:
11
+ (a) use the free functionality for any lawful purpose; and
12
+ (b) use the paid functionality only while you hold a valid Omitly licence
13
+ issued by Innisfallen and in accordance with the Omitly Terms of
14
+ Service at https://omitly.app.
15
+
16
+ You may NOT, except to the extent permitted by applicable law that cannot be
17
+ excluded by agreement:
18
+ - copy, modify, or create derivative works of this software;
19
+ - reverse engineer, decompile, or disassemble it, or attempt to derive the
20
+ source of the closed components or engine;
21
+ - redistribute, sublicense, sell, rent, or lease it;
22
+ - circumvent, disable, or interfere with the licence gating or any
23
+ usage metering; or
24
+ - remove or alter any proprietary notices.
25
+
26
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
29
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
31
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32
+ SOFTWARE.
33
+
34
+ For commercial licensing terms, contact hello@omitly.app.
package/README.md ADDED
@@ -0,0 +1,205 @@
1
+ # omitly-mcp
2
+
3
+ A [Model Context Protocol](https://modelcontextprotocol.io) server that exposes
4
+ Omitly's **local, verifiable PDF redaction** to AI agents (Claude Code, Claude
5
+ Desktop, and any other MCP client).
6
+
7
+ The point of difference: an agent can redact a document **without uploading it
8
+ anywhere**. Redaction runs on-device through the Omitly engine and returns a
9
+ signed audit log proving the data was removed — the opposite of pasting a
10
+ confidential file into a chat model.
11
+
12
+ **Four of the seven tools (`find_sensitive_regions`, `locate_text`,
13
+ `check_redaction`, `verify_redaction`) work out of the box — `npm install`,
14
+ no Rust toolchain, no native binary, no desktop app.** They run on a
15
+ wasm-bindgen build of the same detector that powers the web leak-checker at
16
+ omitly.app, bundled directly in this package. `create_pdf` and the two
17
+ write tools (`redact_pdf`, `redact_by_entity`) still need a configured
18
+ native engine — see "Build & run" below.
19
+
20
+ ## Tools
21
+
22
+ | Tool | What it does |
23
+ |------|--------------|
24
+ | `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. |
25
+ | `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. |
26
+ | `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. |
27
+ | `redact_pdf` | Removes the underlying data from given regions of a PDF, verifies nothing survives, writes the redacted file, and returns the audit log. |
28
+ | `verify_redaction` | Re-scans an already-redacted PDF and returns the verification verdict. |
29
+ | `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. |
30
+
31
+ ## PDF generation (`create_pdf`)
32
+
33
+ `create_pdf` is served by a **separate** binary, `omitly-pdf` (in
34
+ `crates/omitly-pdf`), kept apart from the redaction engine because generation is
35
+ a different trust model from verifiable redaction. It renders Markdown (or raw
36
+ HTML) through a headless Chromium-family browser (Chrome/Chromium/Edge/Brave;
37
+ override with `OMITLY_BROWSER_BIN`) — the same engine family the Omitly app's
38
+ webview uses, so output looks printed rather than script-generated. Build it and
39
+ point `OMITLY_PDF_BIN` at the binary:
40
+
41
+ ```bash
42
+ cargo build -p omitly-pdf --release # → target/release/omitly-pdf
43
+ ```
44
+
45
+ ```jsonc
46
+ // stdin
47
+ { "command": "create", "outputPath": "/abs/out.pdf",
48
+ "source": "# Hello\n\nBody **markdown**", "format": "markdown", "title": "Hello" }
49
+ // stdout
50
+ { "ok": true, "output": "/abs/out.pdf" }
51
+ ```
52
+
53
+ Typical agent flows:
54
+ - Quick: **`redact_by_entity`** (find + redact + verify in one call).
55
+ - Careful: **`find_sensitive_regions` / `locate_text` → review → `redact_pdf` → `verify_redaction`**.
56
+ Coordinates from `find`/`locate` drop straight into `redact` as its `regions` argument.
57
+
58
+ See [DEMO.md](./DEMO.md) for a full Claude Code walkthrough.
59
+
60
+ ## Status
61
+
62
+ The MCP surface (seven tools, schemas, transport), the native engine binary
63
+ (`crates/omitly-cli`, built as `omitly-redact`), and the bundled wasm engine
64
+ (`crates/leakcheck-wasm`, covering the four free tools without a native
65
+ binary) are all implemented and pass end-to-end tests. `find_sensitive_regions`
66
+ is a first-pass detector (ASCII patterns, per-show-operator matching): treat
67
+ its hits as *candidates for review*, not a completeness guarantee. An LLM can
68
+ always supply additional regions directly.
69
+
70
+ **Privacy of findings.** Detection results are returned with a **masked**
71
+ preview (e.g. `•••-••-6789`), never the raw value. The file isn't uploaded *and*
72
+ the secret detected inside it isn't sent back through the model — redaction is
73
+ driven entirely by page + coordinates, so the plaintext stays on the machine.
74
+
75
+ ### Engine contract (implemented in `crates/omitly-cli`)
76
+
77
+ The server spawns `OMITLY_REDACT_BIN`, writes a JSON request to stdin, and reads
78
+ a JSON response from stdout. Any failure returns `{ "ok": false, "error": "..." }`
79
+ (the process still exits 0, so the caller reads `ok` rather than the exit code).
80
+
81
+ ```jsonc
82
+ // stdin
83
+ { "command": "find", "pdfPath": "..." }
84
+ // stdout
85
+ { "ok": true, "count": 2, "regions": [
86
+ { "page": 0, "x": 250.4, "y": 610.4, "width": 79.2, "height": 14.4, "kind": "ssn", "preview": "•••-••-6789" } ] }
87
+ // `preview` is masked — the raw value never leaves the process; redaction is driven by coordinates.
88
+ ```
89
+
90
+ ```jsonc
91
+ // stdin
92
+ { "command": "redact", "pdfPath": "...", "outputPath": "...",
93
+ "regions": [{ "page": 0, "x": 72, "y": 700, "width": 200, "height": 14, "reason": "PII.SSN" }] }
94
+ // stdout — also writes "<outputPath>.audit.json" beside the file
95
+ { "ok": true, "output": "...", "audit": { "verdict": "pass", "regions": [ ... ], "warnings": [], "metadataScrubbed": true } }
96
+ ```
97
+
98
+ ```jsonc
99
+ // stdin — recovers the redacted regions from "<pdfPath>.audit.json"
100
+ { "command": "verify", "pdfPath": "..." }
101
+ // stdout
102
+ { "ok": true, "verdict": "pass", "regions": [ ... ], "metadataScrubbed": true }
103
+ ```
104
+
105
+ ## Build & run
106
+
107
+ **Free tools only (find_sensitive_regions, locate_text, check_redaction,
108
+ verify_redaction) — no native engine needed:**
109
+
110
+ ```bash
111
+ cd omitly-mcp
112
+ npm install # published releases ship the wasm build already bundled
113
+ npm run build # local/dev only: also runs wasm-pack (needs Rust + wasm-pack)
114
+ node dist/index.js
115
+ ```
116
+
117
+ `npm install omitly-mcp` from the registry gets a package with `wasm/`
118
+ already built — a published install never needs Rust. Building **from
119
+ source** (this repo) does need `wasm-pack` (`cargo install wasm-pack`) and
120
+ the `wasm32-unknown-unknown` target, since `npm run build` regenerates the
121
+ wasm bundle from `../crates/leakcheck-wasm` via `npm run build:wasm`.
122
+
123
+ **Everything, including `create_pdf` and the two write tools
124
+ (`redact_pdf`, `redact_by_entity`):**
125
+
126
+ ```bash
127
+ # 1. Build the local engine binaries (from the repo root)
128
+ cargo build -p omitly-cli -p omitly-pdf --release # → target/release/{omitly-redact,omitly-pdf}
129
+
130
+ # 2. Build and start the MCP server
131
+ cd omitly-mcp
132
+ npm install
133
+ npm run build
134
+ OMITLY_ENGINE_DIR=/abs/path/to/target/release node dist/index.js
135
+ ```
136
+
137
+ One env var covers both binaries: `OMITLY_ENGINE_DIR` is the directory holding
138
+ `omitly-redact` and `omitly-pdf`. Per-binary overrides (`OMITLY_REDACT_BIN`,
139
+ `OMITLY_PDF_BIN`) win over the directory when set. When `OMITLY_ENGINE_DIR`
140
+ (or `OMITLY_REDACT_BIN`) isn't set, `find_sensitive_regions`, `locate_text`,
141
+ and `check_redaction` transparently use the bundled wasm engine instead —
142
+ same detector, no native binary. `verify_redaction` does too, but with a
143
+ narrower check: without a native engine there's no `<path>.audit.json`
144
+ sidecar to verify specific regions against, so it falls back to a general
145
+ re-scan of the whole file (still useful — a non-empty result still means the
146
+ file isn't clean — just not the same rigor as the sidecar-based check).
147
+
148
+ `find`/`redact` need `qpdf` for the redaction pipeline (`QPDF_BIN` overrides the
149
+ PATH lookup). `find` alone (native or wasm) is read-only and works without it.
150
+
151
+ ## Access control
152
+
153
+ Every path in a tool call comes from the model, so the server confines all
154
+ reads and writes to one allowed directory:
155
+
156
+ - **`OMITLY_ALLOWED_DIR`** — set it in the MCP config (recommended). Without
157
+ it, the directory the server was started in is used.
158
+ - Symlinks are resolved before the check, so a link inside the root pointing
159
+ outside it is refused.
160
+ - Outputs **never overwrite an existing file** (or its `.audit.json` sidecar);
161
+ the agent is asked to pick a fresh name instead.
162
+ - **`OMITLY_ENGINE_TIMEOUT_MS`** (default 120000) — a wedged engine process is
163
+ killed at the deadline instead of hanging the agent's tool call.
164
+
165
+ These are guardrails against confused-deputy mistakes, not a sandbox against a
166
+ hostile local user — see `docs/THREAT-MODEL.md`.
167
+
168
+ ## Licensing
169
+
170
+ `redact_pdf`/`redact_by_entity` are the write surface, enforced **inside the
171
+ engine binary** (not in this server, and not bypassable by the bundled wasm
172
+ fallback — wasm never touches these two tools): a Pro or Personal licence
173
+ (`OMITLY_LICENSE_FILE`, or the Omitly desktop app's activated licence on the
174
+ same machine) runs unmarked; otherwise the shared 14-day trial applies and
175
+ the audit output is permanently marked as evaluation output. The redaction
176
+ itself is never degraded, and licence checks never touch the network.
177
+ `find_sensitive_regions`, `locate_text`, `check_redaction`, and
178
+ `verify_redaction` are free forever — the wasm build backing them has no
179
+ licence logic at all, because there's nothing to gate.
180
+
181
+ ## Register with Claude Code
182
+
183
+ ```bash
184
+ claude mcp add omitly -- env \
185
+ OMITLY_ENGINE_DIR=/path/to/engine-dir \
186
+ OMITLY_ALLOWED_DIR=/path/agents/may/touch \
187
+ node /abs/path/to/omitly-mcp/dist/index.js
188
+ ```
189
+
190
+ Or in Claude Desktop's `claude_desktop_config.json`:
191
+
192
+ ```jsonc
193
+ {
194
+ "mcpServers": {
195
+ "omitly": {
196
+ "command": "node",
197
+ "args": ["/abs/path/to/omitly-mcp/dist/index.js"],
198
+ "env": {
199
+ "OMITLY_ENGINE_DIR": "/path/to/engine-dir",
200
+ "OMITLY_ALLOWED_DIR": "/path/agents/may/touch"
201
+ }
202
+ }
203
+ }
204
+ }
205
+ ```