@safekb/mcp 0.2.0 → 0.4.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 +102 -34
  2. package/dist/safekb-mcp.js +15382 -14642
  3. package/package.json +3 -2
package/README.md CHANGED
@@ -3,60 +3,89 @@
3
3
  Local MCP client for SafeKB (stdio). It proxies the hosted SafeKB tools and adds **sealed** documents
4
4
  and folders: content is encrypted on your machine, and the server stores only ciphertext.
5
5
 
6
- Needs Node 20 or newer.
7
-
8
6
  ## Setup
9
7
 
10
- ```sh
11
- npx -y @safekb/mcp --authorize --url <your SafeKB API URL>
12
- ```
13
-
14
- This opens the browser and prints a **pairing code**. Sign in on the page, choose the spaces, type the
15
- pairing code, and the client saves a service-account token to `~/.config/safekb/mcp.json` (mode 0600).
16
- It then prints the account it was authorized as; check that it is yours. There is no default URL.
8
+ ### Claude Desktop
17
9
 
18
- Last, it chooses where this machine keeps the keys of new seal roots (see [Where secrets are kept](#where-secrets-are-kept)).
19
- With a system keyring that is the choice. Without one, as on most servers, it warns and asks before it
20
- uses key files. `npx -y @safekb/mcp --configure` makes the same choice again without re-authorizing.
21
- A client authorized before this choice existed makes it by itself on its next start when the system
22
- keyring works; only key files need `--configure`, because they need your agreement.
10
+ 1. Download [`safekb.mcpb`](https://safekb.com/download/safekb.mcpb) and open it (or Settings →
11
+ Extensions → Advanced settings → Install Extension…), then click **Install**.
12
+ 2. A SafeKB page opens in your browser. Sign in, choose the spaces, check that the page shows the same
13
+ code as Claude, and click **Confirm**.
23
14
 
24
- How the token gets to the client: the page never sees it. The page hands the client a single-use code
25
- (valid for two minutes), and the client redeems that code itself, proving with a secret it never wrote
26
- anywhere that it is the process that started the flow. The URL in the browser launcher's arguments is
27
- readable by other users of the machine, so it holds nothing that completes the flow; the pairing code
28
- is what ties your sign-in to your terminal. Every authorization creates its own token: authorizing
29
- again never changes the token or the spaces of another client. Revoke one under Settings → service
30
- accounts.
15
+ The tools appear in Claude without a restart. Nothing else is needed: no Node, no config file.
31
16
 
32
- Claude Code:
17
+ ### Claude Code
33
18
 
34
19
  ```sh
35
- claude mcp add safekb -- npx -y @safekb/mcp
20
+ npx -y @safekb/mcp --setup
36
21
  ```
37
22
 
38
- Claude Desktop and Cursor (`mcpServers` in their MCP config file):
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.
26
+
27
+ Over SSH, or anywhere without a browser, the command prints a link instead. Open it on any device, sign
28
+ in and approve; the terminal then shows the account and a code, and you answer `y` when they match what
29
+ your browser shows.
30
+
31
+ ### Other MCP clients (Cursor, …)
39
32
 
40
33
  ```json
41
34
  { "mcpServers": { "safekb": { "command": "npx", "args": ["-y", "@safekb/mcp"] } } }
42
35
  ```
43
36
 
37
+ Started without a token, the client serves one tool, `connect_safekb`, which opens the page and puts
38
+ the SafeKB tools in its place once you have confirmed. `SAFEKB_MCP_NO_BROWSER=1` makes it show the link
39
+ instead of opening a browser.
40
+
41
+ ### A self-hosted SafeKB
42
+
43
+ The default server is `https://api.safekb.com`. For another one run
44
+ `npx -y @safekb/mcp --setup --url <your API URL>` first; the Desktop extension then uses the token that
45
+ command saved.
46
+
47
+ ### How the authorization is protected
48
+
49
+ - The page never sees the token. The client keeps a random secret, the link carries only its SHA-256,
50
+ and the client collects the token itself by presenting the secret to the server it was pointed at.
51
+ - Whoever sees the link could approve it for *their* account, to make your client write into it. So an
52
+ approval is taken only when it is tied to you: it carries the pairing code, or you confirmed it in
53
+ your terminal.
54
+ - The pairing code reaches the page in a file only your user can read (mode 0600), which the browser is
55
+ started with. The browser launcher's arguments, which other users of the machine can read, hold only
56
+ that file's path. This is why you check the code instead of typing it. When the page was opened by
57
+ hand in a client without a terminal, you type the code.
58
+ - Every authorization creates its own token: authorizing again never changes the token or the spaces of
59
+ another client. Revoke one under Settings → service accounts. The token is saved to
60
+ `~/.config/safekb/mcp.json` (mode 0600).
61
+
62
+ ### Where keys go
63
+
64
+ The keys of new seal roots go to the system keyring. On a machine without one (most servers) they are
65
+ kept as files, and the client says so in plain words on the page, in the terminal and in the result of
66
+ every tool that keeps a key this way; see [Where secrets are kept](#where-secrets-are-kept).
67
+ `npx -y @safekb/mcp --configure` makes the choice again, for example after a keyring was installed.
68
+
44
69
  ## Options
45
70
 
46
71
  | Flag | Environment | Meaning |
47
72
  | --- | --- | --- |
48
- | `--url <api url>` | `KB_API_URL` | API base URL. Otherwise the URL saved by `--authorize`. When serving, it must have the origin the token was issued for: the token is never sent anywhere else. |
73
+ | `--setup` | | Authorize, choose the key store and register with Claude Code, in one step. Safe to repeat. |
74
+ | `--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. |
49
75
  | `--config <path>` | `SAFEKB_MCP_CONFIG` | Token file. Default `~/.config/safekb/mcp.json`. Use one per project for per-project access. |
50
- | `--keyring <dir>` | `SAFEKB_SEALED_KEYRING` | Directory of sealed key files (`*.json`). Otherwise the one `--configure` chose, if any. |
76
+ | `--keyring <dir>` | `SAFEKB_SEALED_KEYRING` | Directory of sealed key files (`*.json`). Otherwise the one in the store settings, if any. |
51
77
  | `--sealed-only` | | Expose only the sealed tools; the hosted tools are not proxied. |
52
- | `--authorize` | | Authorize in the browser and write the token file, then choose the key store. |
53
- | `--configure` | | Choose where new sealed keys are kept. Writes `stores.json` next to the token file. |
78
+ | `--authorize` | | Authorize again in the browser and replace the token file. |
79
+ | `--configure` | | Choose again where new sealed keys are kept. Writes `stores.json` next to the token file. |
54
80
  | `--sidecar` | | Run the sidecar in this terminal. With `--confirm` it asks before every release of a secret. |
55
81
  | `--sidecar-socket <path>` | `SAFEKB_SIDECAR_SOCKET` | The sidecar's socket. Default `sidecar.sock` next to the token file. |
82
+ | `--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
+ | `--import-keys <file>` | | Read such a file and keep its keys on this machine. |
56
84
  | | `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). |
57
85
  | | `SAFEKB_HOSTED_TIMEOUT` | Seconds a hosted tool call may take. Default 300. |
86
+ | | `SAFEKB_MCP_NO_BROWSER` | Set to anything: never start a browser, only show the authorize link. |
58
87
 
59
- The client refuses to start when the token file or a keyring file is readable by group or others,
88
+ With a token file, the client refuses to start when that file or a keyring file is readable by group or others,
60
89
  when `--url` names another origin than the token's, when the hosted endpoint cannot be reached, or
61
90
  when a hosted tool has the name of a sealed tool.
62
91
 
@@ -89,14 +118,16 @@ have it, it stops and asks for `store`.
89
118
  | `system` | a generated key | the operating system's keyring: Keychain (macOS), Credential Manager (Windows), Secret Service (Linux) | encrypted by the system |
90
119
  | `file` | a generated key | a file (mode 0600) in the keyring directory | not encrypted |
91
120
  | `sidecar` | a passphrase you type | the memory of a separate process, for a time you choose | nowhere |
92
- | `default` | | whichever of `system` and `file` `--configure` chose | |
121
+ | `default` | | `system` where the system keyring works, otherwise `file` | |
93
122
 
94
123
  `key_out_path` still writes a key file to a path of your choice, and `passphrase` still takes one as
95
124
  text (which then is in the conversation transcript).
96
125
 
97
- The system keyring is used only where `--configure` found it working. On Linux that means a running
126
+ The choice is made once, on the first start or by `--setup`, and saved in `stores.json`; `--configure`
127
+ makes it again. The system keyring is used only where it was found working. On Linux that means a running
98
128
  Secret Service (GNOME Keyring, KWallet, KeePassXC); the kernel keyring is never used, because it
99
- forgets its content at logout. Keys are kept there with [`@napi-rs/keyring`](https://github.com/Brooooooklyn/keyring-node).
129
+ forgets its content at logout. Where it does not work, keys are kept as files in `keys/` next to the
130
+ token file: readable only by your user, but not encrypted on disk. The client tells you when it does this. Keys are kept there with [`@napi-rs/keyring`](https://github.com/Brooooooklyn/keyring-node).
100
131
 
101
132
  ### The sidecar
102
133
 
@@ -107,9 +138,11 @@ a terminal of its own:
107
138
  npx -y @safekb/mcp --sidecar
108
139
  ```
109
140
 
110
- - `add <root id> [minutes]` asks for the root's secret without showing it and holds it for that long
141
+ - `add <name or id> [minutes]` asks for the root's secret without showing it and holds it for that long
111
142
  (default 15 minutes). The time is fixed when you type the secret: use does not extend it.
112
- - `list`, `drop <root id>`, `drop all`, `quit`.
143
+ - `list`, `drop <name or id>`, `drop all`, `quit`.
144
+ - A seal root can be named by its name instead of its id (`add Tax papers`), see
145
+ [Names of seal roots](#names-of-seal-roots).
113
146
  - A new seal root with `store: "sidecar"` makes the sidecar ask you for a new passphrase, twice, while
114
147
  the tool call waits.
115
148
  - With `--confirm` it asks for your approval before every release of a secret.
@@ -118,6 +151,41 @@ Several roots can be held at once, each with its own passphrase and time. The si
118
151
  socket only your user can open; it is not available on Windows. Any program running as you can still
119
152
  ask it for a held secret, which is what `--confirm` is for.
120
153
 
154
+ ### Names of seal roots
155
+
156
+ The server cannot read the name of a sealed folder, and an id like `6acdb031893533e0ae1f5456` is no
157
+ 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.
160
+
161
+ - The names are in plaintext in that file: readable on this machine by programs running as you,
162
+ 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.
166
+
167
+ ### Moving keys to another machine
168
+
169
+ A generated key lives only where it was made: in the system keyring or in a key file. To carry all of
170
+ them to another machine, or to back them up:
171
+
172
+ ```sh
173
+ npx -y @safekb/mcp --export-keys keys.safekb # on the machine that has the keys
174
+ npx -y @safekb/mcp --import-keys keys.safekb # on the other machine, after --setup there
175
+ ```
176
+
177
+ - Export asks for a passphrase (typed twice, not shown; 12 or more characters are recommended, a
178
+ shorter one is pointed out and taken) and writes one file
179
+ with every key of the system keyring and of the key file directory, and the names of their roots.
180
+ The content is encrypted with AES-256-GCM under a key derived from the passphrase with Argon2id.
181
+ Without the passphrase the file opens for nobody.
182
+ - Import asks for that passphrase and keeps the keys in the default store of that
183
+ machine. A key the machine already keeps is left as it is, so an import can be repeated.
184
+ - Both need a terminal: the passphrase is never an argument, so it is in no shell history, no process
185
+ list and no conversation.
186
+ - A root that is opened by a passphrase has no key on any machine. There is nothing to export: type
187
+ the passphrase at the other machine's sidecar.
188
+
121
189
  ## Sealed credentials
122
190
 
123
191
  A sealed credential belongs to a seal root. Its password, TOTP seed, `sealed_notes` and the values of