@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.
- package/README.md +100 -0
- package/dist/index.js +654 -106
- 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**:
|