@safekb/mcp 0.14.0 → 0.14.2

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 (2) hide show
  1. package/dist/safekb-mcp.js +21 -11
  2. package/package.json +1 -1
@@ -2581,7 +2581,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
2581
2581
  // package.json
2582
2582
  var package_default = {
2583
2583
  name: "@safekb/mcp",
2584
- version: "0.14.0",
2584
+ version: "0.14.2",
2585
2585
  description: "SafeKB local MCP client: proxies the hosted SafeKB tools and adds zero-knowledge sealed documents.",
2586
2586
  license: "SEE LICENSE IN LICENSE",
2587
2587
  author: "Mustafa Öztürk",
@@ -34531,12 +34531,16 @@ function defaultSidecarSocket(configPath) {
34531
34531
  // src/sidecar/client.ts
34532
34532
  var SIDECAR_PROMPT_TIMEOUT_MS = 5 * 60 * 1000;
34533
34533
  var SIDECAR_QUICK_TIMEOUT_MS = 3000;
34534
- var PROMPTING_OPS = new Set([
34535
- "lookup",
34536
- "new",
34537
- "passphrase",
34538
- "confirm"
34539
- ]);
34534
+ var WAITS_FOR_USER = {
34535
+ holds: false,
34536
+ lookup: true,
34537
+ new: true,
34538
+ discard: false,
34539
+ settle: false,
34540
+ passphrase: true,
34541
+ secret: true,
34542
+ confirm: true
34543
+ };
34540
34544
  var NOT_RUNNING_CODES = new Set(["ENOENT", "ECONNREFUSED"]);
34541
34545
 
34542
34546
  class SidecarNotRunningError extends ClientError {
@@ -34576,7 +34580,7 @@ class SidecarClient {
34576
34580
  this.quickTimeoutMs = quickTimeoutMs;
34577
34581
  }
34578
34582
  async request(request) {
34579
- const text2 = await send(this.socketPath, request, PROMPTING_OPS.has(request.op) ? this.promptTimeoutMs : this.quickTimeoutMs);
34583
+ const text2 = await send(this.socketPath, request, WAITS_FOR_USER[request.op] ? this.promptTimeoutMs : this.quickTimeoutMs);
34580
34584
  let response;
34581
34585
  try {
34582
34586
  response = JSON.parse(text2);
@@ -36165,6 +36169,7 @@ var credentialId = objectId2("Id of the sealed credential.");
36165
36169
  var expectedVersion = exports_external.number().int().min(1).describe("The version you last read for this credential. The write fails if it changed since.");
36166
36170
  var CLEARABLE = "An empty string clears the field.";
36167
36171
  var SECRET_ARGUMENT_NOTE = "Secret values passed here are part of the conversation transcript and reach the model provider; the server never sees them.";
36172
+ var TYPED_NOTE = "For a secret you must not see (one the user has not given you, e.g. a card number, PIN or bank password), pass typed_at_sidecar with the field names instead of the value: the user types it at the sidecar while the call waits. Then use it with secret_exec, never by revealing it.";
36168
36173
  var visibleFields = {
36169
36174
  username: exports_external.string().describe(`Visible to the server. ${CLEARABLE}`).optional(),
36170
36175
  url: exports_external.string().describe(`Visible to the server. ${CLEARABLE}`).optional(),
@@ -36185,7 +36190,7 @@ var sealedFields = {
36185
36190
  function credentialTools(credentials, migration, names, entry) {
36186
36191
  const atSidecar = (fields2) => fields2 === undefined ? undefined : { fields: fields2, entry };
36187
36192
  return [
36188
- defineTool("reveal_sealed_credential", `Decrypt one sealed field of a sealed credential on this machine. The server only sees that the ciphertext was fetched. Find credentials and their visible fields with the standard credential tools (search_credentials, get_credential). Refused unless its llm_visibility is visible. ${EXPOSURE_NOTE}`, {
36193
+ defineTool("reveal_sealed_credential", `Decrypt one sealed field of a sealed credential on this machine. The server only sees that the ciphertext was fetched. Find credentials and their visible fields with the standard credential tools (search_credentials, get_credential). Refused unless its llm_visibility is visible. To use a secret (log in, call an API, type it on a device), use secret_exec instead: it never shows the value. ${EXPOSURE_NOTE}`, {
36189
36194
  credential_id: credentialId,
36190
36195
  field: exports_external.string().min(1).describe("password, totp, sealed_notes, or custom:{key}.")
36191
36196
  }, (args) => credentials.reveal(args.credential_id, args.field)),
@@ -36195,7 +36200,7 @@ function credentialTools(credentials, migration, names, entry) {
36195
36200
  llm_visibility: exports_external.enum(["use_only", "human_only"]).describe("human_only is the more restrictive."),
36196
36201
  field: exports_external.string().min(1).describe("One sealed field: password, totp, sealed_notes or custom:{key}. Omit for the whole credential; a field without its own value follows the credential.").optional()
36197
36202
  }, (args) => credentials.setLlmVisibility(args.credential_id, args.llm_visibility, args.field)),
36198
- defineTool("insert_sealed_credential", `Create a sealed credential in a seal root. It is kept in the space of that root. Whether an LLM may reveal its secrets follows the default the user chose for the tenant; only the user changes it, in the SafeKB app. password, totp, sealed_notes and the values of sealed custom fields are encrypted on this machine; title, username, url, tags, notes, visible custom fields and the key name of every custom field (sealed ones too) stay readable by the server so search and duplicate detection work. ${SECRET_ARGUMENT_NOTE}`, {
36203
+ defineTool("insert_sealed_credential", `Create a sealed credential in a seal root. ${TYPED_NOTE} It is kept in the space of that root. Whether an LLM may reveal its secrets follows the default the user chose for the tenant (it may be visible): tighten a field the user wants hidden with set_sealed_credential_llm_visibility (use_only); only the user loosens it, in the SafeKB app. password, totp, sealed_notes and the values of sealed custom fields are encrypted on this machine; title, username, url, tags, notes, visible custom fields and the key name of every custom field (sealed ones too) stay readable by the server so search and duplicate detection work. ${SECRET_ARGUMENT_NOTE}`, {
36199
36204
  root_id: rootRef("The seal root that will own it."),
36200
36205
  title: exports_external.string().min(1).describe("Visible to the server."),
36201
36206
  folder_id: exports_external.string().min(1).describe("Group it belongs to.").optional(),
@@ -36206,7 +36211,7 @@ function credentialTools(credentials, migration, names, entry) {
36206
36211
  root_id: await names.resolve(args.root_id),
36207
36212
  typed_at_sidecar: atSidecar(args.typed_at_sidecar)
36208
36213
  })),
36209
- defineTool("update_sealed_credential", `Change a sealed credential with its seal root's signature. Pass only the fields to change; the save also attests the visible fields as they are now. If visible fields were changed without the key since the last sealed save, the call fails and names them until accept_visible_changes is true. ${SECRET_ARGUMENT_NOTE}`, {
36214
+ defineTool("update_sealed_credential", `Change a sealed credential with its seal root's signature. Pass only the fields to change; the save also attests the visible fields as they are now. If visible fields were changed without the key since the last sealed save, the call fails and names them until accept_visible_changes is true. ${TYPED_NOTE} ${SECRET_ARGUMENT_NOTE}`, {
36210
36215
  credential_id: credentialId,
36211
36216
  expected_version: expectedVersion,
36212
36217
  accept_visible_changes: exports_external.boolean().describe("Attest visible fields that were changed without the key, after reviewing them.").optional(),
@@ -36417,6 +36422,11 @@ The keys of this machine, and backups, are handled by tools that never take or r
36417
36422
 
36418
36423
  ## Sealed credentials
36419
36424
 
36425
+ Secrets in short:
36426
+ 1. Store a secret you must not see: insert_sealed_credential with only the visible fields and typed_at_sidecar: ["password"] (or custom:{key}, totp, sealed_notes). The user types it at the sidecar (safekb-mcp --sidecar, started from a folder outside the knowledge-base repo) while the call waits. Never ask for it in the conversation.
36427
+ 2. Keep it hidden: set_sealed_credential_llm_visibility(field, "use_only"); the account default may let LLMs reveal it.
36428
+ 3. Use it: secret_exec with a handle {{secret://<credential_id>/<field>}} in env, stdin or files; your own program does the rest (HTTP, browser, adb, ssh, ...).
36429
+
36420
36430
  A credential with \`sealed: true\` belongs to a seal root. Its title, username, url, tags, notes and visible custom fields are readable by the server: find it with the standard credential tools. Its password, TOTP seed, sealed_notes and sealed custom fields open only here: reveal_sealed_credential, get_sealed_totp, insert_sealed_credential, update_sealed_credential.
36421
36431
 
36422
36432
  - A secret the user has not given you yet (a card number, a PIN, a new password): never ask for it in the conversation. Call insert_sealed_credential or update_sealed_credential with typed_at_sidecar naming the fields; the user types them at the sidecar (safekb-mcp --sidecar) while the call waits. Or they enter it in the SafeKB web app.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@safekb/mcp",
3
- "version": "0.14.0",
3
+ "version": "0.14.2",
4
4
  "description": "SafeKB local MCP client: proxies the hosted SafeKB tools and adds zero-knowledge sealed documents.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "author": "Mustafa Öztürk",