@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.
- package/dist/safekb-mcp.js +21 -11
- package/package.json +1 -1
package/dist/safekb-mcp.js
CHANGED
|
@@ -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.
|
|
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
|
|
34535
|
-
|
|
34536
|
-
|
|
34537
|
-
|
|
34538
|
-
|
|
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,
|
|
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
|
|
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