sealnet-mcp 0.2.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,7 @@
1
+ SEAL — Proprietary License
2
+
3
+ Copyright (c) 2026 SEAL. All rights reserved.
4
+
5
+ This software is proprietary and confidential. Unauthorized copying,
6
+ modification, distribution, or use of this software, via any medium,
7
+ is strictly prohibited without express written permission.
package/README.md ADDED
@@ -0,0 +1,106 @@
1
+ # sealnet-mcp
2
+
3
+ SEAL is how agents and people hand each other large files and secrets without the content passing through the chat, the model or the SEAL server.
4
+
5
+ Model Context Protocol (MCP) server for **SEAL**.
6
+
7
+ ## Design principle: the model never holds capability by default
8
+
9
+ When an MCP-connected AI agent (Claude Desktop, Cursor, Cline, etc.) creates a SEAL via this server:
10
+
11
+ - **handoff mode (default)**: the share link (the key is in its fragment, there is no password) is delivered to the human user via **system clipboard** (primary) or **0o600 local file** (fallback when clipboard is unavailable — headless, SSH, WSL without display). The `tool_result` returned to the model contains **only** an opaque handle and a human-readable receipt. The model cannot exfiltrate the link because it never sees it.
12
+ - **forward mode (explicit opt-in)**: model receives `{ handle, share_url, expires_at, max_reads, mode }` in `tool_result`; the link opens only with the recipient's X25519 key. Requires short TTL (≤30m), `max_reads=1`, mandatory `to` (recipient pubkey). Every forward is audit-logged in the encrypted state file.
13
+
14
+ ## Tool surface
15
+
16
+ | Tool | Purpose |
17
+ |------|---------|
18
+ | `seal_share(path, mode='handoff'\|'forward', expire?, max_reads?, tier?)` / `seal_share(payment)` | Create a SEAL from a local file or folder (sent as one ZIP), streamed from disk. Default: handoff; without `expire`, 5m, or 1d for a handoff over 100 MB. Over 1 GB the user pays once by card (smallest tier that fits, or `tier`): the payment page goes through URL-mode elicitation, or comes back as `payment_url` and a second call with `payment` finishes the upload into the same seal |
19
+ | `seal_request(what, kind='any'\|'secret'\|'file', where_url?, wait=true)` / `seal_request(request)` | Ask the user for a file or a secret without it passing through the chat. On the user's computer the `seal` CLI draws the system dialog (`seal request --local --json`); otherwise the user answers at `seal.net/i/<id>`, sealed to this server's key, shown with three emoji of that key. Returns the path of a 0600 file, never the value |
20
+ | `seal_open(url, mode='metadata'\|'file'\|'inline', dir?)` | Read metadata (default — no download, no counter consumption), save to disk (`file`: streamed, verified before it gets its name, 0600, returns paths) or inline parse (whitelisted MIMEs only, ≤100 KB, refuses secrets, HTML and binary) |
21
+ | `seal_list()` | List SEALs created by **this MCP server only** (scope: `state.json`; never reveals user's CLI seals) |
22
+ | `seal_revoke(handle)` | Revoke by opaque handle (model never learns raw `seal_id`) |
23
+
24
+ With SEAL Pro workload credentials (`sealpro agent enroll`, or `SEALPRO_WORKLOAD_ID`, `SEALPRO_WORKLOAD_SEED` and `SEALPRO_BASE_URL`) three tools appear: `seal_pro_secret_use(secret, version?)` saves a secret your organisation granted to this agent to a 0600 file and returns its path, never the value; `seal_pro_secret_request(secret, reason, ttl_seconds?)` asks a person for one, for a while, and waits for the decision; `seal_pro_file_get(seal, file?, dir?)` saves the files of a room whose key a person granted this agent, 0600 and digest-checked, and returns their paths.
25
+
26
+ Large transfers report `notifications/progress` when the host sends a `progressToken`. Where the host supports URL-mode elicitation, `seal_request` shows the page link through it and waits in one call; otherwise it returns the link and a second call with `request` waits.
27
+
28
+ ## State
29
+
30
+ All state lives encrypted at `${XDG_CONFIG_HOME}/seal-mcp/state.json` (mode `0o600`). Envelope: Argon2id KDF → AES-256-GCM cipher. Contains:
31
+
32
+ - `identity` — X25519 long-term keypair for incoming targeted seals
33
+ - `handles[]` — opaque handle → `{ seal_id, owner_token, mode, created_at }`. Each `owner_token` is full owner-capability for that seal; encrypting only `identity` and leaving tokens plain would be a security illusion.
34
+ - `audit[]` — historical tool invocations (timestamps, handles, modes). Side-channel relevant.
35
+
36
+ Passphrase delivery, in priority order:
37
+
38
+ 1. **OS keychain** (default) — `keytar` (optional dependency) reads from libsecret / macOS Keychain / wincred. The first `serve` stores a random passphrase there itself; `sealnet-mcp init` lets you choose one instead.
39
+ 2. **`SEAL_MCP_PASSPHRASE` env var** — explicit opt-in for CI/headless. Warning logged.
40
+ 3. **Cleartext on disk** — explicit refusal even if user tries `--passphrase-file …`.
41
+
42
+ With neither a keychain nor the env var (containers, CI), or with `serve --ephemeral`, the server runs **ephemeral**: identity and handles live in memory and nothing is written to disk. Handles then last until restart; seal expiry does not depend on it.
43
+
44
+ ## Install + configure
45
+
46
+ No setup step: the first start creates the state.
47
+
48
+ Add to `~/.cursor/mcp.json` (or `claude_desktop_config.json`):
49
+
50
+ ```json
51
+ {
52
+ "seal": {
53
+ "command": "npx",
54
+ "args": ["-y", "sealnet-mcp"]
55
+ }
56
+ }
57
+ ```
58
+
59
+ Production defaults are baked in (backend `https://api.seal.net`, share URLs on `https://seal.net`) — no env needed. Self-hosted deployments set **both** URL knobs; they are independent origins and are never derived from each other:
60
+
61
+ ```json
62
+ {
63
+ "seal": {
64
+ "command": "npx",
65
+ "args": ["-y", "sealnet-mcp"],
66
+ "env": {
67
+ "SEAL_MCP_BACKEND_URL": "https://api.example.com",
68
+ "SEAL_MCP_PUBLIC_HOST": "https://example.com"
69
+ }
70
+ }
71
+ }
72
+ ```
73
+
74
+ Environment variables consumed by the CLI:
75
+
76
+ | Variable | Used by | Purpose |
77
+ |---|---|---|
78
+ | `SEAL_MCP_CONFIG_DIR` | every subcommand | Override default state directory (mirrors Rust `seal-cli`'s `SEAL_CONFIG_DIR`) |
79
+ | `SEAL_MCP_PASSPHRASE` | `serve`, `init` | Provide passphrase non-interactively (CI / headless); warning logged on every use |
80
+ | `SEAL_MCP_BACKEND_URL` | `serve`, `doctor` | SEAL backend base URL (default `https://api.seal.net`) |
81
+ | `SEAL_MCP_PUBLIC_HOST` | `serve`, `doctor` | Public origin for minted share URLs (default `https://seal.net`) |
82
+ | `SEAL_MCP_DEBUG=1` | all | Include stack traces in fatal-error output (otherwise plain `error: <msg>`) |
83
+
84
+ Available subcommands:
85
+
86
+ ```
87
+ sealnet-mcp serve # default — start the stdio MCP server (used by MCP hosts); --ephemeral keeps nothing on disk
88
+ sealnet-mcp init # optional: set up with a passphrase you choose
89
+ sealnet-mcp pubkey # print this server's X25519 public key (for senders)
90
+ sealnet-mcp doctor # diagnose keychain / state perms / backend reachability
91
+ sealnet-mcp reset --force # wipe state.json + keychain entry (after passphrase loss)
92
+ ```
93
+
94
+ ## Development
95
+
96
+ ```bash
97
+ pnpm install
98
+ pnpm build
99
+ pnpm test
100
+ ```
101
+
102
+ ## Source of truth
103
+
104
+ - `docs/security/seal_phase1_plan.md` §"Phase 3 — MCP-оболочка"
105
+ - `shared/contracts/seal_mcp.v1.json` (tool input/output schemas)
106
+ - All cryptographic primitives are bundled into `dist/` from internal workspace packages — no algorithm code is duplicated and no external `@`-scoped dependencies are pulled at install time.