@yawlabs/mcph 0.35.0 → 0.36.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,10 @@
2
2
 
3
3
  All notable changes to `@yawlabs/mcph` are documented here. This project uses [semantic versioning](https://semver.org) and a CI-gated release flow: pushing a `vX.Y.Z` tag triggers `.github/workflows/release.yml`, which publishes to npm.
4
4
 
5
+ ## 0.36.0 — 2026-04-18
6
+
7
+ - **Negative signal in dispatch ranking (`boostFactor` penalty branch)** — The learning store's `boostFactor` now drops *below* 1.0 for namespaces with flaky history, mirroring the existing upward boost. Threshold is the same ≥3 dispatches / <80% success gate used by discover's inline reliability warning (v0.35.0) and health's cross-session block (v0.34.0) — so a server flagged flaky in those views also loses rank points at dispatch time rather than quietly continuing to win routing. Floor is `-10%` (`LEARNING_MIN_BOOST = 0.9`), symmetric with the existing `+10%` ceiling. Rate-based signal trumps count-based: a namespace with 10 successes but a 50% overall rate is flaky, not useful, and the penalty branch beats the positive branch in that case.
8
+
5
9
  ## 0.35.0 — 2026-04-18
6
10
 
7
11
  - **Inline reliability warning in `mcp_connect_discover`** — Discover now annotates dormant (not currently loaded) servers with `reliability: P% success across N past calls` when persisted learning shows ≥3 dispatches and <80% success. Renders under the server card right after the live health warning, so the LLM sees the flaky history *before* it picks a server to activate — not only after `handleHealth` surfaces it post-hoc. Thresholds match the cross-session reliability block from v0.34.0 so the two views stay consistent. Suppressed for loaded servers (the live per-call warning already covers them with fresher data).
package/dist/index.js CHANGED
@@ -946,7 +946,7 @@ function errorMessage(err) {
946
946
  }
947
947
 
948
948
  // src/doctor-cmd.ts
949
- var VERSION = true ? "0.35.0" : "dev";
949
+ var VERSION = true ? "0.36.0" : "dev";
950
950
  async function runDoctor(opts = {}) {
951
951
  const lines = [];
952
952
  const write = opts.out ?? ((s) => process.stdout.write(s));
@@ -2355,6 +2355,8 @@ function pushToolCall(history, record, limit = HISTORY_LIMIT) {
2355
2355
  // src/learning.ts
2356
2356
  var LEARNING_MIN_OBSERVATIONS = 3;
2357
2357
  var LEARNING_MAX_BOOST = 1.1;
2358
+ var LEARNING_MIN_BOOST = 0.9;
2359
+ var PENALTY_RATE_THRESHOLD = 0.8;
2358
2360
  var SATURATION_AT = 10;
2359
2361
  var LearningStore = class {
2360
2362
  usage = /* @__PURE__ */ new Map();
@@ -2377,13 +2379,23 @@ var LearningStore = class {
2377
2379
  get(namespace) {
2378
2380
  return this.usage.get(namespace);
2379
2381
  }
2380
- // Boost factor in [1.0, LEARNING_MAX_BOOST]. Grows with successful
2381
- // observation count, saturating at SATURATION_AT successes so a
2382
- // heavily-used server can't runaway-win against legitimately better
2383
- // matches.
2382
+ // Boost factor in [LEARNING_MIN_BOOST, LEARNING_MAX_BOOST]. Penalty
2383
+ // branch wins when a namespace has been dispatched enough times and
2384
+ // its success rate has fallen below 80%; otherwise the positive
2385
+ // branch grows the factor with successful observation count, saturating
2386
+ // at SATURATION_AT successes so a heavily-used server can't runaway-win
2387
+ // against legitimately better matches.
2384
2388
  boostFactor(namespace) {
2385
2389
  const u = this.usage.get(namespace);
2386
- if (!u || u.succeeded < LEARNING_MIN_OBSERVATIONS) return 1;
2390
+ if (!u) return 1;
2391
+ if (u.dispatched >= LEARNING_MIN_OBSERVATIONS) {
2392
+ const rate = u.succeeded / u.dispatched;
2393
+ if (rate < PENALTY_RATE_THRESHOLD) {
2394
+ const distance = Math.min(1, (PENALTY_RATE_THRESHOLD - rate) / PENALTY_RATE_THRESHOLD);
2395
+ return 1 - distance * (1 - LEARNING_MIN_BOOST);
2396
+ }
2397
+ }
2398
+ if (u.succeeded < LEARNING_MIN_OBSERVATIONS) return 1;
2387
2399
  const progress = Math.min(1, u.succeeded / SATURATION_AT);
2388
2400
  return 1 + progress * (LEARNING_MAX_BOOST - 1);
2389
2401
  }
@@ -3922,7 +3934,7 @@ function categorizeSpawnError(err) {
3922
3934
  }
3923
3935
  async function connectToUpstream(config, onDisconnect, onListChanged) {
3924
3936
  const client = new Client(
3925
- { name: "mcph", version: true ? "0.35.0" : "dev" },
3937
+ { name: "mcph", version: true ? "0.36.0" : "dev" },
3926
3938
  { capabilities: {} }
3927
3939
  );
3928
3940
  let transport;
@@ -4448,7 +4460,7 @@ var ConnectServer = class _ConnectServer {
4448
4460
  this.apiUrl = apiUrl6;
4449
4461
  this.token = token6;
4450
4462
  this.server = new Server(
4451
- { name: "mcph", version: true ? "0.35.0" : "dev" },
4463
+ { name: "mcph", version: true ? "0.36.0" : "dev" },
4452
4464
  {
4453
4465
  capabilities: {
4454
4466
  tools: { listChanged: true },
@@ -6553,7 +6565,7 @@ ${installBlock}
6553
6565
  );
6554
6566
  process.exit(0);
6555
6567
  } else if (subcommand === "--version" || subcommand === "-V") {
6556
- process.stdout.write(`mcph ${true ? "0.35.0" : "dev"}
6568
+ process.stdout.write(`mcph ${true ? "0.36.0" : "dev"}
6557
6569
  `);
6558
6570
  process.exit(0);
6559
6571
  } else if (subcommand && !subcommand.startsWith("-")) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yawlabs/mcph",
3
- "version": "0.35.0",
3
+ "version": "0.36.0",
4
4
  "description": "mcp.hosting — one install, all your MCP servers, managed from the cloud",
5
5
  "license": "UNLICENSED",
6
6
  "author": "Yaw Labs <contact@yaw.sh> (https://yaw.sh)",