@yawlabs/mcph 0.33.0 → 0.35.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 +8 -0
- package/dist/index.js +67 -21
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
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.35.0 — 2026-04-18
|
|
6
|
+
|
|
7
|
+
- **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).
|
|
8
|
+
|
|
9
|
+
## 0.34.0 — 2026-04-18
|
|
10
|
+
|
|
11
|
+
- **Cross-session reliability block in `mcp_connect_health`** — New section at the bottom of health output surfaces flaky *dormant* namespaces pulled from persisted learning: `<namespace> — N calls, P% success, last used <age> ago`. Threshold is deliberately high (≥3 dispatches, <80% success) so a one-off failure doesn't light up the panel; loaded namespaces are skipped (in-session block already covers them). Sorted worst-rate first, ties broken by most calls then alpha; capped at 5. Also fixes a gap where `handleHealth` returned early on an empty-connections session and never showed dormant history — now it falls through so operators can see which past servers were unreliable even before loading anything.
|
|
12
|
+
|
|
5
13
|
## 0.33.0 — 2026-04-18
|
|
6
14
|
|
|
7
15
|
- **`mcph doctor` ENVIRONMENT section** — New block enumerating every behavior-modifier env var mcph actually reads (`MCPH_POLL_INTERVAL`, `MCPH_SERVER_CAP`, `MCPH_MIN_COMPLIANCE`, `MCPH_AUTO_LOAD`, `MCPH_PRUNE_RESPONSES`). Each shows its current value, or `(not set — <default>)` when unset. Closes a diagnostic gap where users reporting "my server cap isn't taking effect" or "compliance filter isn't blocking anything" had no doctor signal on whether the knob was even set. TOKEN / URL / DISABLE_PERSISTENCE still get their dedicated sections (richer context there).
|
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.
|
|
949
|
+
var VERSION = true ? "0.35.0" : "dev";
|
|
950
950
|
async function runDoctor(opts = {}) {
|
|
951
951
|
const lines = [];
|
|
952
952
|
const write = opts.out ?? ((s) => process.stdout.write(s));
|
|
@@ -2402,6 +2402,19 @@ var LearningStore = class {
|
|
|
2402
2402
|
}
|
|
2403
2403
|
return out;
|
|
2404
2404
|
}
|
|
2405
|
+
// Iterate the current store as { namespace, usage } pairs. Used by
|
|
2406
|
+
// observability paths (e.g., mcp_connect_health's cross-session
|
|
2407
|
+
// reliability block) that need to walk every recorded namespace.
|
|
2408
|
+
entries() {
|
|
2409
|
+
const out = [];
|
|
2410
|
+
for (const [ns, u] of this.usage) {
|
|
2411
|
+
out.push({
|
|
2412
|
+
namespace: ns,
|
|
2413
|
+
usage: { dispatched: u.dispatched, succeeded: u.succeeded, lastUsedAt: u.lastUsedAt }
|
|
2414
|
+
});
|
|
2415
|
+
}
|
|
2416
|
+
return out;
|
|
2417
|
+
}
|
|
2405
2418
|
// Replace in-memory state with the given snapshot. Used on startup
|
|
2406
2419
|
// to restore persisted signal; silently overwrites anything already
|
|
2407
2420
|
// in the store, so callers should only invoke this before recording.
|
|
@@ -3909,7 +3922,7 @@ function categorizeSpawnError(err) {
|
|
|
3909
3922
|
}
|
|
3910
3923
|
async function connectToUpstream(config, onDisconnect, onListChanged) {
|
|
3911
3924
|
const client = new Client(
|
|
3912
|
-
{ name: "mcph", version: true ? "0.
|
|
3925
|
+
{ name: "mcph", version: true ? "0.35.0" : "dev" },
|
|
3913
3926
|
{ capabilities: {} }
|
|
3914
3927
|
);
|
|
3915
3928
|
let transport;
|
|
@@ -4310,6 +4323,8 @@ async function reportTools(serverId, tools) {
|
|
|
4310
4323
|
// src/usage-hints.ts
|
|
4311
4324
|
var MAX_PEERS = 3;
|
|
4312
4325
|
var MIN_SUCCESS_TO_SHOW = 1;
|
|
4326
|
+
var RELIABILITY_MIN_OBSERVATIONS = 3;
|
|
4327
|
+
var RELIABILITY_THRESHOLD = 0.8;
|
|
4313
4328
|
function buildCoUsageMap(packs) {
|
|
4314
4329
|
const result = /* @__PURE__ */ new Map();
|
|
4315
4330
|
for (const pack of packs) {
|
|
@@ -4342,6 +4357,13 @@ function formatUsageHint(usage, coUsedWith) {
|
|
|
4342
4357
|
if (parts.length === 0) return null;
|
|
4343
4358
|
return `usage: ${parts.join("; ")}`;
|
|
4344
4359
|
}
|
|
4360
|
+
function formatReliabilityWarning(usage) {
|
|
4361
|
+
if (!usage || usage.dispatched < RELIABILITY_MIN_OBSERVATIONS) return null;
|
|
4362
|
+
const rate = usage.succeeded / usage.dispatched;
|
|
4363
|
+
if (rate >= RELIABILITY_THRESHOLD) return null;
|
|
4364
|
+
const pct = Math.round(rate * 100);
|
|
4365
|
+
return `reliability: ${pct}% success across ${usage.dispatched} past calls`;
|
|
4366
|
+
}
|
|
4345
4367
|
|
|
4346
4368
|
// src/server.ts
|
|
4347
4369
|
var DEFAULT_POLL_INTERVAL_MS = 6e4;
|
|
@@ -4426,7 +4448,7 @@ var ConnectServer = class _ConnectServer {
|
|
|
4426
4448
|
this.apiUrl = apiUrl6;
|
|
4427
4449
|
this.token = token6;
|
|
4428
4450
|
this.server = new Server(
|
|
4429
|
-
{ name: "mcph", version: true ? "0.
|
|
4451
|
+
{ name: "mcph", version: true ? "0.35.0" : "dev" },
|
|
4430
4452
|
{
|
|
4431
4453
|
capabilities: {
|
|
4432
4454
|
tools: { listChanged: true },
|
|
@@ -5301,6 +5323,10 @@ var ConnectServer = class _ConnectServer {
|
|
|
5301
5323
|
if (shadow) lines.push(` ${shadow}`);
|
|
5302
5324
|
const warning = formatHealthWarning(connection?.health, this.activationFailures.get(server.namespace));
|
|
5303
5325
|
if (warning) lines.push(` ${warning}`);
|
|
5326
|
+
if (!connection) {
|
|
5327
|
+
const reliability = formatReliabilityWarning(this.learning.get(server.namespace));
|
|
5328
|
+
if (reliability) lines.push(` ${reliability}`);
|
|
5329
|
+
}
|
|
5304
5330
|
const usageHint = formatUsageHint(this.learning.get(server.namespace), coUsageMap.get(server.namespace) ?? []);
|
|
5305
5331
|
if (usageHint) lines.push(` ${usageHint}`);
|
|
5306
5332
|
if (!connection) {
|
|
@@ -6161,23 +6187,43 @@ Use mcp_connect_discover to see imported servers.`
|
|
|
6161
6187
|
}
|
|
6162
6188
|
if (this.connections.size === 0) {
|
|
6163
6189
|
lines.push("No servers loaded in this session yet.");
|
|
6164
|
-
|
|
6165
|
-
|
|
6166
|
-
|
|
6167
|
-
|
|
6168
|
-
|
|
6169
|
-
|
|
6170
|
-
|
|
6171
|
-
|
|
6172
|
-
|
|
6173
|
-
|
|
6174
|
-
|
|
6175
|
-
|
|
6176
|
-
|
|
6177
|
-
|
|
6178
|
-
|
|
6179
|
-
|
|
6180
|
-
|
|
6190
|
+
} else {
|
|
6191
|
+
lines.push("Session health:\n");
|
|
6192
|
+
for (const [namespace, conn] of this.connections) {
|
|
6193
|
+
const h = conn.health;
|
|
6194
|
+
const avgLatency = h.totalCalls > 0 ? Math.round(h.totalLatencyMs / h.totalCalls) : 0;
|
|
6195
|
+
const errorRate = h.totalCalls > 0 ? Math.round(h.errorCount / h.totalCalls * 100) : 0;
|
|
6196
|
+
const idleCount = this.idleCallCounts.get(namespace) ?? 0;
|
|
6197
|
+
const idleLimit = adaptiveThreshold(namespace, this.recentToolCalls, _ConnectServer.IDLE_CALL_THRESHOLD);
|
|
6198
|
+
const toolNames = conn.tools.map((t) => t.name).join(", ");
|
|
6199
|
+
lines.push(` ${namespace} [${conn.status}] (${conn.config.type})`);
|
|
6200
|
+
lines.push(` tools: ${conn.tools.length} \u2014 ${toolNames}`);
|
|
6201
|
+
lines.push(` calls: ${h.totalCalls}, errors: ${h.errorCount} (${errorRate}%)`);
|
|
6202
|
+
lines.push(` avg latency: ${avgLatency}ms`);
|
|
6203
|
+
lines.push(` idle: ${idleCount}/${idleLimit} until auto-unload`);
|
|
6204
|
+
if (h.lastErrorMessage) {
|
|
6205
|
+
lines.push(` last error: ${h.lastErrorMessage} at ${h.lastErrorAt}`);
|
|
6206
|
+
}
|
|
6207
|
+
}
|
|
6208
|
+
}
|
|
6209
|
+
const now = Date.now();
|
|
6210
|
+
const flaky = this.learning.entries().filter(({ namespace, usage }) => {
|
|
6211
|
+
if (this.connections.has(namespace)) return false;
|
|
6212
|
+
if (usage.dispatched < 3) return false;
|
|
6213
|
+
return usage.succeeded / usage.dispatched < 0.8;
|
|
6214
|
+
}).sort((a, b) => {
|
|
6215
|
+
const aRate = a.usage.succeeded / a.usage.dispatched;
|
|
6216
|
+
const bRate = b.usage.succeeded / b.usage.dispatched;
|
|
6217
|
+
if (aRate !== bRate) return aRate - bRate;
|
|
6218
|
+
if (a.usage.dispatched !== b.usage.dispatched) return b.usage.dispatched - a.usage.dispatched;
|
|
6219
|
+
return a.namespace.localeCompare(b.namespace);
|
|
6220
|
+
}).slice(0, 5);
|
|
6221
|
+
if (flaky.length > 0) {
|
|
6222
|
+
lines.push("\nCross-session reliability (dormant, <80% success):");
|
|
6223
|
+
for (const { namespace, usage } of flaky) {
|
|
6224
|
+
const rate = Math.round(usage.succeeded / usage.dispatched * 100);
|
|
6225
|
+
const age = formatRelativeAge(now - usage.lastUsedAt);
|
|
6226
|
+
lines.push(` ${namespace} \u2014 ${usage.dispatched} calls, ${rate}% success, last used ${age} ago`);
|
|
6181
6227
|
}
|
|
6182
6228
|
}
|
|
6183
6229
|
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
@@ -6507,7 +6553,7 @@ ${installBlock}
|
|
|
6507
6553
|
);
|
|
6508
6554
|
process.exit(0);
|
|
6509
6555
|
} else if (subcommand === "--version" || subcommand === "-V") {
|
|
6510
|
-
process.stdout.write(`mcph ${true ? "0.
|
|
6556
|
+
process.stdout.write(`mcph ${true ? "0.35.0" : "dev"}
|
|
6511
6557
|
`);
|
|
6512
6558
|
process.exit(0);
|
|
6513
6559
|
} else if (subcommand && !subcommand.startsWith("-")) {
|
package/package.json
CHANGED