@yawlabs/mcp 0.64.1 → 0.65.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 +100 -0
  2. package/dist/index.js +654 -106
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -373,6 +373,8 @@ Rotate a credential in one place (the dashboard), every machine picks up the new
373
373
  | `YAW_MCP_DISABLE_PERSISTENCE` | No | Set to `1` or `true` to keep learning + pack-history scoped to the current process -- nothing loaded at start, nothing written on shutdown. Intended for ephemeral / shared environments (CI, containers). Default: cross-session persistence enabled at `~/.yaw-mcp/state.json`. |
374
374
  | `YAW_MCP_AUTO_LOAD` | No | Set to `1` or `true` to pre-activate the top recurring pack (from persisted pack-history) on startup -- no LLM round-trip required. Skips silently when history is empty or no pack's namespaces are all installed. Default: off. Requires persistence to be enabled. |
375
375
  | `YAW_MCP_MIN_COMPLIANCE` | No | Minimum compliance grade (`A`, `B`, `C`, `D`, or `F`, case-insensitive) an installed server must report before `mcp_connect_activate` will load it. Ungraded servers always pass (don't punish unknown). `discover()` annotates below-grade servers in place and shows a "Compliance filter active" header when set. Invalid values log a warning and disable the filter. Default: unset (no filter). |
376
+ | `YAW_MCP_VAULT_PASSPHRASE` | No | Passphrase for the local secret vault (`~/.yaw-mcp/secrets.json`). Required for spawn-time `${secret:NAME}` substitution and to avoid the interactive prompt on `yaw-mcp secrets`. Stripped from every spawned upstream server's env. |
377
+ | `YAW_MCP_VAULT_PASSPHRASE_NEW` | No | The NEW passphrase for `yaw-mcp secrets rotate`. When unset, rotate prompts (confirm-twice) on a TTY instead. |
376
378
  | `MCP_CONNECT_TIMEOUT` | No | Connection timeout in ms for upstream servers (default: `15000`) |
377
379
  | `MCP_CONNECT_IDLE_THRESHOLD` | No | Baseline for idle auto-unload (default: `10`). The per-namespace adaptive cap is `[5, 50]` -- bursty namespaces extend past the baseline, long-idle ones unload at it. |
378
380
 
@@ -388,6 +390,104 @@ The popular Python-based MCP servers (`sqlite`, `time`, `sentry`, and other uvx-
388
390
 
389
391
  `uvx ARGS` is always rewritten to `uv tool run ARGS` at spawn time -- so only `uv` needs to be reachable, not `uvx` separately. Fixes Windows setups where one was on PATH and the other wasn't.
390
392
 
393
+ ## Local secret vault (no account required)
394
+
395
+ There are two ways a secret reaches a server yaw-mcp spawns, and they have
396
+ different threat models:
397
+
398
+ 1. **Backend-credential path (account required).** Paste a token into a
399
+ server's `env` block in the yaw.sh/mcp dashboard. The value is encrypted
400
+ at rest on the backend and injected at spawn time. It syncs across every
401
+ machine signed in to the same account, and revoking the account token cuts
402
+ off access everywhere on the next poll. This is the path the
403
+ "Cross-machine config sync" and "Trust & security" sections describe.
404
+
405
+ 2. **Local secret vault (no account required).** Keep the value in an
406
+ encrypted file on your own machine at `~/.yaw-mcp/secrets.json` and
407
+ reference it from any server's `env` with a `${secret:NAME}` placeholder.
408
+ yaw-mcp substitutes the decrypted value into the child env at spawn time.
409
+ The value never leaves your machine, never goes to the backend, and works
410
+ with no login. This is the right path when you want a credential that is
411
+ strictly local to one machine, or you don't have an account at all.
412
+
413
+ ### How `${secret:NAME}` references work
414
+
415
+ Put a placeholder in a server's `env` value -- it can stand alone or be
416
+ composed inline:
417
+
418
+ ```jsonc
419
+ {
420
+ "namespace": "gh",
421
+ "command": "npx",
422
+ "args": ["-y", "@modelcontextprotocol/server-github"],
423
+ "env": {
424
+ "GITHUB_PERSONAL_ACCESS_TOKEN": "${secret:gh}",
425
+ "AUTH_HEADER": "Bearer ${secret:gh}"
426
+ }
427
+ }
428
+ ```
429
+
430
+ At spawn time, if `YAW_MCP_VAULT_PASSPHRASE` is set in yaw-mcp's own
431
+ environment, yaw-mcp loads the vault, decrypts the referenced names, and
432
+ substitutes the values into the child's env. If the passphrase is absent, the
433
+ vault is missing, or a referenced name isn't stored, the literal
434
+ `${secret:NAME}` is passed through unchanged -- the child then surfaces its
435
+ own "missing/invalid credential" error, which is louder than silently passing
436
+ an empty string.
437
+
438
+ ### Managing the vault -- `yaw-mcp secrets`
439
+
440
+ ```bash
441
+ yaw-mcp secrets set <name> # store a value (stdin prompt, no echo; or --value/--stdin)
442
+ yaw-mcp secrets get <name> # decrypt and print one value
443
+ yaw-mcp secrets list # show entry NAMES (values stay encrypted)
444
+ yaw-mcp secrets remove <name> # delete an entry
445
+ yaw-mcp secrets lock # clear the in-process passphrase cache
446
+ yaw-mcp secrets rotate [--push] # re-encrypt the whole vault under a NEW passphrase
447
+ yaw-mcp secrets audit [--secret NAME] [--server NS] [--json] # who consumed which secret, when
448
+ yaw-mcp secrets push # upload the encrypted blob to your account (login required)
449
+ yaw-mcp secrets pull # download it back (login required)
450
+ ```
451
+
452
+ The passphrase derives the encryption key via scrypt and is cached in memory
453
+ for the lifetime of one yaw-mcp process; the on-disk file only ever holds
454
+ ciphertext (AES-256-GCM, per-entry IV + auth tag, one vault-level salt). Set
455
+ `YAW_MCP_VAULT_PASSPHRASE` in the env to avoid the prompt -- this is required
456
+ for spawn-time substitution, since a spawned MCP server runs non-interactively
457
+ and can't prompt on a TTY.
458
+
459
+ `rotate` re-encrypts every entry under a fresh salt + key derived from a NEW
460
+ passphrase (read from `YAW_MCP_VAULT_PASSPHRASE_NEW` or a confirm-twice
461
+ prompt). It decrypts every entry under the current passphrase FIRST and aborts
462
+ the whole operation -- leaving the on-disk vault untouched -- if any entry
463
+ fails to decrypt. **Rotate re-wraps the ENCRYPTION, not the underlying token
464
+ values:** a token that has leaked is still leaked after a rotate; rotate it at
465
+ its source. Rotate does NOT push to your account unless you pass `--push`.
466
+
467
+ `audit` reads an append-only NDJSON log at `~/.yaw-mcp/secrets-audit.log`
468
+ (mode `0600`, tail-capped) recording that a secret NAME was `injected` into
469
+ (or was `missing` for) a given server namespace, with a timestamp. **The log
470
+ never records a value** -- only names, namespaces, and times. Writes to it are
471
+ fail-open: a broken or unwritable log never blocks a server spawn.
472
+
473
+ The `mcp_connect_secrets` meta-tool gives the same picture to the model
474
+ without any decryption: per server it lists `injectedSecrets` (names the vault
475
+ has and the server references) and `missing` (referenced names the vault
476
+ lacks). It reads only the vault's key list and the servers' reference names --
477
+ it never decrypts or returns a value, and needs no passphrase.
478
+
479
+ ### Offline threat model
480
+
481
+ The vault protects the on-disk file against **offline brute-force after
482
+ exfiltration** -- a stolen laptop, a leaked backup, a synced dotfile repo. The
483
+ file is useless without the passphrase: scrypt key derivation makes guessing
484
+ expensive, and AES-256-GCM means a tampered ciphertext fails to decrypt rather
485
+ than yielding garbage plaintext. What the vault does **not** defend against: a
486
+ process running as you while the passphrase is cached in memory (it can ask
487
+ yaw-mcp to decrypt), a keylogger capturing the passphrase, or a value already
488
+ leaked at its source. For those, rotate the underlying token, not the vault
489
+ encryption.
490
+
391
491
  ## Trust & security
392
492
 
393
493
  MCP servers are third-party code that you choose to run, and yaw-mcp launches them on your machine or calls them over the network. We don't sandbox arbitrary code and we're not an antivirus -- that's your OS and network. What yaw-mcp gives you is **visibility and a gate**: