@safekb/mcp 0.4.0 → 0.6.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.
Files changed (3) hide show
  1. package/README.md +160 -9
  2. package/dist/safekb-mcp.js +20075 -16678
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -21,8 +21,10 @@ npx -y @safekb/mcp --setup
21
21
  ```
22
22
 
23
23
  This opens the same page, then registers the server with Claude Code for all your projects
24
- (`claude mcp add --scope user safekb`). What is already done is left as it is, so it can be run again.
25
- Needs Node 20 or newer.
24
+ (`claude mcp add --scope user safekb -- npx -y @safekb/mcp --mcp`). What is already done is left as it
25
+ is, so it can be run again. A registration that `--setup` of a version before 0.5.0 wrote no longer
26
+ starts the server and is written again with `--mcp` and the arguments it had (see
27
+ [Upgrading to 0.5.0](#upgrading-to-050)). Needs Node 20 or newer.
26
28
 
27
29
  Over SSH, or anywhere without a browser, the command prints a link instead. Open it on any device, sign
28
30
  in and approve; the terminal then shows the account and a code, and you answer `y` when they match what
@@ -31,13 +33,30 @@ your browser shows.
31
33
  ### Other MCP clients (Cursor, …)
32
34
 
33
35
  ```json
34
- { "mcpServers": { "safekb": { "command": "npx", "args": ["-y", "@safekb/mcp"] } } }
36
+ { "mcpServers": { "safekb": { "command": "npx", "args": ["-y", "@safekb/mcp", "--mcp"] } } }
35
37
  ```
36
38
 
37
- Started without a token, the client serves one tool, `connect_safekb`, which opens the page and puts
39
+ `--mcp` is what starts the MCP server. Without any argument the command prints its usage text and exits.
40
+
41
+ Started with `--mcp` but without a token, the client serves one tool, `connect_safekb`, which opens the page and puts
38
42
  the SafeKB tools in its place once you have confirmed. `SAFEKB_MCP_NO_BROWSER=1` makes it show the link
39
43
  instead of opening a browser.
40
44
 
45
+ ### Upgrading to 0.5.0
46
+
47
+ Until 0.4.0 the bare command started the MCP server. From 0.5.0 it prints the usage text, and the
48
+ server is started by `--mcp`. A client configured with `"args": ["-y", "@safekb/mcp"]` therefore gets
49
+ no tools after the upgrade:
50
+
51
+ - Claude Code: run `npx -y @safekb/mcp --setup` again. What an older `--setup` registered (user scope,
52
+ `npx -y @safekb/mcp`, with or without `--config`) is registered again with `--mcp`. A registration
53
+ you made or changed yourself (other arguments, environment entries, local or project scope, a
54
+ pinned version, the `safekb-mcp` program) is not touched: the command stops and prints the two
55
+ `claude mcp` commands that add `--mcp` to it. A registration pinned to an exact version below 0.5.0
56
+ (`@safekb/mcp@0.4.0`) keeps working without `--mcp` and is left as it is.
57
+ - Claude Desktop: install the current `safekb.mcpb`.
58
+ - Other clients: add `"--mcp"` to the `args` in their MCP config.
59
+
41
60
  ### A self-hosted SafeKB
42
61
 
43
62
  The default server is `https://api.safekb.com`. For another one run
@@ -70,6 +89,7 @@ every tool that keeps a key this way; see [Where secrets are kept](#where-secret
70
89
 
71
90
  | Flag | Environment | Meaning |
72
91
  | --- | --- | --- |
92
+ | `--mcp` | | Start the MCP server on stdio. Modified by `--url`, `--keyring`, `--sealed-only` and `--sidecar-socket`. |
73
93
  | `--setup` | | Authorize, choose the key store and register with Claude Code, in one step. Safe to repeat. |
74
94
  | `--url <api url>` | `KB_API_URL` | API base URL. Otherwise the URL in the token file, or `https://api.safekb.com` when authorizing. When serving, it must have the origin the token was issued for: the token is never sent anywhere else. |
75
95
  | `--config <path>` | `SAFEKB_MCP_CONFIG` | Token file. Default `~/.config/safekb/mcp.json`. Use one per project for per-project access. |
@@ -81,10 +101,23 @@ every tool that keeps a key this way; see [Where secrets are kept](#where-secret
81
101
  | `--sidecar-socket <path>` | `SAFEKB_SIDECAR_SOCKET` | The sidecar's socket. Default `sidecar.sock` next to the token file. |
82
102
  | `--export-keys <file>` | | Write every sealed key this machine keeps to a new passphrase-protected file. See [Moving keys to another machine](#moving-keys-to-another-machine). |
83
103
  | `--import-keys <file>` | | Read such a file and keep its keys on this machine. |
104
+ | `--list-keys` | | Print the sealed keys this machine keeps and how each stands with the server. See [Managing keys](#managing-keys). |
105
+ | `--create-key <name>` | | Create a sealed folder as a new seal root with a generated key. Needs `--space <space id>`; takes `--store`. |
106
+ | `--rename-key <ref>` | | Rename a seal root to `--name <new name>`. |
107
+ | `--rotate-key <ref>` | | Replace a seal root's secret with a new generated key; takes `--store`. |
108
+ | `--move-key <ref>` | | Move a seal root's current key to `--store system\|file`. |
109
+ | `--delete-key <ref>` | | Remove a seal root's keys from this machine, after a typed confirmation. |
110
+ | `--export <file>` | | Write what this token reaches to a new passphrase-protected file. Takes `--space`, `--doc`, `--compress`, `--skip-locked`. See [Export and import](#export-and-import). |
111
+ | `--import <file>` | | Create what such a file holds in this token's account, as new things. Takes `--into-space`, `--store`. |
84
112
  | | `SAFEKB_SEALED_IDLE_TTL` | Seconds an unlocked seal root may stay idle. Default 900. `unlock_sealed` can set it per root with `idle_ttl_seconds` (60 to 3600). |
85
113
  | | `SAFEKB_HOSTED_TIMEOUT` | Seconds a hosted tool call may take. Default 300. |
86
114
  | | `SAFEKB_MCP_NO_BROWSER` | Set to anything: never start a browser, only show the authorize link. |
87
115
 
116
+ Exactly one mode is given: `--mcp`, `--setup`, `--authorize`, `--configure`, `--sidecar`, `--export`,
117
+ `--import`, or one of the key modes. A flag that only modifies a mode (`--sealed-only`, `--keyring`, …) without one is an error;
118
+ it never starts the server by itself. A flag the given mode does not take is an error too, as is a
119
+ mode without the value or the modifier it needs, and any flag given an empty value.
120
+
88
121
  With a token file, the client refuses to start when that file or a keyring file is readable by group or others,
89
122
  when `--url` names another origin than the token's, when the hosted endpoint cannot be reached, or
90
123
  when a hosted tool has the name of a sealed tool.
@@ -106,6 +139,14 @@ Its secret is a generated key or a passphrase.
106
139
  `update_sealed_document`, `replace_in_sealed_document`, and the move, archive, delete, export,
107
140
  attachment and deposit tools.
108
141
  - `lock_sealed`, the idle timeout, or the end of the process forget the keys.
142
+ - `change_seal_password` replaces the root's secret. Once the server has taken the new one, the
143
+ superseded key is removed from the store that kept it (the system keyring, or any key file in the
144
+ key file directory). A superseded passphrase, or a key file you had written elsewhere with
145
+ `key_out_path`, is yours to remove.
146
+ - Deleting a seal root (`delete_sealed_document`, or `export_sealed_document` of a standalone sealed
147
+ document) also removes its keys and its name from this machine.
148
+ - A tool parameter that names a seal root (`root_id`, `new_root_id`) takes its id or its
149
+ [name](#names-of-seal-roots). Documents and folders inside a root are named by id.
109
150
 
110
151
  ## Where secrets are kept
111
152
 
@@ -155,14 +196,65 @@ ask it for a held secret, which is what `--confirm` is for.
155
196
 
156
197
  The server cannot read the name of a sealed folder, and an id like `6acdb031893533e0ae1f5456` is no
157
198
  name to type. So the client remembers the name of every seal root it creates or unlocks, in
158
- `roots.json` next to the token file (mode 0600), and follows a rename or a delete. The sidecar and
159
- the key export show a root as `"Tax papers" (6acd…)` and take the name wherever they take an id.
199
+ `roots.json` next to the token file (mode 0600), and follows a rename or a delete. The sidecar, the
200
+ key export and the key modes show a root as `"Tax papers" (6acd…)`, and the name is taken wherever a
201
+ seal root's id is: at the sidecar, in the key modes, and in the `root_id` of the MCP tools.
160
202
 
161
203
  - The names are in plaintext in that file: readable on this machine by programs running as you,
162
204
  never by the server. Delete the file to forget them; they come back as roots are unlocked.
163
- - A name is never put into a tool result or an error, so unlocking a root does not tell the model
164
- what it is called.
165
- - When two roots have the same name, the name stands for neither: use the id.
205
+ - The client never puts a name into a tool result, so unlocking a root does not tell the model what
206
+ it is called. An error repeats only the name the caller passed.
207
+ - A name stays a usable handle: creating or renaming a seal root is refused when the name has the
208
+ shape of an id (24 hex characters), or when another root known on this machine already has it.
209
+ - Text with the shape of an id always means the root with that id. A root that got such a title
210
+ elsewhere is not given a name here when it is unlocked or its key is imported: use its id.
211
+ - Two roots can still share a name when both were named elsewhere and then unlocked or imported
212
+ here. Such a name stands for neither and is refused as ambiguous: use the id. Such a root can
213
+ still be changed while its name stays the same.
214
+
215
+ ### Managing keys
216
+
217
+ ```sh
218
+ npx -y @safekb/mcp --list-keys
219
+ npx -y @safekb/mcp --create-key "Banking" --space <space id> [--store system|file]
220
+ npx -y @safekb/mcp --rename-key "Banking" --name "Bank"
221
+ npx -y @safekb/mcp --rotate-key "Bank" [--store system|file]
222
+ npx -y @safekb/mcp --move-key "Bank" --store file
223
+ npx -y @safekb/mcp --delete-key "Bank"
224
+ ```
225
+
226
+ A seal root is given by its [name](#names-of-seal-roots) or its id.
227
+
228
+ - `--list-keys` prints one line per key in the system keyring and the key file directory: the store,
229
+ the seal root, the key id, and its status. `current`: the root's seal expects this key.
230
+ `superseded`: the root exists but has a newer key. `root not found or not accessible with this
231
+ token`: the server knows no such root for this token (404 or 403), which may be limited to some
232
+ spaces. `its root id is not a seal root on the server`: the id is that of something else.
233
+ A key of another tenant is shown as such and is not asked about. When the server cannot be asked
234
+ (rejected token, server error, no network) the listing fails; a status is never guessed. It never
235
+ prints a key, and needs no terminal. Secrets held by the sidecar are in memory only and are not
236
+ listed.
237
+ - `--create-key` makes a sealed folder at the top of the space as a new seal root and keeps its
238
+ generated key in the default store, or the one named by `--store`.
239
+ - `--rename-key` changes the root's sealed title on the server and the name on this machine. The
240
+ root's key must be in a local store.
241
+ - `--rotate-key` gives the root a newly generated key, in the store that kept the old one or the one
242
+ named by `--store`, and removes the superseded key once the server has taken the new one. Like
243
+ `change_seal_password`, it does not revoke what a holder of the old key already read.
244
+ - `--move-key` puts the root's current key into the other store, reads it back from there, and only
245
+ then removes it from where it was. Nothing is written to the server. A move that was interrupted
246
+ with the key in both stores is finished by running it again, towards either store.
247
+ - `--delete-key` removes every key of the root from this machine's key stores and forgets its name.
248
+ The root on the server is not touched. It shows the keys with their status and asks you to type
249
+ the root's name or id. It warns when a current key is among them (without another copy the root's
250
+ content can never be opened again), and when a key's status could not be verified with this token
251
+ (root not found or not accessible, another tenant, not a seal root): such a key may still be the
252
+ only one that opens existing content. Only keys the server says are superseded come without a
253
+ warning. A name left behind without any key is removed the same way. It needs a terminal, and no
254
+ flag skips the question.
255
+
256
+ When a step after the point of no return fails (the superseded key cannot be removed after a rotation,
257
+ the old copy cannot be removed after a move), the command fails and says where the key now is.
166
258
 
167
259
  ### Moving keys to another machine
168
260
 
@@ -220,6 +312,64 @@ irreversible per credential: the server drops its own encrypted secrets and that
220
312
 
221
313
  Old database and storage backups keep the server-encrypted copies until they age out.
222
314
 
315
+ ## Export and import
316
+
317
+ ```sh
318
+ npx -y @safekb/mcp --export all.safekb --compress # everything this token reaches
319
+ npx -y @safekb/mcp --export work.safekb --space <space id> --doc <document id>
320
+ npx -y @safekb/mcp --import all.safekb # into the account of this token
321
+ npx -y @safekb/mcp --import work.safekb --into-space <space id>
322
+ ```
323
+
324
+ Both need a terminal: the passphrase of the file is typed there (twice for an export) and is never an
325
+ argument. The file is encrypted with AES-256-GCM under a key derived from the passphrase with Argon2id,
326
+ in parts that cannot be dropped, swapped or cut off unnoticed; `--compress` gzips before encrypting.
327
+ Attachments are streamed, so the file may be large. **The file holds every secret in plaintext under
328
+ that one passphrase**: passwords, TOTP seeds and the content of sealed documents.
329
+
330
+ What an export holds:
331
+
332
+ - Spaces, folders, documents (Markdown, rich text and canvas blocks) with their tags, external
333
+ references, workflow status and due date, and their attachments.
334
+ - Databases with columns, indexes, views, rows, row pages and the rows' files.
335
+ - Credentials with every secret field. Each secret of a server-encrypted credential is one audited
336
+ reveal on the server.
337
+ - Sealed folders, documents, attachments and credentials, decrypted on this machine. Every seal root
338
+ in the selection must open with a secret this machine keeps or its sidecar holds
339
+ (`safekb-mcp --sidecar`, then `add <name or id>` for a passphrase root); otherwise the export
340
+ stops before it writes anything. `--skip-locked` exports without those roots and says so.
341
+ - Projects, tasks, decisions, and the links between exported things.
342
+
343
+ What is selected:
344
+
345
+ - No `--space` and no `--doc`: everything the token reaches.
346
+ - `--space <id>` (repeatable): that space with its documents, credentials, projects and tasks.
347
+ - `--doc <id>` (repeatable): that document, or that folder with everything in it and the credentials
348
+ filed in it. Its parent is not exported, so on import it is at the top of its space.
349
+ - A link to something outside the selection is left out and counted.
350
+
351
+ What an import does:
352
+
353
+ - It creates new things with new ids and changes nothing that exists, so it works for a restore, for
354
+ another account or server, and twice in a row. References between imported things (links, relations,
355
+ ids in text, attachments shown in a page, the task of a checklist item) follow the new ids.
356
+ - Every exported space becomes a new space, also where one of that name exists. `--into-space` puts
357
+ everything into one existing space instead. A token limited to some spaces is given the spaces it
358
+ creates (this needs a SafeKB server of October 2026 or newer).
359
+ - Every seal root becomes a new seal root with a newly generated key, kept in the default key store
360
+ or the one named by `--store`; a passphrase root therefore becomes a key root. A seal root whose
361
+ name is taken on this machine gets ` (imported)` appended. **Back the new keys up**
362
+ (`--export-keys`).
363
+ - A project whose slug is taken gets a numbered one. A task keeps its assignee only in the account
364
+ the export came from.
365
+ - When a step fails the import stops, says at which record, and leaves what it had created; nothing
366
+ is rolled back.
367
+
368
+ Not in an export: version history, creation and change times and authors, shares, public links, space
369
+ members, and the account's settings. A vault document (encrypted in the browser to its owner's vault)
370
+ is exported as ciphertext and can only go back into the account it came from, while its id is free
371
+ there. Merged tasks are left out.
372
+
223
373
  ## What sealing does not protect
224
374
 
225
375
  - Whatever a tool returns enters the local conversation transcript and is sent to the model provider.
@@ -227,6 +377,7 @@ Old database and storage backups keep the server-encrypted copies until they age
227
377
  - A program running as your user can read a key file, ask the system keyring or the sidecar for a
228
378
  secret, and read the memory of this client while a root is unlocked.
229
379
  - A session that has unlocked a root can be tricked by content it reads into copying it elsewhere.
380
+ - An export file: it holds sealed content under the passphrase of the file, not under its seal roots.
230
381
  - A compromised local machine, or a malicious server serving stale data.
231
382
  - Metadata: existence, sizes, timestamps, tree position.
232
383