@yawlabs/mcph 0.47.7 → 0.48.6
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/dist/index.js +340 -215
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1006,6 +1006,8 @@ var flushTimer = null;
|
|
|
1006
1006
|
var apiUrl = "";
|
|
1007
1007
|
var token = "";
|
|
1008
1008
|
var lastFailure = null;
|
|
1009
|
+
var lastLoggedConnectStatus = null;
|
|
1010
|
+
var lastLoggedDispatchStatus = null;
|
|
1009
1011
|
function getLastAnalyticsFailure() {
|
|
1010
1012
|
return lastFailure;
|
|
1011
1013
|
}
|
|
@@ -1041,12 +1043,19 @@ async function flush() {
|
|
|
1041
1043
|
bodyTimeout: 1e4
|
|
1042
1044
|
});
|
|
1043
1045
|
if (res.statusCode >= 400) {
|
|
1044
|
-
const
|
|
1045
|
-
if (
|
|
1046
|
-
|
|
1046
|
+
const retryable = res.statusCode >= 500 || res.statusCode === 408 || res.statusCode === 429;
|
|
1047
|
+
if (retryable) {
|
|
1048
|
+
const room = MAX_BUFFER - buffer.length;
|
|
1049
|
+
if (room > 0) buffer.push(...events.slice(0, room));
|
|
1050
|
+
}
|
|
1051
|
+
if (lastLoggedConnectStatus !== res.statusCode) {
|
|
1052
|
+
log("warn", "Analytics flush failed", { status: res.statusCode, retried: retryable });
|
|
1053
|
+
lastLoggedConnectStatus = res.statusCode;
|
|
1054
|
+
}
|
|
1047
1055
|
lastFailure = { statusCode: res.statusCode, url, at: Date.now() };
|
|
1048
1056
|
} else {
|
|
1049
1057
|
lastFailure = null;
|
|
1058
|
+
lastLoggedConnectStatus = null;
|
|
1050
1059
|
}
|
|
1051
1060
|
await res.body.text().catch(() => {
|
|
1052
1061
|
});
|
|
@@ -1072,12 +1081,19 @@ async function flushDispatch() {
|
|
|
1072
1081
|
bodyTimeout: 1e4
|
|
1073
1082
|
});
|
|
1074
1083
|
if (res.statusCode >= 400 && res.statusCode !== 204) {
|
|
1075
|
-
const
|
|
1076
|
-
if (
|
|
1077
|
-
|
|
1084
|
+
const retryable = res.statusCode >= 500 || res.statusCode === 408 || res.statusCode === 429;
|
|
1085
|
+
if (retryable) {
|
|
1086
|
+
const room = MAX_BUFFER - dispatchBuffer.length;
|
|
1087
|
+
if (room > 0) dispatchBuffer.push(...events.slice(0, room));
|
|
1088
|
+
}
|
|
1089
|
+
if (lastLoggedDispatchStatus !== res.statusCode) {
|
|
1090
|
+
log("warn", "Dispatch-events flush failed", { status: res.statusCode, retried: retryable });
|
|
1091
|
+
lastLoggedDispatchStatus = res.statusCode;
|
|
1092
|
+
}
|
|
1078
1093
|
lastFailure = { statusCode: res.statusCode, url, at: Date.now() };
|
|
1079
1094
|
} else if (res.statusCode < 400) {
|
|
1080
1095
|
lastFailure = null;
|
|
1096
|
+
lastLoggedDispatchStatus = null;
|
|
1081
1097
|
}
|
|
1082
1098
|
await res.body.text().catch(() => {
|
|
1083
1099
|
});
|
|
@@ -1090,6 +1106,8 @@ async function flushDispatch() {
|
|
|
1090
1106
|
function initAnalytics(url, tok) {
|
|
1091
1107
|
apiUrl = url;
|
|
1092
1108
|
token = tok;
|
|
1109
|
+
lastLoggedConnectStatus = null;
|
|
1110
|
+
lastLoggedDispatchStatus = null;
|
|
1093
1111
|
flushTimer = setInterval(() => {
|
|
1094
1112
|
flush().catch(() => {
|
|
1095
1113
|
});
|
|
@@ -1435,7 +1453,7 @@ function pathFor(client, scope, os, base) {
|
|
|
1435
1453
|
throw new Error(`Unhandled client: ${client}`);
|
|
1436
1454
|
}
|
|
1437
1455
|
function buildLaunchEntry(opts) {
|
|
1438
|
-
const pkg = opts.pkg ?? "@yawlabs/mcph";
|
|
1456
|
+
const pkg = opts.pkg ?? "@yawlabs/mcph@latest";
|
|
1439
1457
|
const entry = opts.os === "windows" ? { command: "cmd", args: ["/c", "npx", "-y", pkg] } : { command: "npx", args: ["-y", pkg] };
|
|
1440
1458
|
if (opts.token) entry.env = { MCPH_TOKEN: opts.token };
|
|
1441
1459
|
return entry;
|
|
@@ -1650,7 +1668,7 @@ function selectFlakyNamespaces(entries, limit) {
|
|
|
1650
1668
|
}
|
|
1651
1669
|
|
|
1652
1670
|
// src/doctor-cmd.ts
|
|
1653
|
-
var VERSION = true ? "0.
|
|
1671
|
+
var VERSION = true ? "0.48.6" : "dev";
|
|
1654
1672
|
async function runDoctor(opts = {}) {
|
|
1655
1673
|
if (opts.json) return runDoctorJson(opts);
|
|
1656
1674
|
const lines = [];
|
|
@@ -2905,6 +2923,259 @@ import {
|
|
|
2905
2923
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
2906
2924
|
import { request as request9 } from "undici";
|
|
2907
2925
|
|
|
2926
|
+
// src/auto-upgrade.ts
|
|
2927
|
+
import { spawn as spawn3 } from "child_process";
|
|
2928
|
+
|
|
2929
|
+
// src/upgrade-cmd.ts
|
|
2930
|
+
import { spawn as spawn2 } from "child_process";
|
|
2931
|
+
var UPGRADE_USAGE = `Usage: mcph upgrade [--run] [--json]
|
|
2932
|
+
|
|
2933
|
+
Show (or execute) the command to upgrade @yawlabs/mcph to the latest version.
|
|
2934
|
+
|
|
2935
|
+
--run If this install is global (npm install -g), spawn the upgrade
|
|
2936
|
+
command. No-op for npx installs \u2014 they always fetch the latest.
|
|
2937
|
+
--json Emit a machine-readable snapshot ({ current, latest, stale,
|
|
2938
|
+
method, command }) instead of prose.`;
|
|
2939
|
+
function parseUpgradeArgs(argv) {
|
|
2940
|
+
const opts = {};
|
|
2941
|
+
for (const a of argv) {
|
|
2942
|
+
if (a === "--run") opts.run = true;
|
|
2943
|
+
else if (a === "--json") opts.json = true;
|
|
2944
|
+
else if (a === "--help" || a === "-h") return { ok: false, error: UPGRADE_USAGE };
|
|
2945
|
+
else return { ok: false, error: `mcph upgrade: unknown argument "${a}"
|
|
2946
|
+
|
|
2947
|
+
${UPGRADE_USAGE}` };
|
|
2948
|
+
}
|
|
2949
|
+
return { ok: true, options: opts };
|
|
2950
|
+
}
|
|
2951
|
+
function detectInstallMethod(argvPath) {
|
|
2952
|
+
if (!argvPath) return "unknown";
|
|
2953
|
+
const normalized = argvPath.replace(/\\/g, "/");
|
|
2954
|
+
if (/\/_npx\//.test(normalized)) return "npx";
|
|
2955
|
+
if (/\/npm\/node_modules\/@yawlabs\/mcph\//.test(normalized)) return "global-npm";
|
|
2956
|
+
if (/\/lib\/node_modules\/@yawlabs\/mcph\//.test(normalized)) return "global-npm";
|
|
2957
|
+
if (/\/AppData\/Roaming\/npm\/node_modules\/@yawlabs\/mcph\//.test(normalized)) return "global-npm";
|
|
2958
|
+
if (/\/node_modules\/@yawlabs\/mcph\//.test(normalized)) return "local-node-modules";
|
|
2959
|
+
if (/\/mcph\/(dist|src)\//.test(normalized)) return "dev-checkout";
|
|
2960
|
+
return "unknown";
|
|
2961
|
+
}
|
|
2962
|
+
function buildUpgradePlan(input) {
|
|
2963
|
+
const { current, latest, method } = input;
|
|
2964
|
+
const stale = latest !== null && current !== "dev" && compareSemverLocal(current, latest) < 0;
|
|
2965
|
+
let command;
|
|
2966
|
+
switch (method) {
|
|
2967
|
+
case "global-npm":
|
|
2968
|
+
command = "npm install -g @yawlabs/mcph@latest";
|
|
2969
|
+
break;
|
|
2970
|
+
case "npx":
|
|
2971
|
+
command = null;
|
|
2972
|
+
break;
|
|
2973
|
+
case "local-node-modules":
|
|
2974
|
+
command = "npm install @yawlabs/mcph@latest";
|
|
2975
|
+
break;
|
|
2976
|
+
case "dev-checkout":
|
|
2977
|
+
command = "git pull && npm run build";
|
|
2978
|
+
break;
|
|
2979
|
+
default:
|
|
2980
|
+
command = "npm install -g @yawlabs/mcph@latest";
|
|
2981
|
+
break;
|
|
2982
|
+
}
|
|
2983
|
+
return { current, latest, stale, method, command };
|
|
2984
|
+
}
|
|
2985
|
+
function compareSemverLocal(a, b) {
|
|
2986
|
+
const parse = (s) => {
|
|
2987
|
+
const m = /^v?(\d+)\.(\d+)\.(\d+)/.exec(s);
|
|
2988
|
+
if (!m) return null;
|
|
2989
|
+
return [Number(m[1]), Number(m[2]), Number(m[3])];
|
|
2990
|
+
};
|
|
2991
|
+
const pa = parse(a);
|
|
2992
|
+
const pb = parse(b);
|
|
2993
|
+
if (!pa || !pb) return 0;
|
|
2994
|
+
for (let i = 0; i < 3; i++) {
|
|
2995
|
+
if (pa[i] < pb[i]) return -1;
|
|
2996
|
+
if (pa[i] > pb[i]) return 1;
|
|
2997
|
+
}
|
|
2998
|
+
return 0;
|
|
2999
|
+
}
|
|
3000
|
+
async function defaultFetchLatest() {
|
|
3001
|
+
const ac = new AbortController();
|
|
3002
|
+
const timer = setTimeout(() => ac.abort(), 3e3);
|
|
3003
|
+
try {
|
|
3004
|
+
const res = await fetch("https://registry.npmjs.org/@yawlabs/mcph/latest", {
|
|
3005
|
+
signal: ac.signal,
|
|
3006
|
+
headers: { accept: "application/json" }
|
|
3007
|
+
});
|
|
3008
|
+
if (!res.ok) return null;
|
|
3009
|
+
const body = await res.json();
|
|
3010
|
+
return typeof body.version === "string" ? body.version : null;
|
|
3011
|
+
} catch {
|
|
3012
|
+
return null;
|
|
3013
|
+
} finally {
|
|
3014
|
+
clearTimeout(timer);
|
|
3015
|
+
}
|
|
3016
|
+
}
|
|
3017
|
+
async function defaultSpawn(cmd, args) {
|
|
3018
|
+
return new Promise((resolve4) => {
|
|
3019
|
+
const child = spawn2(cmd, args, { stdio: "inherit", shell: process.platform === "win32" });
|
|
3020
|
+
child.on("close", (code) => resolve4(typeof code === "number" ? code : 1));
|
|
3021
|
+
child.on("error", () => resolve4(1));
|
|
3022
|
+
});
|
|
3023
|
+
}
|
|
3024
|
+
async function runUpgrade(opts = {}) {
|
|
3025
|
+
const write = opts.out ?? ((s) => process.stdout.write(s));
|
|
3026
|
+
const writeErr = opts.err ?? ((s) => process.stderr.write(s));
|
|
3027
|
+
const lines = [];
|
|
3028
|
+
const print = (s = "") => {
|
|
3029
|
+
lines.push(s);
|
|
3030
|
+
write(`${s}
|
|
3031
|
+
`);
|
|
3032
|
+
};
|
|
3033
|
+
const printErr = (s) => {
|
|
3034
|
+
lines.push(s);
|
|
3035
|
+
writeErr(`${s}
|
|
3036
|
+
`);
|
|
3037
|
+
};
|
|
3038
|
+
const fetcher = opts.fetchLatest ?? defaultFetchLatest;
|
|
3039
|
+
const current = opts.currentVersion ?? readCurrentVersion();
|
|
3040
|
+
const argvPath = opts.argvPath ?? process.argv[1];
|
|
3041
|
+
const method = detectInstallMethod(argvPath);
|
|
3042
|
+
let latest;
|
|
3043
|
+
try {
|
|
3044
|
+
latest = await fetcher();
|
|
3045
|
+
} catch {
|
|
3046
|
+
latest = null;
|
|
3047
|
+
}
|
|
3048
|
+
const plan = buildUpgradePlan({ current, latest, method });
|
|
3049
|
+
if (opts.json) {
|
|
3050
|
+
print(JSON.stringify(plan, null, 2));
|
|
3051
|
+
return { exitCode: plan.stale && !opts.run ? 1 : 0, lines };
|
|
3052
|
+
}
|
|
3053
|
+
if (latest === null) {
|
|
3054
|
+
print("mcph upgrade: couldn't reach the npm registry (offline? firewall?).");
|
|
3055
|
+
if (plan.command) {
|
|
3056
|
+
print(`When you're back online, run:
|
|
3057
|
+
${plan.command}`);
|
|
3058
|
+
} else {
|
|
3059
|
+
print("Your install uses `npx -y` \u2014 just restart the MCP client when you're back online.");
|
|
3060
|
+
}
|
|
3061
|
+
return { exitCode: 0, lines };
|
|
3062
|
+
}
|
|
3063
|
+
print(`Current: ${current}`);
|
|
3064
|
+
print(`Latest: ${latest}`);
|
|
3065
|
+
print(`Install: ${method}`);
|
|
3066
|
+
if (!plan.stale) {
|
|
3067
|
+
print("");
|
|
3068
|
+
print("\u2713 You're on the latest version \u2014 nothing to do.");
|
|
3069
|
+
return { exitCode: 0, lines };
|
|
3070
|
+
}
|
|
3071
|
+
print("");
|
|
3072
|
+
if (method === "npx") {
|
|
3073
|
+
print("Your install uses `npx -y` \u2014 restart the MCP client and it will fetch the new version.");
|
|
3074
|
+
return { exitCode: 0, lines };
|
|
3075
|
+
}
|
|
3076
|
+
if (!plan.command) {
|
|
3077
|
+
print("No upgrade command available for this install method.");
|
|
3078
|
+
return { exitCode: 0, lines };
|
|
3079
|
+
}
|
|
3080
|
+
const autoRunnable = method === "global-npm";
|
|
3081
|
+
if (!opts.run) {
|
|
3082
|
+
if (autoRunnable) {
|
|
3083
|
+
print(`Run:
|
|
3084
|
+
${plan.command}
|
|
3085
|
+
|
|
3086
|
+
Or re-run with --run to upgrade in place.`);
|
|
3087
|
+
} else {
|
|
3088
|
+
print(`Suggested command (run it yourself; --run only works for global-npm installs):
|
|
3089
|
+
${plan.command}`);
|
|
3090
|
+
}
|
|
3091
|
+
return { exitCode: 1, lines };
|
|
3092
|
+
}
|
|
3093
|
+
if (!autoRunnable) {
|
|
3094
|
+
printErr(
|
|
3095
|
+
`mcph upgrade --run: install method "${method}" can't be upgraded automatically. Run manually:
|
|
3096
|
+
${plan.command}`
|
|
3097
|
+
);
|
|
3098
|
+
return { exitCode: 2, lines };
|
|
3099
|
+
}
|
|
3100
|
+
const runner = opts.spawnImpl ?? defaultSpawn;
|
|
3101
|
+
print(`Running: ${plan.command}`);
|
|
3102
|
+
const code = await runner("npm", ["install", "-g", "@yawlabs/mcph@latest"]);
|
|
3103
|
+
if (code === 0) {
|
|
3104
|
+
print("");
|
|
3105
|
+
print(`\u2713 Upgraded @yawlabs/mcph to ${latest}.`);
|
|
3106
|
+
return { exitCode: 0, lines };
|
|
3107
|
+
}
|
|
3108
|
+
printErr(`mcph upgrade: npm exited ${code}. Try running the command manually.`);
|
|
3109
|
+
return { exitCode: 3, lines };
|
|
3110
|
+
}
|
|
3111
|
+
function readCurrentVersion() {
|
|
3112
|
+
return true ? "0.48.6" : "dev";
|
|
3113
|
+
}
|
|
3114
|
+
|
|
3115
|
+
// src/auto-upgrade.ts
|
|
3116
|
+
async function fetchLatestVersion2() {
|
|
3117
|
+
const ac = new AbortController();
|
|
3118
|
+
const timer = setTimeout(() => ac.abort(), 3e3);
|
|
3119
|
+
try {
|
|
3120
|
+
const res = await fetch("https://registry.npmjs.org/@yawlabs/mcph/latest", {
|
|
3121
|
+
signal: ac.signal,
|
|
3122
|
+
headers: { accept: "application/json" }
|
|
3123
|
+
});
|
|
3124
|
+
if (!res.ok) return null;
|
|
3125
|
+
const body = await res.json();
|
|
3126
|
+
return typeof body.version === "string" ? body.version : null;
|
|
3127
|
+
} catch {
|
|
3128
|
+
return null;
|
|
3129
|
+
} finally {
|
|
3130
|
+
clearTimeout(timer);
|
|
3131
|
+
}
|
|
3132
|
+
}
|
|
3133
|
+
function defaultSpawn2(cmd, args) {
|
|
3134
|
+
const child = spawn3(cmd, args, {
|
|
3135
|
+
stdio: "ignore",
|
|
3136
|
+
// Stay a child of this process (not detached) so it dies with mcph
|
|
3137
|
+
// if mcph exits mid-install -- a half-finished `npm i -g` is fine
|
|
3138
|
+
// (npm is atomic per package) and a re-run next startup completes it.
|
|
3139
|
+
detached: false,
|
|
3140
|
+
shell: process.platform === "win32"
|
|
3141
|
+
});
|
|
3142
|
+
child.on("close", (code) => {
|
|
3143
|
+
if (code === 0) {
|
|
3144
|
+
log("info", "mcph self-upgrade complete; the next client restart will run the new version");
|
|
3145
|
+
} else {
|
|
3146
|
+
log(
|
|
3147
|
+
"warn",
|
|
3148
|
+
"mcph self-upgrade: npm exited non-zero (often EACCES on a sudo-installed global). Run `npm install -g @yawlabs/mcph@latest` with the right permissions, or set MCPH_AUTO_UPGRADE=0 to silence this.",
|
|
3149
|
+
{ code }
|
|
3150
|
+
);
|
|
3151
|
+
}
|
|
3152
|
+
});
|
|
3153
|
+
child.on("error", (err) => {
|
|
3154
|
+
log("warn", "mcph self-upgrade: npm spawn failed", { error: err?.message });
|
|
3155
|
+
});
|
|
3156
|
+
}
|
|
3157
|
+
async function maybeAutoUpgrade(deps = {}) {
|
|
3158
|
+
const optOut = process.env.MCPH_AUTO_UPGRADE;
|
|
3159
|
+
if (optOut === "0" || optOut?.toLowerCase() === "false") return;
|
|
3160
|
+
const current = deps.currentVersion ?? (true ? "0.48.6" : "dev");
|
|
3161
|
+
if (current === "dev") return;
|
|
3162
|
+
const method = detectInstallMethod(deps.argvPath ?? process.argv[1]);
|
|
3163
|
+
const latest = await (deps.fetchLatestImpl ?? fetchLatestVersion2)();
|
|
3164
|
+
if (latest === null) return;
|
|
3165
|
+
const plan = buildUpgradePlan({ current, latest, method });
|
|
3166
|
+
if (!plan.stale) return;
|
|
3167
|
+
if (method === "global-npm") {
|
|
3168
|
+
log("info", "mcph is out of date; upgrading the global install in the background", { current, latest });
|
|
3169
|
+
(deps.spawnImpl ?? defaultSpawn2)("npm", ["install", "-g", "@yawlabs/mcph@latest"]);
|
|
3170
|
+
return;
|
|
3171
|
+
}
|
|
3172
|
+
log("info", "mcph is out of date; restart your MCP client to pick up the latest version", {
|
|
3173
|
+
current,
|
|
3174
|
+
latest,
|
|
3175
|
+
method
|
|
3176
|
+
});
|
|
3177
|
+
}
|
|
3178
|
+
|
|
2908
3179
|
// src/compliance.ts
|
|
2909
3180
|
var GRADE_ORDER = {
|
|
2910
3181
|
A: 4,
|
|
@@ -3341,9 +3612,13 @@ import { request as request5 } from "undici";
|
|
|
3341
3612
|
var HEARTBEAT_PATH = "/api/connect/heartbeat";
|
|
3342
3613
|
var apiUrl3 = "";
|
|
3343
3614
|
var token3 = "";
|
|
3615
|
+
var lastLoggedFailureStatus = null;
|
|
3616
|
+
var lastLoggedErrorMessage = null;
|
|
3344
3617
|
function initHeartbeat(url, tok) {
|
|
3345
3618
|
apiUrl3 = url;
|
|
3346
3619
|
token3 = tok;
|
|
3620
|
+
lastLoggedFailureStatus = null;
|
|
3621
|
+
lastLoggedErrorMessage = null;
|
|
3347
3622
|
}
|
|
3348
3623
|
async function reportHeartbeat(clientName, clientVersion, isRefresh = false) {
|
|
3349
3624
|
if (!apiUrl3 || !token3) return;
|
|
@@ -3368,15 +3643,26 @@ async function reportHeartbeat(clientName, clientVersion, isRefresh = false) {
|
|
|
3368
3643
|
await res.body.text().catch(() => {
|
|
3369
3644
|
});
|
|
3370
3645
|
if (res.statusCode >= 400 && res.statusCode !== 404) {
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3646
|
+
if (lastLoggedFailureStatus !== res.statusCode) {
|
|
3647
|
+
log("warn", "Heartbeat failed", { status: res.statusCode, isRefresh });
|
|
3648
|
+
lastLoggedFailureStatus = res.statusCode;
|
|
3649
|
+
}
|
|
3650
|
+
} else {
|
|
3651
|
+
lastLoggedFailureStatus = null;
|
|
3652
|
+
lastLoggedErrorMessage = null;
|
|
3653
|
+
if (!isRefresh) {
|
|
3654
|
+
log("info", "Reported AI client connect to mcp.hosting", {
|
|
3655
|
+
clientName: clientName ?? null,
|
|
3656
|
+
clientVersion: clientVersion ?? null
|
|
3657
|
+
});
|
|
3658
|
+
}
|
|
3377
3659
|
}
|
|
3378
3660
|
} catch (err) {
|
|
3379
|
-
|
|
3661
|
+
const errMsg = err?.message ?? "unknown error";
|
|
3662
|
+
if (lastLoggedErrorMessage !== errMsg) {
|
|
3663
|
+
log("warn", "Heartbeat error", { error: errMsg, isRefresh });
|
|
3664
|
+
lastLoggedErrorMessage = errMsg;
|
|
3665
|
+
}
|
|
3380
3666
|
}
|
|
3381
3667
|
}
|
|
3382
3668
|
|
|
@@ -4500,7 +4786,7 @@ async function rerank(intent, candidateIds, limit) {
|
|
|
4500
4786
|
}
|
|
4501
4787
|
|
|
4502
4788
|
// src/runtime-detect.ts
|
|
4503
|
-
import { spawn as
|
|
4789
|
+
import { spawn as spawn4 } from "child_process";
|
|
4504
4790
|
import { request as request7 } from "undici";
|
|
4505
4791
|
var PROBE_TIMEOUT_MS = 3e3;
|
|
4506
4792
|
var RUNTIME_REPORT_PATH = "/api/connect/runtimes";
|
|
@@ -4558,7 +4844,7 @@ async function probe(name, p) {
|
|
|
4558
4844
|
let stderr = "";
|
|
4559
4845
|
let child;
|
|
4560
4846
|
try {
|
|
4561
|
-
child =
|
|
4847
|
+
child = spawn4(p.bin, p.args, {
|
|
4562
4848
|
stdio: ["ignore", "pipe", "pipe"],
|
|
4563
4849
|
// Windows needs a shell for PATH lookup of .cmd/.bat shims —
|
|
4564
4850
|
// node/npx/uvx arrive as `npx.cmd` in PATH, and native spawn
|
|
@@ -4783,7 +5069,7 @@ import {
|
|
|
4783
5069
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
4784
5070
|
|
|
4785
5071
|
// src/uv-bootstrap.ts
|
|
4786
|
-
import { spawn as
|
|
5072
|
+
import { spawn as spawn5 } from "child_process";
|
|
4787
5073
|
import { createHash } from "crypto";
|
|
4788
5074
|
import { createWriteStream } from "fs";
|
|
4789
5075
|
import fs from "fs/promises";
|
|
@@ -4836,7 +5122,7 @@ async function onPath(cmd) {
|
|
|
4836
5122
|
};
|
|
4837
5123
|
let child;
|
|
4838
5124
|
try {
|
|
4839
|
-
child =
|
|
5125
|
+
child = spawn5(cmd, ["--version"], {
|
|
4840
5126
|
stdio: "ignore",
|
|
4841
5127
|
shell: false,
|
|
4842
5128
|
windowsHide: process.platform === "win32"
|
|
@@ -4897,7 +5183,7 @@ async function extractArchive(archivePath, destDir) {
|
|
|
4897
5183
|
}
|
|
4898
5184
|
function runCommand(cmd, args) {
|
|
4899
5185
|
return new Promise((resolve4, reject) => {
|
|
4900
|
-
const child =
|
|
5186
|
+
const child = spawn5(cmd, args, {
|
|
4901
5187
|
stdio: ["ignore", "pipe", "pipe"],
|
|
4902
5188
|
shell: false,
|
|
4903
5189
|
windowsHide: process.platform === "win32"
|
|
@@ -5015,7 +5301,7 @@ function categorizeSpawnError(err) {
|
|
|
5015
5301
|
}
|
|
5016
5302
|
async function connectToUpstream(config, onDisconnect, onListChanged) {
|
|
5017
5303
|
const client = new Client(
|
|
5018
|
-
{ name: "mcph", version: true ? "0.
|
|
5304
|
+
{ name: "mcph", version: true ? "0.48.6" : "dev" },
|
|
5019
5305
|
{ capabilities: {} }
|
|
5020
5306
|
);
|
|
5021
5307
|
let transport;
|
|
@@ -5323,7 +5609,7 @@ var ConnectServer = class _ConnectServer {
|
|
|
5323
5609
|
this.apiUrl = apiUrl6;
|
|
5324
5610
|
this.token = token6;
|
|
5325
5611
|
this.server = new Server(
|
|
5326
|
-
{ name: "mcph", version: true ? "0.
|
|
5612
|
+
{ name: "mcph", version: true ? "0.48.6" : "dev" },
|
|
5327
5613
|
{
|
|
5328
5614
|
capabilities: {
|
|
5329
5615
|
tools: { listChanged: true },
|
|
@@ -5609,6 +5895,7 @@ var ConnectServer = class _ConnectServer {
|
|
|
5609
5895
|
await this.server.connect(transport);
|
|
5610
5896
|
this.startPolling();
|
|
5611
5897
|
this.prewarmDormantServers().catch((err) => log("warn", "Pre-warm failed", { error: err?.message }));
|
|
5898
|
+
maybeAutoUpgrade().catch((err) => log("warn", "Auto-upgrade check failed", { error: err?.message }));
|
|
5612
5899
|
if (isAutoLoadEnabled() && this.persistenceReady) {
|
|
5613
5900
|
this.autoLoadRecurringPack().catch((err) => log("warn", "Auto-load failed", { error: err?.message }));
|
|
5614
5901
|
}
|
|
@@ -5634,15 +5921,33 @@ var ConnectServer = class _ConnectServer {
|
|
|
5634
5921
|
});
|
|
5635
5922
|
if (candidates.length === 0) return;
|
|
5636
5923
|
const top = candidates[0];
|
|
5924
|
+
const loaded = [];
|
|
5925
|
+
const refused = [];
|
|
5637
5926
|
for (const namespace of top.namespaces) {
|
|
5638
|
-
|
|
5639
|
-
|
|
5640
|
-
|
|
5927
|
+
try {
|
|
5928
|
+
const result = await this.activateOne(namespace);
|
|
5929
|
+
if (result.ok) {
|
|
5930
|
+
loaded.push(namespace);
|
|
5931
|
+
} else {
|
|
5932
|
+
refused.push({ namespace, message: result.message });
|
|
5933
|
+
}
|
|
5934
|
+
} catch (err) {
|
|
5935
|
+
refused.push({ namespace, message: err?.message ?? "unknown error" });
|
|
5936
|
+
}
|
|
5641
5937
|
}
|
|
5642
5938
|
log("info", "Auto-loaded recurring pack", {
|
|
5643
|
-
|
|
5939
|
+
loaded,
|
|
5940
|
+
refusedCount: refused.length,
|
|
5644
5941
|
frequency: top.frequency
|
|
5645
5942
|
});
|
|
5943
|
+
if (refused.length > 0) {
|
|
5944
|
+
const message = loaded.length === 0 ? "Auto-load could not activate any namespace in the pack" : "Auto-load could not activate every namespace in the pack";
|
|
5945
|
+
log("warn", message, {
|
|
5946
|
+
serverCap: this.serverCap,
|
|
5947
|
+
loadedCount: loaded.length,
|
|
5948
|
+
refused
|
|
5949
|
+
});
|
|
5950
|
+
}
|
|
5646
5951
|
}
|
|
5647
5952
|
// Populate toolCache for any isActive-but-never-activated server so
|
|
5648
5953
|
// Claude's tools/list shows the full toggled set on first run.
|
|
@@ -7522,192 +7827,6 @@ function truncateVersion(v) {
|
|
|
7522
7827
|
return v.length > 8 ? v.slice(0, 8) : v;
|
|
7523
7828
|
}
|
|
7524
7829
|
|
|
7525
|
-
// src/upgrade-cmd.ts
|
|
7526
|
-
import { spawn as spawn4 } from "child_process";
|
|
7527
|
-
var UPGRADE_USAGE = `Usage: mcph upgrade [--run] [--json]
|
|
7528
|
-
|
|
7529
|
-
Show (or execute) the command to upgrade @yawlabs/mcph to the latest version.
|
|
7530
|
-
|
|
7531
|
-
--run If this install is global (npm install -g), spawn the upgrade
|
|
7532
|
-
command. No-op for npx installs \u2014 they always fetch the latest.
|
|
7533
|
-
--json Emit a machine-readable snapshot ({ current, latest, stale,
|
|
7534
|
-
method, command }) instead of prose.`;
|
|
7535
|
-
function parseUpgradeArgs(argv) {
|
|
7536
|
-
const opts = {};
|
|
7537
|
-
for (const a of argv) {
|
|
7538
|
-
if (a === "--run") opts.run = true;
|
|
7539
|
-
else if (a === "--json") opts.json = true;
|
|
7540
|
-
else if (a === "--help" || a === "-h") return { ok: false, error: UPGRADE_USAGE };
|
|
7541
|
-
else return { ok: false, error: `mcph upgrade: unknown argument "${a}"
|
|
7542
|
-
|
|
7543
|
-
${UPGRADE_USAGE}` };
|
|
7544
|
-
}
|
|
7545
|
-
return { ok: true, options: opts };
|
|
7546
|
-
}
|
|
7547
|
-
function detectInstallMethod(argvPath) {
|
|
7548
|
-
if (!argvPath) return "unknown";
|
|
7549
|
-
const normalized = argvPath.replace(/\\/g, "/");
|
|
7550
|
-
if (/\/_npx\//.test(normalized)) return "npx";
|
|
7551
|
-
if (/\/npm\/node_modules\/@yawlabs\/mcph\//.test(normalized)) return "global-npm";
|
|
7552
|
-
if (/\/lib\/node_modules\/@yawlabs\/mcph\//.test(normalized)) return "global-npm";
|
|
7553
|
-
if (/\/AppData\/Roaming\/npm\/node_modules\/@yawlabs\/mcph\//.test(normalized)) return "global-npm";
|
|
7554
|
-
if (/\/node_modules\/@yawlabs\/mcph\//.test(normalized)) return "local-node-modules";
|
|
7555
|
-
if (/\/mcph\/(dist|src)\//.test(normalized)) return "dev-checkout";
|
|
7556
|
-
return "unknown";
|
|
7557
|
-
}
|
|
7558
|
-
function buildUpgradePlan(input) {
|
|
7559
|
-
const { current, latest, method } = input;
|
|
7560
|
-
const stale = latest !== null && current !== "dev" && compareSemverLocal(current, latest) < 0;
|
|
7561
|
-
let command;
|
|
7562
|
-
switch (method) {
|
|
7563
|
-
case "global-npm":
|
|
7564
|
-
command = "npm install -g @yawlabs/mcph@latest";
|
|
7565
|
-
break;
|
|
7566
|
-
case "npx":
|
|
7567
|
-
command = null;
|
|
7568
|
-
break;
|
|
7569
|
-
case "local-node-modules":
|
|
7570
|
-
command = "npm install @yawlabs/mcph@latest";
|
|
7571
|
-
break;
|
|
7572
|
-
case "dev-checkout":
|
|
7573
|
-
command = "git pull && npm run build";
|
|
7574
|
-
break;
|
|
7575
|
-
default:
|
|
7576
|
-
command = "npm install -g @yawlabs/mcph@latest";
|
|
7577
|
-
break;
|
|
7578
|
-
}
|
|
7579
|
-
return { current, latest, stale, method, command };
|
|
7580
|
-
}
|
|
7581
|
-
function compareSemverLocal(a, b) {
|
|
7582
|
-
const parse = (s) => {
|
|
7583
|
-
const m = /^v?(\d+)\.(\d+)\.(\d+)/.exec(s);
|
|
7584
|
-
if (!m) return null;
|
|
7585
|
-
return [Number(m[1]), Number(m[2]), Number(m[3])];
|
|
7586
|
-
};
|
|
7587
|
-
const pa = parse(a);
|
|
7588
|
-
const pb = parse(b);
|
|
7589
|
-
if (!pa || !pb) return 0;
|
|
7590
|
-
for (let i = 0; i < 3; i++) {
|
|
7591
|
-
if (pa[i] < pb[i]) return -1;
|
|
7592
|
-
if (pa[i] > pb[i]) return 1;
|
|
7593
|
-
}
|
|
7594
|
-
return 0;
|
|
7595
|
-
}
|
|
7596
|
-
async function defaultFetchLatest() {
|
|
7597
|
-
const ac = new AbortController();
|
|
7598
|
-
const timer = setTimeout(() => ac.abort(), 3e3);
|
|
7599
|
-
try {
|
|
7600
|
-
const res = await fetch("https://registry.npmjs.org/@yawlabs/mcph/latest", {
|
|
7601
|
-
signal: ac.signal,
|
|
7602
|
-
headers: { accept: "application/json" }
|
|
7603
|
-
});
|
|
7604
|
-
if (!res.ok) return null;
|
|
7605
|
-
const body = await res.json();
|
|
7606
|
-
return typeof body.version === "string" ? body.version : null;
|
|
7607
|
-
} catch {
|
|
7608
|
-
return null;
|
|
7609
|
-
} finally {
|
|
7610
|
-
clearTimeout(timer);
|
|
7611
|
-
}
|
|
7612
|
-
}
|
|
7613
|
-
async function defaultSpawn(cmd, args) {
|
|
7614
|
-
return new Promise((resolve4) => {
|
|
7615
|
-
const child = spawn4(cmd, args, { stdio: "inherit", shell: process.platform === "win32" });
|
|
7616
|
-
child.on("close", (code) => resolve4(typeof code === "number" ? code : 1));
|
|
7617
|
-
child.on("error", () => resolve4(1));
|
|
7618
|
-
});
|
|
7619
|
-
}
|
|
7620
|
-
async function runUpgrade(opts = {}) {
|
|
7621
|
-
const write = opts.out ?? ((s) => process.stdout.write(s));
|
|
7622
|
-
const writeErr = opts.err ?? ((s) => process.stderr.write(s));
|
|
7623
|
-
const lines = [];
|
|
7624
|
-
const print = (s = "") => {
|
|
7625
|
-
lines.push(s);
|
|
7626
|
-
write(`${s}
|
|
7627
|
-
`);
|
|
7628
|
-
};
|
|
7629
|
-
const printErr = (s) => {
|
|
7630
|
-
lines.push(s);
|
|
7631
|
-
writeErr(`${s}
|
|
7632
|
-
`);
|
|
7633
|
-
};
|
|
7634
|
-
const fetcher = opts.fetchLatest ?? defaultFetchLatest;
|
|
7635
|
-
const current = opts.currentVersion ?? readCurrentVersion();
|
|
7636
|
-
const argvPath = opts.argvPath ?? process.argv[1];
|
|
7637
|
-
const method = detectInstallMethod(argvPath);
|
|
7638
|
-
let latest;
|
|
7639
|
-
try {
|
|
7640
|
-
latest = await fetcher();
|
|
7641
|
-
} catch {
|
|
7642
|
-
latest = null;
|
|
7643
|
-
}
|
|
7644
|
-
const plan = buildUpgradePlan({ current, latest, method });
|
|
7645
|
-
if (opts.json) {
|
|
7646
|
-
print(JSON.stringify(plan, null, 2));
|
|
7647
|
-
return { exitCode: plan.stale && !opts.run ? 1 : 0, lines };
|
|
7648
|
-
}
|
|
7649
|
-
if (latest === null) {
|
|
7650
|
-
print("mcph upgrade: couldn't reach the npm registry (offline? firewall?).");
|
|
7651
|
-
if (plan.command) {
|
|
7652
|
-
print(`When you're back online, run:
|
|
7653
|
-
${plan.command}`);
|
|
7654
|
-
} else {
|
|
7655
|
-
print("Your install uses `npx -y` \u2014 just restart the MCP client when you're back online.");
|
|
7656
|
-
}
|
|
7657
|
-
return { exitCode: 0, lines };
|
|
7658
|
-
}
|
|
7659
|
-
print(`Current: ${current}`);
|
|
7660
|
-
print(`Latest: ${latest}`);
|
|
7661
|
-
print(`Install: ${method}`);
|
|
7662
|
-
if (!plan.stale) {
|
|
7663
|
-
print("");
|
|
7664
|
-
print("\u2713 You're on the latest version \u2014 nothing to do.");
|
|
7665
|
-
return { exitCode: 0, lines };
|
|
7666
|
-
}
|
|
7667
|
-
print("");
|
|
7668
|
-
if (method === "npx") {
|
|
7669
|
-
print("Your install uses `npx -y` \u2014 restart the MCP client and it will fetch the new version.");
|
|
7670
|
-
return { exitCode: 0, lines };
|
|
7671
|
-
}
|
|
7672
|
-
if (!plan.command) {
|
|
7673
|
-
print("No upgrade command available for this install method.");
|
|
7674
|
-
return { exitCode: 0, lines };
|
|
7675
|
-
}
|
|
7676
|
-
const autoRunnable = method === "global-npm";
|
|
7677
|
-
if (!opts.run) {
|
|
7678
|
-
if (autoRunnable) {
|
|
7679
|
-
print(`Run:
|
|
7680
|
-
${plan.command}
|
|
7681
|
-
|
|
7682
|
-
Or re-run with --run to upgrade in place.`);
|
|
7683
|
-
} else {
|
|
7684
|
-
print(`Suggested command (run it yourself; --run only works for global-npm installs):
|
|
7685
|
-
${plan.command}`);
|
|
7686
|
-
}
|
|
7687
|
-
return { exitCode: 1, lines };
|
|
7688
|
-
}
|
|
7689
|
-
if (!autoRunnable) {
|
|
7690
|
-
printErr(
|
|
7691
|
-
`mcph upgrade --run: install method "${method}" can't be upgraded automatically. Run manually:
|
|
7692
|
-
${plan.command}`
|
|
7693
|
-
);
|
|
7694
|
-
return { exitCode: 2, lines };
|
|
7695
|
-
}
|
|
7696
|
-
const runner = opts.spawnImpl ?? defaultSpawn;
|
|
7697
|
-
print(`Running: ${plan.command}`);
|
|
7698
|
-
const code = await runner("npm", ["install", "-g", "@yawlabs/mcph@latest"]);
|
|
7699
|
-
if (code === 0) {
|
|
7700
|
-
print("");
|
|
7701
|
-
print(`\u2713 Upgraded @yawlabs/mcph to ${latest}.`);
|
|
7702
|
-
return { exitCode: 0, lines };
|
|
7703
|
-
}
|
|
7704
|
-
printErr(`mcph upgrade: npm exited ${code}. Try running the command manually.`);
|
|
7705
|
-
return { exitCode: 3, lines };
|
|
7706
|
-
}
|
|
7707
|
-
function readCurrentVersion() {
|
|
7708
|
-
return true ? "0.47.7" : "dev";
|
|
7709
|
-
}
|
|
7710
|
-
|
|
7711
7830
|
// src/index.ts
|
|
7712
7831
|
var KNOWN_SUBCOMMANDS = [
|
|
7713
7832
|
"compliance",
|
|
@@ -7850,10 +7969,16 @@ if (subcommand === "compliance") {
|
|
|
7850
7969
|
MCPH_POLL_INTERVAL Dashboard polling interval, seconds (default 60).
|
|
7851
7970
|
MCPH_SERVER_CAP Max concurrently active servers (default 6).
|
|
7852
7971
|
MCPH_MIN_COMPLIANCE Minimum grade to auto-activate (A|B|C|D|F).
|
|
7853
|
-
MCPH_AUTO_LOAD
|
|
7972
|
+
MCPH_AUTO_LOAD Auto-activate the namespaces of the highest-
|
|
7973
|
+
ranked recurring pack at startup, subject to
|
|
7974
|
+
SERVER_CAP (default: off).
|
|
7854
7975
|
MCPH_AUTO_ACTIVATE Set to \`0\` to disable discover's auto-activate
|
|
7855
7976
|
gate (default: a clearly-winning server is
|
|
7856
7977
|
activated in the same call).
|
|
7978
|
+
MCPH_AUTO_UPGRADE Set to \`0\` to disable the background
|
|
7979
|
+
self-upgrade check at \`mcph serve\` startup
|
|
7980
|
+
(default: stale global-npm installs are
|
|
7981
|
+
upgraded in the background).
|
|
7857
7982
|
MCPH_PRUNE_RESPONSES Set to \`0\` to disable response pruning.
|
|
7858
7983
|
MCPH_DISABLE_PERSISTENCE Disable cross-session learning state.
|
|
7859
7984
|
|
|
@@ -7873,7 +7998,7 @@ if (subcommand === "compliance") {
|
|
|
7873
7998
|
`);
|
|
7874
7999
|
process.exit(0);
|
|
7875
8000
|
} else if (subcommand === "--version" || subcommand === "-V") {
|
|
7876
|
-
process.stdout.write(`mcph ${true ? "0.
|
|
8001
|
+
process.stdout.write(`mcph ${true ? "0.48.6" : "dev"}
|
|
7877
8002
|
`);
|
|
7878
8003
|
process.exit(0);
|
|
7879
8004
|
} else if (subcommand && !subcommand.startsWith("-")) {
|
|
@@ -7890,7 +8015,7 @@ async function runServer() {
|
|
|
7890
8015
|
const config = await loadMcphConfig();
|
|
7891
8016
|
if (!config.token) {
|
|
7892
8017
|
process.stderr.write(
|
|
7893
|
-
'\n mcph: no token resolved.\n\n Quick start (recommended):\n mcph install <claude-code|claude-desktop|cursor|vscode> --token mcp_pat_\u2026\n Creates ~/.mcph/config.json so every MCP client picks up the token automatically.\n\n Or set MCPH_TOKEN in your MCP client config:\n\n {\n "mcpServers": {\n "mcp.hosting": {\n "command": "npx",\n "args": ["-y", "@yawlabs/mcph"],\n "env": {\n "MCPH_TOKEN": "mcp_pat_your_token_here"\n }\n }\n }\n }\n\n Get a token at https://mcp.hosting \u2192 Settings \u2192 API Tokens, or run\n `mcph doctor` to see exactly where mcph looked.\n\n'
|
|
8018
|
+
'\n mcph: no token resolved.\n\n Quick start (recommended):\n mcph install <claude-code|claude-desktop|cursor|vscode> --token mcp_pat_\u2026\n Creates ~/.mcph/config.json so every MCP client picks up the token automatically.\n\n Or set MCPH_TOKEN in your MCP client config:\n\n {\n "mcpServers": {\n "mcp.hosting": {\n "command": "npx",\n "args": ["-y", "@yawlabs/mcph@latest"],\n "env": {\n "MCPH_TOKEN": "mcp_pat_your_token_here"\n }\n }\n }\n }\n\n Get a token at https://mcp.hosting \u2192 Settings \u2192 API Tokens, or run\n `mcph doctor` to see exactly where mcph looked.\n\n'
|
|
7894
8019
|
);
|
|
7895
8020
|
process.exit(1);
|
|
7896
8021
|
}
|
package/package.json
CHANGED