@yawlabs/mcp 0.60.3 → 0.62.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/CHANGELOG.md CHANGED
@@ -2,6 +2,29 @@
2
2
 
3
3
  All notable changes to `@yawlabs/mcp` (formerly `@yawlabs/mcph`) are documented here. This project uses [semantic versioning](https://semver.org) and a script-gated release flow: `./release.sh <version>` runs lint + tests + build, bumps, tags, publishes to npm, and creates the GitHub release.
4
4
 
5
+ ## 0.62.0 -- verifiable-signal routing: graded reward, miss tracking, and an eval foundry
6
+
7
+ Lands #25, #26, and #27. The dispatch router's learning signal moves from a binary "any non-error reply counts as success" to a sound, quality-graded reward, plus the surrounding machinery to manufacture and verify that signal. All of the new behavior is additive and the new knobs are off by default, so existing setups are unchanged.
8
+
9
+ - **Dispatch reward is now graded, not binary.** An empty body or an error-shaped 200 (e.g. `{isError:false, text:"not found"}`) no longer banks full credit toward a server's `mcp_connect_dispatch` boost -- the learning signal is a quality-weighted reward in [0,1], so a server that "replies" but does not actually help stops accruing a nudge. When every reply is a clean success or a hard error this collapses to the old behavior.
10
+ - **Re-dispatch miss signal.** When an intent routes to server A, A replies cleanly but is abandoned, and a token-similar intent then routes to a different server B within ~2 minutes, A is penalized as the wrong route. Designed multi-server flows (curated bundles, detected packs) are excluded so an intentional A-then-B chain is not mistaken for a miss.
11
+ - **Step-level (process) reward in `mcp_connect_exec`.** Each pipeline step is graded on its own; a step that fails on bad `$ref` input it consumed from an upstream step splits the blame with the producer instead of being fully blamed, so a flaky producer does not hide behind a healthy consumer (or vice versa).
12
+ - **Routing effort dial -- `YAW_MCP_ROUTE_EFFORT=off|auto|aggressive`** (also a per-call `routeEffort` arg on `mcp_connect_dispatch`). Controls how much LLM sampling dispatch spends to disambiguate close rankings. `auto` (default) preserves the prior fixed top-2 tiebreak exactly -- no latency change on the default path; `aggressive` samples best-of-3 on milder ambiguity; `off` never samples.
13
+ - **Opt-in LLM reward grader -- `YAW_MCP_REWARD_GRADER=1`** (off by default). On the uncertain reward bands only, it asks the client's own LLM (via MCP sampling) whether a call accomplished the goal and revises the credit in the background. Non-blocking (the tool result never waits on the grade), capability-gated, and never-throwing -- a missing capability / timeout / unparseable reply just leaves the heuristic reward standing.
14
+ - **Opt-in routing-eval harvest + CI gate -- `YAW_MCP_FOUNDRY=1`** (off by default). Writes a privacy-safe `(intent -> chosen server)` corpus to `~/.yaw-mcp/foundry.jsonl`: the intent is reduced to a redacted, secret-stripped, order-shuffled token bag, never the raw text. New `yaw-mcp foundry export` folds the harvest into a checked-in regression corpus (snapshotting the local server catalog), and a BM25-floor gate scores the ranker against real dispatches -- it skips cleanly until a corpus is committed.
15
+
16
+ ## 0.61.0 -- audit-fix wave: exec binding payloads (BREAKING), live-bug fixes, 50+ hardening items
17
+
18
+ - **BREAKING: `mcp_connect_exec` step bindings now hold the step's semantic payload, not the raw MCP wire wrapper.** A single-text-item JSON response binds as the parsed value, a non-JSON text response binds as the string, and anything else binds as the content array. `$ref` paths written against the old wire shape -- e.g. `"stepId.content[0].text"` -- now throw a RefError; migrate to `"stepId"` (whole value) or `"stepId.field"` (a specific field). This matches what the tool description always promised.
19
+ - `mcp_connect_activate` / `mcp_connect_dispatch` set `isError` whenever a real activation failure occurs (partial successes no longer mask failures); concurrent-server-cap refusals are flagged internally and stay informational in both. Deactivating an already-unloaded namespace is now an idempotent success.
20
+ - ~30 further fixes from a full-implementation audit: bundles.json write serialization + ENOENT-only absence handling, `list` surfaces parse warnings, analytics 401 no longer clears the team session, `secrets pull` refuses cross-passphrase overwrites without `--force`, uv probe/retry fixes on Windows, stable array positions in pruned tool output, `--help` exits 0 on stdout across subcommands, ASCII-safe terminal output, and assorted copy corrections.
21
+
22
+ ## 0.60.3 -- npm-prefix refinement + pnpm/bun self-upgrade
23
+
24
+ - `refineInstallMethod` now probes `npm prefix -g` (3s timeout) and normalises the result through `realpathSync` so junctioned prefixes (scoop's `current` symlink, Volta shims) resolve to the real path before comparison. When the running entrypoint lives under the npm global prefix, ambiguous `local-node-modules` / `unknown` detections are promoted to `global-npm` -- fixes exotic prefix setups the path-marker list doesn't know.
25
+ - The probe is wired into both `yaw-mcp upgrade` and `yaw-mcp doctor` via the shared `refineInstallMethod` call in `runUpgrade` / `runDoctor`.
26
+ - `maybeAutoUpgrade` (the fire-and-forget startup check) now acts on stale pnpm and bun global installs with `pnpm add -g` / `bun add -g @yawlabs/mcp@latest` in addition to the existing `npm install -g` path. The background spawn log messages interpolate the actual tool name instead of hardcoding `npm`.
27
+
5
28
  ## 0.60.2 -- pnpm/bun global stores upgrade with their owning tool
6
29
 
7
30
  - `yaw-mcp upgrade` now detects pnpm global stores (`<pnpm-home>/global/<n>/node_modules/...`) and bun global installs (`~/.bun/install/global/...`) as their own install methods. `--run` spawns `pnpm add -g` / `bun add -g @yawlabs/mcp@latest` instead of misclassifying them as local node_modules trees -- which would have npm-installed a foreign package-lock + node_modules into the tool manager's internal store.
package/README.md CHANGED
@@ -367,7 +367,7 @@ Rotate a credential in one place (the dashboard), every machine picks up the new
367
367
  | `LOG_LEVEL` | No | Log verbosity: `debug`, `info`, `warn`, `error` (default: `info`) |
368
368
  | `YAW_MCP_POLL_INTERVAL` | No | Config-poll interval in seconds. `0` disables polling (config fetched once at startup). Default: `60` |
369
369
  | `YAW_MCP_AUTO_ACTIVATE` | No | When `discover` is called with a context string and one server clearly wins, auto-load it. Set to `0` to disable. Default: enabled |
370
- | `YAW_MCP_AUTO_UPGRADE` | No | When yaw-mcp starts as a server, runs a non-blocking background check for a newer global-npm install and upgrades quietly. Set to `0` to disable. Default: enabled. |
370
+ | `YAW_MCP_AUTO_UPGRADE` | No | When yaw-mcp starts as a server, runs a non-blocking background check for a newer npm/pnpm/bun global install and upgrades quietly with the owning tool. Set to `0` to disable. Default: enabled. |
371
371
  | `YAW_MCP_SERVER_CAP` | No | Hard cap on concurrently activated servers. Default: `6`. Set to `0` to disable. |
372
372
  | `YAW_MCP_PRUNE_RESPONSES` | No | Conservative response pruning (redact large file blobs etc. before returning to the client). Set to `0` or `false` to disable. Default: enabled. |
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`. |
@@ -410,7 +410,7 @@ If you find a security issue in yaw-mcp itself, report it via [GitHub's private
410
410
  ## Requirements
411
411
 
412
412
  - Node.js 18+
413
- - A [yaw.sh/mcp](https://yaw.sh/mcp) account
413
+ - No account required for core features. A Yaw Team license key is needed only for sync and shared bundles (yaw.sh/mcp).
414
414
 
415
415
  ## Links
416
416
 
@@ -27,7 +27,7 @@ var LOG_LEVELS = { debug: 0, info: 1, warn: 2, error: 3 };
27
27
  var minLevel = LOG_LEVELS[process.env.LOG_LEVEL?.toLowerCase()] ?? LOG_LEVELS.info;
28
28
  function log(level, msg, data) {
29
29
  if (LOG_LEVELS[level] < minLevel) return;
30
- const entry = JSON.stringify({ level, msg, ts: (/* @__PURE__ */ new Date()).toISOString(), ...data });
30
+ const entry = JSON.stringify({ ...data, level, msg, ts: (/* @__PURE__ */ new Date()).toISOString() });
31
31
  process.stderr.write(`${entry}
32
32
  `);
33
33
  }
@@ -115,10 +115,10 @@ function invalidateState() {
115
115
  cachedState = null;
116
116
  }
117
117
  async function loadStoredState(filePath) {
118
- if (cachedState) {
118
+ if (cachedState && cachedState.filePath === filePath) {
119
119
  const s = cachedState.state;
120
120
  if (s && expMs(s.session) < Date.now()) {
121
- cachedState = { state: null };
121
+ cachedState = { filePath, state: null };
122
122
  return null;
123
123
  }
124
124
  return s;
@@ -135,14 +135,14 @@ async function loadStoredState(filePath) {
135
135
  parsed = null;
136
136
  }
137
137
  if (parsed && expMs(parsed.session) < Date.now()) {
138
- cachedState = { state: null };
138
+ cachedState = { filePath, state: null };
139
139
  return null;
140
140
  }
141
- cachedState = { state: parsed };
141
+ cachedState = { filePath, state: parsed };
142
142
  return parsed;
143
143
  }
144
144
  async function saveStoredState(filePath, state) {
145
- cachedState = { state };
145
+ cachedState = { filePath, state };
146
146
  try {
147
147
  await atomicWriteFile(filePath, JSON.stringify(state, null, 2));
148
148
  if (process.platform !== "win32") {
@@ -328,7 +328,6 @@ async function postAnalyticsEvent(event, opts = {}) {
328
328
  baseUrl: opts.baseUrl
329
329
  });
330
330
  if (res.status === 401) {
331
- await clearStoredState(filePath);
332
331
  return { ok: false };
333
332
  }
334
333
  return { ok: res.status === 200 };
@@ -353,18 +352,23 @@ async function listAnalyticsEvents(opts = {}) {
353
352
  }
354
353
  return { events: res.body.events ?? [], cap: res.body.cap ?? 0, order_id: res.body.order_id ?? "" };
355
354
  }
355
+ function getCachedCookie(home = homedir2()) {
356
+ void home;
357
+ const s = cachedState?.state ?? null;
358
+ return s ? s.cookie : null;
359
+ }
356
360
  function _resetForTests() {
357
361
  invalidateState();
358
362
  }
359
363
 
360
364
  export {
365
+ atomicWriteFile,
361
366
  log,
362
367
  cacheDir,
363
368
  CONFIG_DIRNAME,
364
369
  userConfigDir,
365
370
  findProjectConfigDir,
366
371
  guidePath,
367
- atomicWriteFile,
368
372
  SESSION_STATE_FILENAME,
369
373
  TeamSyncAuthError,
370
374
  TeamSyncForbiddenError,
@@ -377,5 +381,6 @@ export {
377
381
  putResource,
378
382
  postAnalyticsEvent,
379
383
  listAnalyticsEvents,
384
+ getCachedCookie,
380
385
  _resetForTests
381
386
  };