@yawlabs/mcp 0.65.0 → 0.66.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 (2) hide show
  1. package/dist/index.js +220 -23
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -825,6 +825,7 @@ async function readConfigAt(path5, scope, warnings) {
825
825
  }
826
826
  const servers = Array.isArray(obj.servers) ? obj.servers.filter((v) => typeof v === "string") : void 0;
827
827
  const blocked = Array.isArray(obj.blocked) ? obj.blocked.filter((v) => typeof v === "string") : void 0;
828
+ const installNudge = typeof obj.installNudge === "boolean" ? obj.installNudge : void 0;
828
829
  if (token5) {
829
830
  if (scope === "project") {
830
831
  warnings.push(
@@ -833,7 +834,7 @@ async function readConfigAt(path5, scope, warnings) {
833
834
  }
834
835
  await checkPermissions(path5, warnings);
835
836
  }
836
- return { path: path5, scope, version, token: token5, apiBase, servers, blocked };
837
+ return { path: path5, scope, version, token: token5, apiBase, servers, blocked, installNudge };
837
838
  }
838
839
  async function checkPermissions(path5, warnings) {
839
840
  if (process.platform === "win32") return;
@@ -855,6 +856,13 @@ function pickServers(files) {
855
856
  if (project !== void 0) return project;
856
857
  return files.find((f) => f.scope === "global")?.servers;
857
858
  }
859
+ function pickInstallNudge(files) {
860
+ const local = files.find((f) => f.scope === "local")?.installNudge;
861
+ if (local !== void 0) return local;
862
+ const project = files.find((f) => f.scope === "project")?.installNudge;
863
+ if (project !== void 0) return project;
864
+ return files.find((f) => f.scope === "global")?.installNudge;
865
+ }
858
866
  function unionBlocked(files) {
859
867
  const set = /* @__PURE__ */ new Set();
860
868
  let touched = false;
@@ -934,6 +942,7 @@ async function loadYawMcpConfig(opts = {}) {
934
942
  apiBaseSource,
935
943
  servers: pickServers(loadedFiles),
936
944
  blocked: unionBlocked(loadedFiles),
945
+ installNudge: pickInstallNudge(loadedFiles),
937
946
  projectConfigDir,
938
947
  loadedFiles,
939
948
  warnings
@@ -963,10 +972,6 @@ function toProfile(config) {
963
972
  }
964
973
  return result;
965
974
  }
966
- async function loadEffectiveProfile(cwd, home) {
967
- const config = await loadYawMcpConfig({ cwd, home });
968
- return toProfile(config);
969
- }
970
975
  function isAllowed(rules, namespace) {
971
976
  if (!rules) return true;
972
977
  if (rules.blocked?.includes(namespace)) return false;
@@ -2060,6 +2065,18 @@ function cliToNamespaces() {
2060
2065
  reverseIndexCache = map;
2061
2066
  return map;
2062
2067
  }
2068
+ var SHADOW_INSTALL_TARGETS = {
2069
+ aws: { package: "@yawlabs/aws-mcp", namespace: "aws", name: "AWS" },
2070
+ caddy: { package: "@yawlabs/caddy-mcp", namespace: "caddy", name: "Caddy" },
2071
+ curl: { package: "@yawlabs/fetch-mcp", namespace: "fetch", name: "Fetch" },
2072
+ wget: { package: "@yawlabs/fetch-mcp", namespace: "fetch", name: "Fetch" },
2073
+ psql: { package: "@yawlabs/postgres-mcp", namespace: "postgres", name: "Postgres" },
2074
+ pg_dump: { package: "@yawlabs/postgres-mcp", namespace: "postgres", name: "Postgres" },
2075
+ tailscale: { package: "@yawlabs/tailscale-mcp", namespace: "tailscale", name: "Tailscale" }
2076
+ };
2077
+ function installTargetForCli(cli) {
2078
+ return SHADOW_INSTALL_TARGETS[cli];
2079
+ }
2063
2080
  function formatShadowLine(server) {
2064
2081
  const shadows = resolveShadowedClis(server);
2065
2082
  if (shadows.length === 0) return null;
@@ -3987,7 +4004,7 @@ async function runUpgrade(opts = {}) {
3987
4004
  return { exitCode: 3, lines };
3988
4005
  }
3989
4006
  function readCurrentVersion() {
3990
- return true ? "0.65.0" : "dev";
4007
+ return true ? "0.66.0" : "dev";
3991
4008
  }
3992
4009
 
3993
4010
  // src/usage-hints.ts
@@ -4049,7 +4066,7 @@ function selectFlakyNamespaces(entries, limit) {
4049
4066
  }
4050
4067
 
4051
4068
  // src/doctor-cmd.ts
4052
- var VERSION = true ? "0.65.0" : "dev";
4069
+ var VERSION = true ? "0.66.0" : "dev";
4053
4070
  function isPersistenceDisabled(env) {
4054
4071
  const raw = env.YAW_MCP_DISABLE_PERSISTENCE;
4055
4072
  return raw !== void 0 && raw !== "" && (raw === "1" || raw.toLowerCase() === "true");
@@ -6857,7 +6874,7 @@ function defaultSpawn2(cmd, args) {
6857
6874
  async function maybeAutoUpgrade(deps = {}) {
6858
6875
  const optOut = process.env.YAW_MCP_AUTO_UPGRADE;
6859
6876
  if (optOut === "0" || optOut?.toLowerCase() === "false") return;
6860
- const current = deps.currentVersion ?? (true ? "0.65.0" : "dev");
6877
+ const current = deps.currentVersion ?? (true ? "0.66.0" : "dev");
6861
6878
  if (current === "dev") return;
6862
6879
  const method = (deps.isSeaImpl ? await deps.isSeaImpl() : await detectSea()) ? "binary" : detectInstallMethod(deps.argvPath ?? process.argv[1]);
6863
6880
  const latest = await (deps.fetchLatestImpl ?? fetchLatestVersion2)();
@@ -7590,6 +7607,63 @@ function pushToolCall(history, record, limit = HISTORY_LIMIT) {
7590
7607
  return history;
7591
7608
  }
7592
7609
 
7610
+ // src/install-nudge.ts
7611
+ import { existsSync as existsSync7, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
7612
+ import { dirname as dirname4, join as join12 } from "path";
7613
+ var INSTALL_NUDGE_MIN_COUNT = 5;
7614
+ var INSTALL_NUDGE_COOLDOWN_MS = 7 * 24 * 60 * 60 * 1e3;
7615
+ var INSTALL_NUDGE_STATE_FILENAME = "install-nudge-state.json";
7616
+ function installNudgeStatePath(home) {
7617
+ return join12(home, CONFIG_DIRNAME, INSTALL_NUDGE_STATE_FILENAME);
7618
+ }
7619
+ function installNudgeEnabled(env, config) {
7620
+ if (env.YAW_MCP_INSTALL_NUDGE === "1") return true;
7621
+ if (config?.installNudge === true) return true;
7622
+ return false;
7623
+ }
7624
+ function readState(home) {
7625
+ const path5 = installNudgeStatePath(home);
7626
+ if (!existsSync7(path5)) return { nudges: [] };
7627
+ try {
7628
+ const parsed = JSON.parse(readFileSync4(path5, "utf8"));
7629
+ if (!parsed || typeof parsed !== "object") return { nudges: [] };
7630
+ const raw = parsed.nudges;
7631
+ if (!Array.isArray(raw)) return { nudges: [] };
7632
+ const nudges = [];
7633
+ for (const entry of raw) {
7634
+ if (entry && typeof entry === "object" && typeof entry.cli === "string" && typeof entry.nudgedAt === "number") {
7635
+ nudges.push({ cli: entry.cli, nudgedAt: entry.nudgedAt });
7636
+ }
7637
+ }
7638
+ return { nudges };
7639
+ } catch {
7640
+ return { nudges: [] };
7641
+ }
7642
+ }
7643
+ function shouldNudge(cli, home, now = Date.now) {
7644
+ const state = readState(home);
7645
+ const rec = state.nudges.find((n) => n.cli === cli);
7646
+ if (!rec) return true;
7647
+ return now() - rec.nudgedAt >= INSTALL_NUDGE_COOLDOWN_MS;
7648
+ }
7649
+ function recordNudge(cli, home, now = Date.now) {
7650
+ try {
7651
+ const at = now();
7652
+ const state = readState(home);
7653
+ const kept = state.nudges.filter((n) => n.cli !== cli && at - n.nudgedAt < INSTALL_NUDGE_COOLDOWN_MS);
7654
+ kept.push({ cli, nudgedAt: at });
7655
+ const path5 = installNudgeStatePath(home);
7656
+ mkdirSync2(dirname4(path5), { recursive: true });
7657
+ writeFileSync2(path5, `${JSON.stringify({ nudges: kept }, null, 2)}
7658
+ `, "utf8");
7659
+ } catch (err) {
7660
+ log("debug", "install-nudge: failed to record nudge state", {
7661
+ cli,
7662
+ error: err instanceof Error ? err.message : String(err)
7663
+ });
7664
+ }
7665
+ }
7666
+
7593
7667
  // src/learning.ts
7594
7668
  var LEARNING_MIN_OBSERVATIONS = 3;
7595
7669
  var LEARNING_MAX_BOOST = 1.1;
@@ -9353,6 +9427,62 @@ import {
9353
9427
  ToolListChangedNotificationSchema
9354
9428
  } from "@modelcontextprotocol/sdk/types.js";
9355
9429
 
9430
+ // src/oam-spawn.ts
9431
+ import { execFileSync } from "child_process";
9432
+ import { createRequire } from "module";
9433
+ var requireFrom = createRequire(import.meta.url);
9434
+ function packageName(spec) {
9435
+ const start = spec.startsWith("@") ? 1 : 0;
9436
+ const at = spec.indexOf("@", start);
9437
+ return at === -1 ? spec : spec.slice(0, at);
9438
+ }
9439
+ var oamBinCache;
9440
+ function oamBin() {
9441
+ if (oamBinCache !== void 0) return oamBinCache;
9442
+ const bin = process.env.OAM_BIN || (process.platform === "win32" ? "oam.exe" : "oam");
9443
+ try {
9444
+ execFileSync(bin, ["--version"], { stdio: "ignore" });
9445
+ oamBinCache = bin;
9446
+ } catch {
9447
+ oamBinCache = null;
9448
+ }
9449
+ return oamBinCache;
9450
+ }
9451
+ function rewriteForOam(command, args, deps) {
9452
+ const bin = deps.oamBin;
9453
+ if (!bin) return { command, args };
9454
+ const toOam = (entry, rest) => ({
9455
+ command: bin,
9456
+ args: rest.length > 0 ? ["run", entry, "--", ...rest] : ["run", entry]
9457
+ });
9458
+ if (command === "node") {
9459
+ const [entry, ...rest] = args;
9460
+ if (!entry) return { command, args };
9461
+ return toOam(entry, rest);
9462
+ }
9463
+ if (command === "npx") {
9464
+ const positional = args.filter((a) => a !== "-y" && a !== "--yes");
9465
+ const [spec, ...rest] = positional;
9466
+ if (!spec) return { command, args };
9467
+ const entry = deps.resolveEntry(packageName(spec));
9468
+ if (!entry) return { command, args };
9469
+ return toOam(entry, rest);
9470
+ }
9471
+ return { command, args };
9472
+ }
9473
+ function resolveOamSpawn(command, args) {
9474
+ return rewriteForOam(command, args, {
9475
+ oamBin: oamBin(),
9476
+ resolveEntry: (pkg) => {
9477
+ try {
9478
+ return requireFrom.resolve(pkg);
9479
+ } catch {
9480
+ return null;
9481
+ }
9482
+ }
9483
+ });
9484
+ }
9485
+
9356
9486
  // src/uv-bootstrap.ts
9357
9487
  import { spawn as spawn5 } from "child_process";
9358
9488
  import { createHash as createHash3 } from "crypto";
@@ -9692,7 +9822,7 @@ function categorizeSpawnError(err) {
9692
9822
  }
9693
9823
  async function connectToUpstream(config, onDisconnect, onListChanged) {
9694
9824
  const client = new Client(
9695
- { name: "yaw-mcp", version: true ? "0.65.0" : "dev" },
9825
+ { name: "yaw-mcp", version: true ? "0.66.0" : "dev" },
9696
9826
  { capabilities: {} }
9697
9827
  );
9698
9828
  let transport;
@@ -9707,7 +9837,10 @@ async function connectToUpstream(config, onDisconnect, onListChanged) {
9707
9837
  YAW_MCP_VAULT_PASSPHRASE: _excludedVaultPassphrase,
9708
9838
  ...parentEnv
9709
9839
  } = process.env;
9710
- const resolved = await resolveUvSpawn(config.command, config.args ?? []);
9840
+ let resolved = await resolveUvSpawn(config.command, config.args ?? []);
9841
+ if (config.runtime === "oam") {
9842
+ resolved = resolveOamSpawn(resolved.command, resolved.args);
9843
+ }
9711
9844
  const serverEnv = await resolveServerEnv(config.env ?? {}, config.namespace);
9712
9845
  resolvedServerEnv = serverEnv;
9713
9846
  const stdioTransport = new StdioClientTransport({
@@ -10024,7 +10157,7 @@ var ConnectServer = class _ConnectServer {
10024
10157
  this.apiUrl = apiUrl5;
10025
10158
  this.token = token5;
10026
10159
  this.server = new Server(
10027
- { name: "yaw-mcp", version: true ? "0.65.0" : "dev" },
10160
+ { name: "yaw-mcp", version: true ? "0.66.0" : "dev" },
10028
10161
  {
10029
10162
  capabilities: {
10030
10163
  tools: { listChanged: true },
@@ -10070,6 +10203,19 @@ var ConnectServer = class _ConnectServer {
10070
10203
  // same namespace, on deactivate, and on config reconcile.
10071
10204
  toolFilters = /* @__PURE__ */ new Map();
10072
10205
  profile = null;
10206
+ // Shadow-driven install-nudge gate. Resolved once at start() from the
10207
+ // env override (YAW_MCP_INSTALL_NUDGE=1) OR config (installNudge: true);
10208
+ // off by default. When false, discover NEVER runs the shell-history scan
10209
+ // and its output is byte-identical to today (the load-bearing privacy
10210
+ // property). See install-nudge.ts. Stays false in unit tests that skip
10211
+ // start(), so the nudge block is opt-in there too.
10212
+ installNudge = false;
10213
+ // home / env used by the install-nudge shell-history scan. Default to the
10214
+ // real process values; overridable so tests can point the scan at a
10215
+ // synthetic home + stubbed env without touching the developer's real
10216
+ // shell history or ~/.yaw-mcp/ state file.
10217
+ nudgeHome = homedir16();
10218
+ nudgeEnv = process.env;
10073
10219
  // Loaded YAW-MCP.md guides (user-global + project-local). Null until
10074
10220
  // start() has run the loader; fail-open if either file is missing,
10075
10221
  // unreadable, or empty.
@@ -10294,7 +10440,8 @@ var ConnectServer = class _ConnectServer {
10294
10440
  }
10295
10441
  this.persistenceReady = true;
10296
10442
  }
10297
- this.profile = await loadEffectiveProfile(process.cwd()).catch(() => null);
10443
+ const resolvedConfig = await loadYawMcpConfig({ cwd: process.cwd() }).catch(() => null);
10444
+ this.profile = resolvedConfig ? toProfile(resolvedConfig) : null;
10298
10445
  if (this.profile) {
10299
10446
  log("info", "Loaded profile", {
10300
10447
  path: this.profile.path,
@@ -10303,6 +10450,10 @@ var ConnectServer = class _ConnectServer {
10303
10450
  block: this.profile.blocked
10304
10451
  });
10305
10452
  }
10453
+ this.installNudge = installNudgeEnabled(process.env, resolvedConfig);
10454
+ if (this.installNudge) {
10455
+ log("info", "Shadow-driven install nudge enabled");
10456
+ }
10306
10457
  this.guides = await loadGuides(process.cwd()).catch(() => ({ user: null, project: null }));
10307
10458
  if (this.guides.user || this.guides.project) {
10308
10459
  log("info", "Loaded YAW-MCP.md guide", {
@@ -11097,6 +11248,7 @@ var ConnectServer = class _ConnectServer {
11097
11248
  lines.push(` ${server.namespace} \u2014 ${server.name} (disabled in dashboard)`);
11098
11249
  }
11099
11250
  }
11251
+ lines.push(...this.buildInstallCandidatesLines(activeServers));
11100
11252
  const activeCount = this.connections.size;
11101
11253
  const totalTools = Array.from(this.connections.values()).reduce((sum, c) => {
11102
11254
  const f = this.toolFilters.get(c.config.namespace);
@@ -11115,6 +11267,51 @@ ${activeCount} loaded in this session, ${totalTools} tools in context${tokenSumm
11115
11267
  }
11116
11268
  return { content: [{ type: "text", text: lines.join("\n") }] };
11117
11269
  }
11270
+ // Build the opt-in "Install candidates" block from the offline shell-
11271
+ // history shadow scan. Returns [] (no lines, byte-identical output) when
11272
+ // the gate is off — the load-bearing privacy property: with the gate
11273
+ // unset the scan never runs and nothing about shell history is read.
11274
+ //
11275
+ // When ON, for each heavily-used CLI the scan found:
11276
+ // - skip unless count >= INSTALL_NUDGE_MIN_COUNT (noise floor),
11277
+ // - skip unless a FIRST-PARTY install target exists (installTargetForCli;
11278
+ // a CLI like kubectl/npm/ssh with no target produces no nudge),
11279
+ // - skip if ANY namespace the CLI maps to is already installed (the user
11280
+ // already has a server that covers it — intersect the hit's namespaces
11281
+ // with the installed set),
11282
+ // - skip if the per-CLI cooldown hasn't elapsed (shouldNudge).
11283
+ // Surviving CLIs are recorded (recordNudge) so they stay suppressed for
11284
+ // the cooldown, and rendered as one line + an mcp_connect_install sketch.
11285
+ //
11286
+ // Privacy: the only data emitted is the aggregate integer count + the
11287
+ // first-party package / namespace / name. No raw history line, command
11288
+ // text, or argument ever reaches this output, and nothing here is sent to
11289
+ // analytics — scanShellHistoryForShadows is local-only and returns just
11290
+ // { cli, count, namespaces }.
11291
+ buildInstallCandidatesLines(activeServers) {
11292
+ if (!this.installNudge) return [];
11293
+ const installedNamespaces = new Set(activeServers.map((s) => s.namespace));
11294
+ const hits = scanShellHistoryForShadows({ home: this.nudgeHome, env: this.nudgeEnv });
11295
+ const candidates = [];
11296
+ for (const hit of hits) {
11297
+ if (hit.count < INSTALL_NUDGE_MIN_COUNT) continue;
11298
+ if (hit.namespaces.some((ns) => installedNamespaces.has(ns))) continue;
11299
+ const target = installTargetForCli(hit.cli);
11300
+ if (!target) continue;
11301
+ if (installedNamespaces.has(target.namespace)) continue;
11302
+ if (!shouldNudge(hit.cli, this.nudgeHome)) continue;
11303
+ candidates.push({ cli: hit.cli, count: hit.count, target });
11304
+ }
11305
+ if (candidates.length === 0) return [];
11306
+ const lines = ["\nInstall candidates (from your recent shell usage; history stays local):"];
11307
+ for (const { cli, count, target } of candidates) {
11308
+ lines.push(` ${cli.padEnd(10)} (ran ${count}x recently) -> install ${target.package}`);
11309
+ const sketch = `mcp_connect_install({ name: ${JSON.stringify(target.name)}, namespace: ${JSON.stringify(target.namespace)}, type: "local", command: "npx", args: ["-y", ${JSON.stringify(target.package)}] })`;
11310
+ lines.push(` ${sketch}`);
11311
+ recordNudge(cli, this.nudgeHome);
11312
+ }
11313
+ return lines;
11314
+ }
11118
11315
  // Activate a single server by namespace. Shared by handleActivate,
11119
11316
  // handleDispatch, and handleDiscoverWithAutoWarm so error handling,
11120
11317
  // retries, caching, and tool-report round-trips live in one place.
@@ -12581,16 +12778,16 @@ function truncateVersion(v) {
12581
12778
  import { homedir as homedir17 } from "os";
12582
12779
 
12583
12780
  // src/sync-state.ts
12584
- import { existsSync as existsSync7 } from "fs";
12781
+ import { existsSync as existsSync8 } from "fs";
12585
12782
  import { mkdir as mkdir5, readFile as readFile13 } from "fs/promises";
12586
- import { dirname as dirname4, join as join12 } from "path";
12783
+ import { dirname as dirname5, join as join13 } from "path";
12587
12784
  var SYNC_STATE_FILENAME = "sync-state.json";
12588
12785
  function syncStatePath(home) {
12589
- return join12(home, CONFIG_DIRNAME, SYNC_STATE_FILENAME);
12786
+ return join13(home, CONFIG_DIRNAME, SYNC_STATE_FILENAME);
12590
12787
  }
12591
12788
  async function readSyncState(home) {
12592
12789
  const path5 = syncStatePath(home);
12593
- if (!existsSync7(path5)) return {};
12790
+ if (!existsSync8(path5)) return {};
12594
12791
  try {
12595
12792
  const raw = await readFile13(path5, "utf8");
12596
12793
  const parsed = JSON.parse(raw);
@@ -12602,7 +12799,7 @@ async function readSyncState(home) {
12602
12799
  }
12603
12800
  async function writeSyncState(home, state) {
12604
12801
  const path5 = syncStatePath(home);
12605
- await mkdir5(dirname4(path5), { recursive: true });
12802
+ await mkdir5(dirname5(path5), { recursive: true });
12606
12803
  const existing = await readSyncState(home);
12607
12804
  const merged = { ...existing, ...state };
12608
12805
  await atomicWriteFile(path5, `${JSON.stringify(merged, null, 2)}
@@ -12967,10 +13164,10 @@ function suggestFlag(input, limit = 2) {
12967
13164
  }
12968
13165
 
12969
13166
  // src/sync-cmd.ts
12970
- import { existsSync as existsSync8 } from "fs";
13167
+ import { existsSync as existsSync9 } from "fs";
12971
13168
  import { mkdir as mkdir6, readFile as readFile14 } from "fs/promises";
12972
13169
  import { homedir as homedir19 } from "os";
12973
- import { dirname as dirname5, join as join13 } from "path";
13170
+ import { dirname as dirname6, join as join14 } from "path";
12974
13171
  var SYNC_USAGE = `Usage: yaw-mcp sync <push|pull|status> [--json]
12975
13172
 
12976
13173
  Replicate ~/.yaw-mcp/bundles.json across machines via your Yaw
@@ -13017,11 +13214,11 @@ ${SYNC_USAGE}` };
13017
13214
  return { ok: true, options: opts };
13018
13215
  }
13019
13216
  function bundlesPath(home) {
13020
- return join13(home, CONFIG_DIRNAME, BUNDLES_FILENAME2);
13217
+ return join14(home, CONFIG_DIRNAME, BUNDLES_FILENAME2);
13021
13218
  }
13022
13219
  async function readLocalBundles(home) {
13023
13220
  const path5 = bundlesPath(home);
13024
- if (!existsSync8(path5)) return { version: 1, servers: [] };
13221
+ if (!existsSync9(path5)) return { version: 1, servers: [] };
13025
13222
  const raw = await readFile14(path5, "utf8");
13026
13223
  let parsed;
13027
13224
  try {
@@ -13037,7 +13234,7 @@ async function readLocalBundles(home) {
13037
13234
  }
13038
13235
  async function writeLocalBundles(home, file) {
13039
13236
  const path5 = bundlesPath(home);
13040
- await mkdir6(dirname5(path5), { recursive: true });
13237
+ await mkdir6(dirname6(path5), { recursive: true });
13041
13238
  await atomicWriteFile(path5, `${JSON.stringify(file, null, 2)}
13042
13239
  `);
13043
13240
  return path5;
@@ -13648,7 +13845,7 @@ if (subcommand === "compliance") {
13648
13845
  `);
13649
13846
  process.exit(0);
13650
13847
  } else if (subcommand === "--version" || subcommand === "-V") {
13651
- process.stdout.write(`yaw-mcp ${true ? "0.65.0" : "dev"}
13848
+ process.stdout.write(`yaw-mcp ${true ? "0.66.0" : "dev"}
13652
13849
  `);
13653
13850
  process.exit(0);
13654
13851
  } else if (subcommand && !subcommand.startsWith("-")) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yawlabs/mcp",
3
- "version": "0.65.0",
3
+ "version": "0.66.0",
4
4
  "mcpName": "io.github.YawLabs/mcp",
5
5
  "description": "Yaw MCP -- MCP servers, managed. Free to run locally; Yaw Team adds cross-machine sync.",
6
6
  "license": "UNLICENSED",