@yawlabs/mcph 0.28.1 → 0.29.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 +4 -0
- package/README.md +1 -0
- package/dist/index.js +71 -6
- package/package.json +1 -1
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.29.0 — 2026-04-18
|
|
6
|
+
|
|
7
|
+
- **Compliance-aware routing (`MCPH_MIN_COMPLIANCE`)** — Phase 3 item. Set the env var to `A`, `B`, `C`, `D`, or `F` and `mcp_connect_activate` refuses to load any installed server whose reported `complianceGrade` is below the floor, with an error that names the grade and the env var to unset. `mcp_connect_discover` annotates below-grade servers in place (so the model knows they exist and why they won't auto-activate) and emits a "Compliance filter active" header. Forward-compatible schema: the optional `complianceGrade` field on `UpstreamServerConfig` rides the existing `/api/connect/config` response — the feature kicks in automatically once the backend starts populating grades. Ungraded servers always pass (don't punish unknown).
|
|
8
|
+
|
|
5
9
|
## 0.28.1 — 2026-04-18
|
|
6
10
|
|
|
7
11
|
Docs-only release.
|
package/README.md
CHANGED
|
@@ -286,6 +286,7 @@ Rotate a credential in one place (the dashboard), every machine picks up the new
|
|
|
286
286
|
| `MCPH_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. |
|
|
287
287
|
| `MCPH_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 `~/.mcph/state.json`. |
|
|
288
288
|
| `MCPH_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. |
|
|
289
|
+
| `MCPH_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). |
|
|
289
290
|
| `MCP_CONNECT_TIMEOUT` | No | Connection timeout in ms for upstream servers (default: `15000`) |
|
|
290
291
|
| `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. |
|
|
291
292
|
|
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.29.0" : "dev";
|
|
950
950
|
async function runDoctor(opts = {}) {
|
|
951
951
|
const lines = [];
|
|
952
952
|
const write = opts.out ?? ((s) => process.stdout.write(s));
|
|
@@ -1825,6 +1825,42 @@ function bundleActivateHint(bundle) {
|
|
|
1825
1825
|
return `mcp_connect_activate({ namespaces: ${JSON.stringify(bundle.namespaces)} })`;
|
|
1826
1826
|
}
|
|
1827
1827
|
|
|
1828
|
+
// src/compliance.ts
|
|
1829
|
+
var GRADE_ORDER = {
|
|
1830
|
+
A: 4,
|
|
1831
|
+
B: 3,
|
|
1832
|
+
C: 2,
|
|
1833
|
+
D: 1,
|
|
1834
|
+
F: 0
|
|
1835
|
+
};
|
|
1836
|
+
function gradeRank(grade) {
|
|
1837
|
+
if (!grade) return -1;
|
|
1838
|
+
const up = grade.toUpperCase();
|
|
1839
|
+
if (up in GRADE_ORDER) return GRADE_ORDER[up];
|
|
1840
|
+
return -1;
|
|
1841
|
+
}
|
|
1842
|
+
var invalidWarned = false;
|
|
1843
|
+
function parseMinCompliance(raw) {
|
|
1844
|
+
if (raw === void 0) return null;
|
|
1845
|
+
const trimmed = raw.trim();
|
|
1846
|
+
if (trimmed === "") return null;
|
|
1847
|
+
const up = trimmed.toUpperCase();
|
|
1848
|
+
if (up === "A" || up === "B" || up === "C" || up === "D" || up === "F") {
|
|
1849
|
+
return up;
|
|
1850
|
+
}
|
|
1851
|
+
if (!invalidWarned) {
|
|
1852
|
+
invalidWarned = true;
|
|
1853
|
+
log("warn", "Invalid MCPH_MIN_COMPLIANCE; filter disabled", { value: raw });
|
|
1854
|
+
}
|
|
1855
|
+
return null;
|
|
1856
|
+
}
|
|
1857
|
+
function passesMinCompliance(serverGrade, min) {
|
|
1858
|
+
if (min === null) return true;
|
|
1859
|
+
const serverRank = gradeRank(serverGrade);
|
|
1860
|
+
if (serverRank < 0) return true;
|
|
1861
|
+
return serverRank >= gradeRank(min);
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1828
1864
|
// src/cost-estimate.ts
|
|
1829
1865
|
var BYTES_PER_TOKEN = 4;
|
|
1830
1866
|
var CACHED_TOOL_SCHEMA_PAD_BYTES = 200;
|
|
@@ -2319,7 +2355,7 @@ var META_TOOLS = {
|
|
|
2319
2355
|
},
|
|
2320
2356
|
activate: {
|
|
2321
2357
|
name: "mcp_connect_activate",
|
|
2322
|
-
description: 'Load one or more installed MCP servers\' tools into the current session by namespace. Each server adds its tools to your context, so load only what the current task needs. When you move on, unload servers you\'re done with via `mcp_connect_deactivate` before loading new ones. Tools are prefixed by namespace (e.g., "gh_create_issue"). Pass "server" for one or "servers" for multiple. Optionally pass `tools: [...]` to expose only those tools by name \u2014 the rest stay proxyable via mcp_connect_dispatch.',
|
|
2358
|
+
description: 'Load one or more installed MCP servers\' tools into the current session by namespace. Each server adds its tools to your context, so load only what the current task needs. When you move on, unload servers you\'re done with via `mcp_connect_deactivate` before loading new ones. Tools are prefixed by namespace (e.g., "gh_create_issue"). Pass "server" for one or "servers" for multiple. Optionally pass `tools: [...]` to expose only those tools by name \u2014 the rest stay proxyable via mcp_connect_dispatch. If `MCPH_MIN_COMPLIANCE` is set, activation refuses servers whose reported grade is below the floor (ungraded servers always pass); the refusal message names the grade and the env var to unset.',
|
|
2323
2359
|
inputSchema: {
|
|
2324
2360
|
type: "object",
|
|
2325
2361
|
properties: {
|
|
@@ -3791,7 +3827,7 @@ function categorizeSpawnError(err) {
|
|
|
3791
3827
|
}
|
|
3792
3828
|
async function connectToUpstream(config, onDisconnect, onListChanged) {
|
|
3793
3829
|
const client = new Client(
|
|
3794
|
-
{ name: "mcph", version: true ? "0.
|
|
3830
|
+
{ name: "mcph", version: true ? "0.29.0" : "dev" },
|
|
3795
3831
|
{ capabilities: {} }
|
|
3796
3832
|
);
|
|
3797
3833
|
let transport;
|
|
@@ -4242,6 +4278,9 @@ function isPersistenceDisabled() {
|
|
|
4242
4278
|
if (raw === void 0 || raw === "") return false;
|
|
4243
4279
|
return raw === "1" || raw.toLowerCase() === "true";
|
|
4244
4280
|
}
|
|
4281
|
+
function resolveMinCompliance() {
|
|
4282
|
+
return parseMinCompliance(process.env.MCPH_MIN_COMPLIANCE);
|
|
4283
|
+
}
|
|
4245
4284
|
function isAutoLoadEnabled() {
|
|
4246
4285
|
const raw = process.env.MCPH_AUTO_LOAD;
|
|
4247
4286
|
if (raw === void 0 || raw === "") return false;
|
|
@@ -4305,7 +4344,7 @@ var ConnectServer = class _ConnectServer {
|
|
|
4305
4344
|
this.apiUrl = apiUrl6;
|
|
4306
4345
|
this.token = token6;
|
|
4307
4346
|
this.server = new Server(
|
|
4308
|
-
{ name: "mcph", version: true ? "0.
|
|
4347
|
+
{ name: "mcph", version: true ? "0.29.0" : "dev" },
|
|
4309
4348
|
{
|
|
4310
4349
|
capabilities: {
|
|
4311
4350
|
tools: { listChanged: true },
|
|
@@ -5100,6 +5139,11 @@ var ConnectServer = class _ConnectServer {
|
|
|
5100
5139
|
const lines = [context ? "Servers ranked by relevance:\n" : "Installed MCP servers:\n"];
|
|
5101
5140
|
if (autoWarmed && sorted.length > 0) {
|
|
5102
5141
|
lines.push(`Auto-loaded "${sorted[0].namespace}" \u2014 top match for your query.
|
|
5142
|
+
`);
|
|
5143
|
+
}
|
|
5144
|
+
const minCompliance = resolveMinCompliance();
|
|
5145
|
+
if (minCompliance !== null) {
|
|
5146
|
+
lines.push(`Compliance filter active: MCPH_MIN_COMPLIANCE=${minCompliance}
|
|
5103
5147
|
`);
|
|
5104
5148
|
}
|
|
5105
5149
|
if (context) {
|
|
@@ -5160,7 +5204,17 @@ var ConnectServer = class _ConnectServer {
|
|
|
5160
5204
|
costLabel = ` \u2014 ${formatCostLabel(estimateFromToolCache(cached))}`;
|
|
5161
5205
|
}
|
|
5162
5206
|
}
|
|
5163
|
-
|
|
5207
|
+
let complianceLabel = "";
|
|
5208
|
+
if (minCompliance !== null && server.complianceGrade) {
|
|
5209
|
+
if (passesMinCompliance(server.complianceGrade, minCompliance)) {
|
|
5210
|
+
complianceLabel = ` [${server.complianceGrade}]`;
|
|
5211
|
+
} else {
|
|
5212
|
+
complianceLabel = ` (grade ${server.complianceGrade} \u2014 below MCPH_MIN_COMPLIANCE=${minCompliance}, won't auto-activate)`;
|
|
5213
|
+
}
|
|
5214
|
+
}
|
|
5215
|
+
lines.push(
|
|
5216
|
+
` ${server.namespace} \u2014 ${server.name} [${status}] (${server.type})${relevance}${costLabel}${complianceLabel}`
|
|
5217
|
+
);
|
|
5164
5218
|
const shadow = formatShadowLine(server);
|
|
5165
5219
|
if (shadow) lines.push(` ${shadow}`);
|
|
5166
5220
|
const warning = formatHealthWarning(connection?.health, this.activationFailures.get(server.namespace));
|
|
@@ -5418,10 +5472,21 @@ ${activeCount} loaded in this session, ${totalTools} tools in context${tokenSumm
|
|
|
5418
5472
|
const results = [];
|
|
5419
5473
|
let anyChanged = false;
|
|
5420
5474
|
let anyError = false;
|
|
5475
|
+
const minCompliance = resolveMinCompliance();
|
|
5421
5476
|
const total = namespaces.length;
|
|
5422
5477
|
let i = 0;
|
|
5423
5478
|
for (const namespace of namespaces) {
|
|
5424
5479
|
i += 1;
|
|
5480
|
+
if (minCompliance !== null) {
|
|
5481
|
+
const cfg = this.config?.servers.find((s) => s.namespace === namespace);
|
|
5482
|
+
if (cfg && !passesMinCompliance(cfg.complianceGrade, minCompliance)) {
|
|
5483
|
+
const grade = cfg.complianceGrade ?? "unknown";
|
|
5484
|
+
const message = `Refused to load "${namespace}": compliance grade ${grade} is below MCPH_MIN_COMPLIANCE=${minCompliance}. Unset MCPH_MIN_COMPLIANCE (or lower it) to override.`;
|
|
5485
|
+
results.push(message);
|
|
5486
|
+
anyError = true;
|
|
5487
|
+
continue;
|
|
5488
|
+
}
|
|
5489
|
+
}
|
|
5425
5490
|
progress?.(`Loading ${namespace} (${i}/${total})`, i - 1, total);
|
|
5426
5491
|
const r = await this.activateOne(namespace, progress);
|
|
5427
5492
|
results.push(r.message);
|
|
@@ -6340,7 +6405,7 @@ ${installBlock}
|
|
|
6340
6405
|
);
|
|
6341
6406
|
process.exit(0);
|
|
6342
6407
|
} else if (subcommand === "--version" || subcommand === "-V") {
|
|
6343
|
-
process.stdout.write(`mcph ${true ? "0.
|
|
6408
|
+
process.stdout.write(`mcph ${true ? "0.29.0" : "dev"}
|
|
6344
6409
|
`);
|
|
6345
6410
|
process.exit(0);
|
|
6346
6411
|
} else {
|
package/package.json
CHANGED