@yawlabs/mcph 0.37.0 → 0.38.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/dist/index.js +73 -8
- 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.38.0 — 2026-04-18
|
|
6
|
+
|
|
7
|
+
- **`mcph reset-learning` CLI subcommand** — Deletes `~/.mcph/state.json` so cross-session learning starts fresh; prints the entry counts that were cleared. Pairs with v0.37.0's doctor RELIABILITY section: once a namespace has been flagged flaky, the dispatch penalty branch (v0.36.0) keeps suppressing it until enough new successes pile up — but if the user has since fixed the underlying cause (rotated a token, swapped the upstream, re-authed), that history is stale and the penalty has overstayed its welcome. This gives them a direct CLI lever to clear it. Scope is all-or-nothing by design; a per-namespace flag is footgunny (user clears one, forgets the others, keeps getting silently mis-ranked). No-op with an explanatory message when `MCPH_DISABLE_PERSISTENCE` is set or the file doesn't exist, so `mcph reset-learning` never surprises. Exit 0 on success or no-op, exit 1 on I/O error (permissions, disk).
|
|
8
|
+
|
|
5
9
|
## 0.37.0 — 2026-04-18
|
|
6
10
|
|
|
7
11
|
- **`mcph doctor` RELIABILITY section** — New block surfaces flaky dormant namespaces pulled directly from `~/.mcph/state.json`, using the same ≥3-dispatches / <80%-success definition as `mcp_connect_health`'s cross-session reliability block — so the CLI diagnostic and the LLM-facing health tool agree on what "flaky" means. Sorted worst-rate first, capped at 5. Silently omitted when no namespace qualifies, state.json doesn't exist yet, or `MCPH_DISABLE_PERSISTENCE` is set. Threshold constants + sort logic extracted into `selectFlakyNamespaces` so handleHealth and doctor can't drift apart.
|
package/dist/index.js
CHANGED
|
@@ -1004,7 +1004,7 @@ function selectFlakyNamespaces(entries, limit) {
|
|
|
1004
1004
|
}
|
|
1005
1005
|
|
|
1006
1006
|
// src/doctor-cmd.ts
|
|
1007
|
-
var VERSION = true ? "0.
|
|
1007
|
+
var VERSION = true ? "0.38.0" : "dev";
|
|
1008
1008
|
async function runDoctor(opts = {}) {
|
|
1009
1009
|
const lines = [];
|
|
1010
1010
|
const write = opts.out ?? ((s) => process.stdout.write(s));
|
|
@@ -1791,9 +1791,61 @@ ${USAGE}` };
|
|
|
1791
1791
|
}
|
|
1792
1792
|
var INSTALL_USAGE = USAGE;
|
|
1793
1793
|
|
|
1794
|
+
// src/reset-learning-cmd.ts
|
|
1795
|
+
import { unlink } from "fs/promises";
|
|
1796
|
+
import { homedir as homedir6 } from "os";
|
|
1797
|
+
import { join as join6 } from "path";
|
|
1798
|
+
async function runResetLearning(opts = {}) {
|
|
1799
|
+
const home = opts.home ?? homedir6();
|
|
1800
|
+
const env = opts.env ?? process.env;
|
|
1801
|
+
const write = opts.out ?? ((s) => process.stdout.write(s));
|
|
1802
|
+
const writeErr = opts.err ?? ((s) => process.stderr.write(s));
|
|
1803
|
+
const lines = [];
|
|
1804
|
+
const print = (s = "") => {
|
|
1805
|
+
lines.push(s);
|
|
1806
|
+
write(`${s}
|
|
1807
|
+
`);
|
|
1808
|
+
};
|
|
1809
|
+
const printErr = (s) => {
|
|
1810
|
+
lines.push(s);
|
|
1811
|
+
writeErr(`${s}
|
|
1812
|
+
`);
|
|
1813
|
+
};
|
|
1814
|
+
const filePath = join6(userConfigDir(home), STATE_FILENAME);
|
|
1815
|
+
const raw = env.MCPH_DISABLE_PERSISTENCE;
|
|
1816
|
+
const disabled = raw !== void 0 && raw !== "" && (raw === "1" || raw.toLowerCase() === "true");
|
|
1817
|
+
if (disabled) {
|
|
1818
|
+
print("mcph reset-learning: persistence is disabled (MCPH_DISABLE_PERSISTENCE) \u2014 nothing to clear.");
|
|
1819
|
+
return { exitCode: 0, lines, removed: false, path: filePath };
|
|
1820
|
+
}
|
|
1821
|
+
const persisted = await loadState(filePath);
|
|
1822
|
+
const learningCount = Object.keys(persisted.learning).length;
|
|
1823
|
+
const packCount = persisted.packHistory.length;
|
|
1824
|
+
try {
|
|
1825
|
+
await unlink(filePath);
|
|
1826
|
+
} catch (err) {
|
|
1827
|
+
if (isFileNotFound2(err)) {
|
|
1828
|
+
print("mcph reset-learning: no persisted state to reset.");
|
|
1829
|
+
print(` path: ${filePath}`);
|
|
1830
|
+
return { exitCode: 0, lines, removed: false, path: filePath };
|
|
1831
|
+
}
|
|
1832
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1833
|
+
printErr(`mcph reset-learning: failed to remove ${filePath}: ${msg}`);
|
|
1834
|
+
return { exitCode: 1, lines, removed: false, path: filePath };
|
|
1835
|
+
}
|
|
1836
|
+
print("mcph reset-learning: cleared persisted state.");
|
|
1837
|
+
print(` path: ${filePath}`);
|
|
1838
|
+
print(` learning entries removed: ${learningCount}`);
|
|
1839
|
+
print(` pack history entries removed: ${packCount}`);
|
|
1840
|
+
return { exitCode: 0, lines, removed: true, path: filePath };
|
|
1841
|
+
}
|
|
1842
|
+
function isFileNotFound2(err) {
|
|
1843
|
+
return !!err && typeof err === "object" && "code" in err && err.code === "ENOENT";
|
|
1844
|
+
}
|
|
1845
|
+
|
|
1794
1846
|
// src/server.ts
|
|
1795
1847
|
import { readFile as readFile6 } from "fs/promises";
|
|
1796
|
-
import { homedir as
|
|
1848
|
+
import { homedir as homedir7 } from "os";
|
|
1797
1849
|
import { isAbsolute, relative, resolve as resolve3 } from "path";
|
|
1798
1850
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
1799
1851
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
@@ -4013,7 +4065,7 @@ function categorizeSpawnError(err) {
|
|
|
4013
4065
|
}
|
|
4014
4066
|
async function connectToUpstream(config, onDisconnect, onListChanged) {
|
|
4015
4067
|
const client = new Client(
|
|
4016
|
-
{ name: "mcph", version: true ? "0.
|
|
4068
|
+
{ name: "mcph", version: true ? "0.38.0" : "dev" },
|
|
4017
4069
|
{ capabilities: {} }
|
|
4018
4070
|
);
|
|
4019
4071
|
let transport;
|
|
@@ -4494,7 +4546,7 @@ var ConnectServer = class _ConnectServer {
|
|
|
4494
4546
|
this.apiUrl = apiUrl6;
|
|
4495
4547
|
this.token = token6;
|
|
4496
4548
|
this.server = new Server(
|
|
4497
|
-
{ name: "mcph", version: true ? "0.
|
|
4549
|
+
{ name: "mcph", version: true ? "0.38.0" : "dev" },
|
|
4498
4550
|
{
|
|
4499
4551
|
capabilities: {
|
|
4500
4552
|
tools: { listChanged: true },
|
|
@@ -5929,7 +5981,7 @@ ${activeCount} loaded in this session, ${totalTools} tools in context${tokenSumm
|
|
|
5929
5981
|
}
|
|
5930
5982
|
const ALLOWED_FILENAMES = ["claude_desktop_config.json", "mcp.json", "settings.json", "mcp_config.json"];
|
|
5931
5983
|
try {
|
|
5932
|
-
const resolved = filepath.startsWith("~/") || filepath.startsWith("~\\") ? resolve3(
|
|
5984
|
+
const resolved = filepath.startsWith("~/") || filepath.startsWith("~\\") ? resolve3(homedir7(), filepath.slice(2)) : resolve3(filepath);
|
|
5933
5985
|
const resolvedBasename = resolved.split(/[/\\]/).pop() || "";
|
|
5934
5986
|
if (!ALLOWED_FILENAMES.includes(resolvedBasename)) {
|
|
5935
5987
|
return {
|
|
@@ -5946,7 +5998,7 @@ ${activeCount} loaded in this session, ${totalTools} tools in context${tokenSumm
|
|
|
5946
5998
|
const rel = relative(base, p);
|
|
5947
5999
|
return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
|
|
5948
6000
|
};
|
|
5949
|
-
if (!isUnder(
|
|
6001
|
+
if (!isUnder(homedir7(), resolved) && !isUnder(process.cwd(), resolved)) {
|
|
5950
6002
|
return {
|
|
5951
6003
|
content: [
|
|
5952
6004
|
{ type: "text", text: "Import path must be under your home directory or the current working directory." }
|
|
@@ -6547,7 +6599,17 @@ To load the top pack in one step, call \`mcp_connect_activate\` with namespaces=
|
|
|
6547
6599
|
};
|
|
6548
6600
|
|
|
6549
6601
|
// src/index.ts
|
|
6550
|
-
var KNOWN_SUBCOMMANDS = [
|
|
6602
|
+
var KNOWN_SUBCOMMANDS = [
|
|
6603
|
+
"compliance",
|
|
6604
|
+
"install",
|
|
6605
|
+
"doctor",
|
|
6606
|
+
"reset-learning",
|
|
6607
|
+
"help",
|
|
6608
|
+
"--help",
|
|
6609
|
+
"-h",
|
|
6610
|
+
"--version",
|
|
6611
|
+
"-V"
|
|
6612
|
+
];
|
|
6551
6613
|
var subcommand = process.argv[2];
|
|
6552
6614
|
if (subcommand === "compliance") {
|
|
6553
6615
|
runComplianceCommand(process.argv.slice(3)).then((code) => process.exit(code));
|
|
@@ -6561,6 +6623,8 @@ if (subcommand === "compliance") {
|
|
|
6561
6623
|
runInstall(parsed.options).then((r) => process.exit(r.exitCode));
|
|
6562
6624
|
} else if (subcommand === "doctor") {
|
|
6563
6625
|
runDoctor().then((r) => process.exit(r.exitCode));
|
|
6626
|
+
} else if (subcommand === "reset-learning") {
|
|
6627
|
+
runResetLearning().then((r) => process.exit(r.exitCode));
|
|
6564
6628
|
} else if (subcommand === "--help" || subcommand === "-h" || subcommand === "help") {
|
|
6565
6629
|
const installBlock = ` ${INSTALL_USAGE.replace(/^Usage: /, "").replace(/\n/g, "\n ")}`;
|
|
6566
6630
|
process.stdout.write(
|
|
@@ -6572,6 +6636,7 @@ if (subcommand === "compliance") {
|
|
|
6572
6636
|
mcph install <client> [flags] Auto-edit an MCP client's config to launch mcph
|
|
6573
6637
|
mcph doctor Print loaded config + detected clients (support diagnostic)
|
|
6574
6638
|
mcph compliance <target> [flags] Run the compliance suite against an MCP server
|
|
6639
|
+
mcph reset-learning Clear cross-session learning history (~/.mcph/state.json)
|
|
6575
6640
|
mcph --version Print version
|
|
6576
6641
|
|
|
6577
6642
|
Install:
|
|
@@ -6592,7 +6657,7 @@ ${installBlock}
|
|
|
6592
6657
|
);
|
|
6593
6658
|
process.exit(0);
|
|
6594
6659
|
} else if (subcommand === "--version" || subcommand === "-V") {
|
|
6595
|
-
process.stdout.write(`mcph ${true ? "0.
|
|
6660
|
+
process.stdout.write(`mcph ${true ? "0.38.0" : "dev"}
|
|
6596
6661
|
`);
|
|
6597
6662
|
process.exit(0);
|
|
6598
6663
|
} else if (subcommand && !subcommand.startsWith("-")) {
|
package/package.json
CHANGED