Metadata-Version: 2.4
Name: jidoseal-mcp
Version: 0.1.0
Summary: JidoSeal MCP server — run the local Self-Check and reach certification from inside Claude Code, Cursor, or any MCP host. 100% local, zero content egress.
Author-email: JidoSeal <support@jidoseal.com>
License: Proprietary
Keywords: mcp,model-context-protocol,okf,knowledge-base,governance,markdown,local-first
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Documentation
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: jidoseal>=0.1.3

# JidoSeal MCP server

Lets a host tool — Claude Code, Cursor, an OpenKnowledge workflow, any MCP client — run
JidoSeal's free Self-Check and reach the paid certification **from inside its own environment**,
without the customer leaving the host and without their files leaving the machine.

It exposes the website's model, unchanged:

| Tool | What it does | Network |
|------|--------------|---------|
| `jidoseal_scan` | The free Self-Check over a folder on this machine: the corpus's tier (Bronze/Silver/Gold), the missing fields per file for the next tier, and coverage against each tier — the same tier, gap list and coverage `jidoseal.com/app/run` reports for the same folder. | none |
| `jidoseal_certification_offer` | What certification would cost for this corpus and why that price, what the certificate includes, and exactly which facts a purchase would send. | none |
| `jidoseal_start_checkout` | Asks jidoseal.com to create a real Stripe Checkout session and returns its URL for the customer to open. Takes no payment. | jidoseal.com only |

On a scan, this is exactly what leaves your machine, in the tool's own words:

> Exactly what leaves your machine: nothing — 100% local, zero content egress.
> Your files, their names, and their contents never leave your device.

The scan writes its own records (`manifest.json`, an appended `progress.ndjson`) under
`<root>/.jidoseal/` and nowhere else.

---

## Arm's length, by construction

This is a **standalone process** that speaks **only** the Model Context Protocol (JSON-RPC 2.0
over stdio). It contains, vendors, links and imports **no** OpenKnowledge source and takes **no**
dependency on OpenKnowledge — or on any host. The only things crossing the boundary are MCP's own
JSON envelopes and JidoSeal's own scan result; no host's internal data structures enter this
process, and none of JidoSeal's leave it. Hosts under GPL-3.0 (OpenKnowledge is) can therefore
call it exactly as any other MCP client would.

It also carries **no MCP SDK**: the protocol is implemented on the Python standard library alone
(`jidoseal_mcp.py`), so nothing it depends on can change that answer later. `mcp_server/tests/
test_mcp_isolation.py` enforces all of this mechanically.

---

## Install

```bash
pip install jidoseal-mcp
```

That is everything. It pulls in `jidoseal` (the scan engine and its CLI) and puts a
`jidoseal-mcp` command on your PATH. Requires Python 3.9+.

No JidoSeal account, no API key, and no network access for the scan — not at install time and
not at run time.

<details>
<summary>Running from a repo checkout instead</summary>

The server also runs straight from a clone, with `engine/` beside `mcp_server/`:

```bash
pip install PyYAML          # the engine's single dependency
python3 <repo>/mcp_server/jidoseal_mcp.py
```

Wherever the instructions below say `"command": "jidoseal-mcp"`, use
`"command": "python3", "args": ["<repo>/mcp_server/jidoseal_mcp.py"]` instead.
</details>

---

## Wiring it into a host

The server is one command — `jidoseal-mcp` — speaking MCP over stdio.

### Claude Code

```bash
claude mcp add jidoseal -- jidoseal-mcp
```

…or commit a project-scoped `.mcp.json` in the repo your team works in:

```json
{
  "mcpServers": {
    "jidoseal": {
      "command": "jidoseal-mcp"
    }
  }
}
```

### Cursor

`~/.cursor/mcp.json` (or `.cursor/mcp.json` inside a project) — same block:

```json
{
  "mcpServers": {
    "jidoseal": {
      "command": "jidoseal-mcp"
    }
  }
}
```

### Any other MCP host

Every MCP host that supports stdio servers takes the same facts — a name and a command.
Register it however that host spells it:

```json
{
  "mcpServers": {
    "jidoseal": {
      "command": "jidoseal-mcp",
      "args": [],
      "env": {}
    }
  }
}
```

If the host runs with a different interpreter than the one you installed into, point it at the
module instead — same server, no PATH lookup:

```json
{
  "mcpServers": {
    "jidoseal": {
      "command": "/path/to/your/python",
      "args": ["-m", "jidoseal_mcp"]
    }
  }
}
```

Optional environment:

| Variable | Default | Purpose |
|----------|---------|---------|
| `JIDOSEAL_SITE_URL` | `https://jidoseal.com` | Where `jidoseal_start_checkout` posts. Only ever read by that tool. |

Verify the wiring by hand, without any host:

```bash
printf '%s\n' \
 '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"x","version":"0"}}}' \
 '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
 | jidoseal-mcp
```

You should see an `initialize` result naming `jidoseal`, then the three tools.

---

## The tools

### `jidoseal_scan`

```json
{ "root": "/path/to/your/docs", "include_machine": false }
```

Returns the `/app/run` result for that folder:

```jsonc
{
  "corpus": { "file_count": 4, "tier": "none",
              "coverage": { "bronze": 75.0, "silver": 50.0, "gold": 25.0 } },
  "files": [
    { "name": "bronze.md", "tier": "bronze", "hash": "sha256:…",
      "present": ["type"],
      "missing": {
        "silver": [ { "field": "title", "fix": "AUTO" },
                    { "field": "owner", "fix": "NEEDS-CLIENT" } ],
        "gold":   [ { "field": "status", "fix": "AUTO", "default": "stable" } ] },
      "frontmatter_ok": true }
  ],
  "certified_eligible": false,
  "score": 50,
  "merkleRoot": "6533130d…",
  "scan": { "manifest_path": "…/.jidoseal/manifest.json", "run_id": "scan-…" }
}
```

* **Tier** — `bronze` = OKF v0.2 as written (a populated `type`); `silver` = Bronze + ISO 9001
  §7.5.2 (title, description, timestamp, owner); `gold` = Silver + ISO 30401 (`status`,
  `review_policy`, `reviewed_at`, `next_review_at`). A corpus's tier is the lowest tier any one
  of its files reaches.
* **Gaps** — per file, which fields are missing for each tier, each marked `AUTO` (JidoSeal can
  propose the value) or `NEEDS-CLIENT` (only the owner can answer it). Close them for free and
  re-run; nothing is charged for scanning.
* **`frontmatter_ok: false`** — that file's frontmatter block exists but does not parse, so its
  gaps cannot be closed by writing fields into it. Fix it by hand.
* `include_machine: true` ignores the corpus's own machine/transient excludes from
  `.jidoseal/config.yaml`. Leave it false — the certified path scans without it.

### `jidoseal_certification_offer`

```json
{ "root": "/path/to/your/docs" }
```

Local computation only. Returns the corpus's tier, the price for it and why that price, the
remaining gap counts, what the certificate includes, and the exhaustive list of what a purchase
would send. It charges nothing and starts nothing.

### `jidoseal_start_checkout`

```json
{ "root": "/path/to/your/docs", "company": "Acme Ltd",
  "submitter_name": "Jane Doe", "submitter_email": "jane@acme.example" }
```

Call it only on the customer's explicit go-ahead: it is the purchase step. It asks
jidoseal.com to create a Stripe Checkout session and returns the link for the customer to open
and pay. **It takes no payment** — JidoSeal never sees a card, Stripe hosts the checkout, and
nothing is charged and no certificate is issued unless the customer completes it there. A corpus
that is not yet at Bronze is refused rather than charged.

The tier, score and Merkle root it submits come from a fresh local scan run here — never from
whatever the caller asserts.

---

## Pricing (the site's own, in full)

| | Price | When |
|---|---|---|
| JidoSeal Self-Check | **Free** | Always. Unlimited, on your machine, no account. |
| Bronze — OKF v0.2, as written | **$149** | if you already meet Bronze — certificate only |
| | **$199** | if you don't yet — corrections + certificate |
| Silver — + ISO 9001 §7.5.2 fields | **$349** | corrections + certificate |
| Gold — + ISO 30401 governance | **$599** | corrections + certificate |

Each tier is a single flat price, however many files need correcting — no file-count tiering.
Certification is point-in-time: the certificate attests your corpus as of its timestamp, and
editing files afterwards does not void it.

Certification buys: guided corrections to your target tier, a human approval step (from the
score and hash alone, never your files), a signed certificate bound to a Merkle root of your
corpus, a verifiable tamper-evident badge, a public verification page, and a listing in the
public JidoSeal registry.

---

## What leaves the machine

**A scan: nothing.** Not the files, not their names, not their contents. That is a property of
the import graph, not a promise about which branches run — `jidoseal_mcp.py`, `scan_result.py` and
`offer.py` import no socket, no urllib, no HTTP client of any kind, and neither does anything
they pull in. `checkout_client.py` is the one module that can reach the network, and it is
imported only inside the checkout handler.

**A purchase**, if the customer chooses one: company, name and email as they typed them, the
tier, a 0-100 score, the corpus's Merkle root, the local scan's id, and which Bronze price
applies. No file contents, no file names, no per-file hashes, no paths. The Merkle root is a
one-way digest — it binds the certificate to that exact corpus, and nothing can be read back out
of it.

Reproduce the proof yourself, against the copy you installed:

```bash
# 1. Read the shipped artifact. The scan path is jidoseal_mcp/scan_result/offer/engine_path/
#    attestation; none of them imports a network module. checkout_client.py is the one that
#    does, and nothing on the scan path imports it.
python3 -c "import jidoseal_mcp, inspect, os; print(os.path.dirname(inspect.getfile(jidoseal_mcp)))"

# 2. No network syscall is made during a scan (the trace ends up empty but for the exit line)
strace -f -o /tmp/trace.txt -e trace=network jidoseal-mcp < your-jsonrpc-input

# 3. The scan works with no network at all
bwrap --unshare-net --dev-bind / / jidoseal-mcp < your-jsonrpc-input
```

---

## Tests

The test suite ships with the source, not with the wheel. From a checkout:

```bash
python3 -m pytest mcp_server/tests/ -q
```

Covers the protocol surface, all three tools, the zero-egress guarantee (static import
inspection + the engine's own runtime `assert_no_egress` guard), and parity: the scan result is
diffed against both existing implementations of the same rubric — `api/main.py`'s audit bridge
(byte for byte) and the real `web/lib/tier1-scoring.ts` that the browser runs at `/app/run`
(tier, coverage, score, Merkle root, and which fields are missing per file). See
`tests/test_mcp_parity.py` for the one annotation difference that comparison excludes, and why.

`tests/test_mcp_packaging.py` builds this wheel and asserts what the artifact itself contains:
exactly the six modules above, no duplicate of any engine module the `jidoseal` package already
installs, exactly one module able to reach the network, and a shipped `attestation.py` that is
byte-identical to the engine's.
