@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.
- package/README.md +102 -34
- package/dist/safekb-mcp.js +15382 -14642
- 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
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
|
|
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
|
-
|
|
20
|
+
npx -y @safekb/mcp --setup
|
|
36
21
|
```
|
|
37
22
|
|
|
38
|
-
|
|
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
|
-
| `--
|
|
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
|
|
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
|
|
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
|
-
|
|
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` | |
|
|
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
|
|
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.
|
|
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 <
|
|
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 <
|
|
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
|