@yawlabs/mcph 0.42.0 → 0.44.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 +9 -0
- package/README.md +41 -2
- package/dist/index.js +244 -12
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,15 @@
|
|
|
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.44.0 — 2026-04-18
|
|
6
|
+
|
|
7
|
+
- **`mcph install --list` + `mcph install --all`** — Two new modes on the install subcommand. `--list` is read-only: it enumerates every client/scope combo for the current OS and shows whether an `mcp.hosting` entry is already wired up, plus a path-per-row and a one-line summary (`N/M client scopes have mcp.hosting configured on linux`). No token, no network, no writes — just a diagnostic view that mirrors the `doctor` CLIENTS section but without the rest of doctor's noise. `--all` walks `INSTALL_TARGETS`, picks the default scope per client (user where supported, else the first non-project-dir scope, else skipped unless `--project-dir` is passed), and calls `runInstall` in a loop — so `--dry-run`, `--force`, `--skip`, and `--token` all propagate as expected. Status is aggregated into a single summary line, and the process exit code is non-zero if any sub-install failed so CI can still gate on one-shot onboarding. Works around the main drop-off during setup ("which client am I supposed to pick?") by offering both the answer (`--list`) and the sledgehammer (`--all`) from the same subcommand.
|
|
8
|
+
|
|
9
|
+
## 0.43.0 — 2026-04-18
|
|
10
|
+
|
|
11
|
+
- **`mcph servers <namespace-filter>` — positional filter** — Passing a bare positional argument now filters the listing to servers whose namespace contains that substring (case-insensitive): `mcph servers git` matches both `github` and `gitlab`. Applies to both the text table and the `--json` output so the two surfaces agree. Summary line reflects the filtered count, and a filter that matches nothing prints an explanatory "No servers match …" instead of an empty table (which previously looked like an empty account).
|
|
12
|
+
- **README catch-up — `CLI reference` block + `doctor --json` documented** — The README was missing the subcommands that landed in v0.38.0 onward (`servers`, `bundles`, `reset-learning`, `completion`) and hadn't been updated to mention doctor's `--json` mode. New compact "Other CLI subcommands" block lists every user-facing command with a one-line purpose, documents the `--json` pattern as the pipeline interface across doctor/servers/bundles, and includes copy-paste install snippets for bash/zsh/fish/powershell completions. The doctor paragraph now lists the actual section coverage (env overrides, persisted state, reliability rollup, shell-shadow hits, upgrade check) so first-time readers know what they get.
|
|
13
|
+
|
|
5
14
|
## 0.42.0 — 2026-04-18
|
|
6
15
|
|
|
7
16
|
- **`mcph completion <shell>` — shell completion scripts** — Prints a completion script for `bash`, `zsh`, `fish`, or `powershell` to stdout so users can one-line it into their completions directory. Each script covers every known subcommand (install, doctor, servers, bundles, compliance, reset-learning, completion) with positional choices (install clients, bundles actions, completion shells) and per-subcommand flags (`--json`, `--scope`, `--token`, `--force`, etc.). Every template derives from a single `SUBCOMMAND_SPEC` table so adding a new subcommand elsewhere updates all four shells at once — no drift between what the CLI accepts and what it completes. Install hints are inlined as comments at the top of each generated script: the bash file drops into `~/.local/share/bash-completion/completions/mcph`, zsh into any `$fpath` dir as `_mcph`, fish into `~/.config/fish/completions/mcph.fish`, pwsh appended to `$PROFILE`.
|
package/README.md
CHANGED
|
@@ -65,15 +65,54 @@ Helpful flags:
|
|
|
65
65
|
- `--force` / `--skip` — overwrite or leave an existing `mcp.hosting` entry. Without either, mcph prompts (TTY) or refuses (non-TTY).
|
|
66
66
|
- `--no-mcph-config` — write only the client config; leave `~/.mcph/config.json` untouched.
|
|
67
67
|
|
|
68
|
+
Or install into every detected client at once:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
mcph install --list # read-only: detect clients + show install state per scope
|
|
72
|
+
mcph install --all --token mcp_pat_… # one-shot: install into every user-scope client on this machine
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`--list` never writes (no token needed). `--all` installs into every client whose user-scope target is resolvable on this OS — Claude Desktop is skipped on Linux, VS Code is skipped unless `--project-dir` is given (it's workspace-only). Aggregate exit code is non-zero if any sub-install fails.
|
|
76
|
+
|
|
68
77
|
Or [edit the JSON by hand](#manual-install) if you'd rather.
|
|
69
78
|
|
|
70
79
|
### Diagnose problems — `mcph doctor`
|
|
71
80
|
|
|
72
81
|
```bash
|
|
73
|
-
npx -y @yawlabs/mcph doctor
|
|
82
|
+
npx -y @yawlabs/mcph doctor # human-readable report
|
|
83
|
+
npx -y @yawlabs/mcph doctor --json # machine-readable snapshot for pipelines
|
|
74
84
|
```
|
|
75
85
|
|
|
76
|
-
Prints the loaded config files, your token's source + fingerprint (last 4 chars), the API base URL,
|
|
86
|
+
Prints the loaded config files, your token's source + fingerprint (last 4 chars), the API base URL, installed clients, env overrides, persisted learning state, flaky-namespace reliability rollup, shell-history "shadow" hits (CLIs you run that an MCP server could replace), and an upgrade check against the npm registry. Exits `0` healthy / `1` no token / `2` warnings (e.g. world-readable token file). Paste the text output into a support ticket; the `--json` blob is the same data as a structured snapshot, so dashboards and CI scripts can `jq` instead of parsing the text layout.
|
|
87
|
+
|
|
88
|
+
### Other CLI subcommands
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
mcph servers [<namespace-filter>] [--json] # list servers; optional substring filter on namespace
|
|
92
|
+
mcph bundles [list|match] [--json] # browse curated multi-server bundles (PR review, DevOps incident, etc.)
|
|
93
|
+
mcph reset-learning # clear cross-session learning history (~/.mcph/state.json)
|
|
94
|
+
mcph completion <bash|zsh|fish|powershell> # print shell completion script
|
|
95
|
+
mcph compliance <target> [--publish] # run the compliance suite against an MCP server
|
|
96
|
+
mcph --version # print version
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Every CLI that reads state has a `--json` mode for pipeline use. `mcph servers` hits the backend; `mcph bundles list` and `mcph completion` are fully static (no network, no token). `mcph bundles match` partitions the curated set against your enabled servers so you see the same ready-to-activate vs. partially-installed view the LLM-facing `mcp_connect_bundles` meta-tool produces.
|
|
100
|
+
|
|
101
|
+
To wire up shell completion:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
# bash
|
|
105
|
+
mcph completion bash > ~/.local/share/bash-completion/completions/mcph
|
|
106
|
+
|
|
107
|
+
# zsh (must be on $fpath, then rebuild compinit)
|
|
108
|
+
mcph completion zsh > "${fpath[1]}/_mcph"
|
|
109
|
+
|
|
110
|
+
# fish
|
|
111
|
+
mcph completion fish > ~/.config/fish/completions/mcph.fish
|
|
112
|
+
|
|
113
|
+
# powershell
|
|
114
|
+
mcph completion powershell >> $PROFILE
|
|
115
|
+
```
|
|
77
116
|
|
|
78
117
|
### Getting your token
|
|
79
118
|
|
package/dist/index.js
CHANGED
|
@@ -663,7 +663,18 @@ var SUBCOMMAND_SPEC = [
|
|
|
663
663
|
{
|
|
664
664
|
name: "install",
|
|
665
665
|
positional: [...INSTALL_CLIENTS],
|
|
666
|
-
flags: [
|
|
666
|
+
flags: [
|
|
667
|
+
"--scope",
|
|
668
|
+
"--token",
|
|
669
|
+
"--project-dir",
|
|
670
|
+
"--os",
|
|
671
|
+
"--force",
|
|
672
|
+
"--skip",
|
|
673
|
+
"--dry-run",
|
|
674
|
+
"--no-mcph-config",
|
|
675
|
+
"--list",
|
|
676
|
+
"--all"
|
|
677
|
+
]
|
|
667
678
|
},
|
|
668
679
|
{ name: "doctor", flags: ["--json", "--help"] },
|
|
669
680
|
{ name: "servers", flags: ["--json", "--help"] },
|
|
@@ -1435,7 +1446,7 @@ function selectFlakyNamespaces(entries, limit) {
|
|
|
1435
1446
|
}
|
|
1436
1447
|
|
|
1437
1448
|
// src/doctor-cmd.ts
|
|
1438
|
-
var VERSION = true ? "0.
|
|
1449
|
+
var VERSION = true ? "0.44.0" : "dev";
|
|
1439
1450
|
async function runDoctor(opts = {}) {
|
|
1440
1451
|
if (opts.json) return runDoctorJson(opts);
|
|
1441
1452
|
const lines = [];
|
|
@@ -1769,6 +1780,62 @@ function walkContainer(root, path4) {
|
|
|
1769
1780
|
if (typeof cur !== "object" || cur === null || Array.isArray(cur)) return null;
|
|
1770
1781
|
return cur;
|
|
1771
1782
|
}
|
|
1783
|
+
async function probeClientsAsync(opts) {
|
|
1784
|
+
const result = [];
|
|
1785
|
+
for (const target of INSTALL_TARGETS) {
|
|
1786
|
+
const unavailable = !target.availableOn.includes(opts.os);
|
|
1787
|
+
if (unavailable) {
|
|
1788
|
+
result.push({
|
|
1789
|
+
clientId: target.clientId,
|
|
1790
|
+
scope: target.scopes[0].scope,
|
|
1791
|
+
path: "(n/a)",
|
|
1792
|
+
exists: false,
|
|
1793
|
+
hasMcphEntry: false,
|
|
1794
|
+
malformed: false,
|
|
1795
|
+
unavailable: true
|
|
1796
|
+
});
|
|
1797
|
+
continue;
|
|
1798
|
+
}
|
|
1799
|
+
for (const scope of target.scopes) {
|
|
1800
|
+
const resolved = resolveInstallPath({
|
|
1801
|
+
clientId: target.clientId,
|
|
1802
|
+
scope: scope.scope,
|
|
1803
|
+
os: opts.os,
|
|
1804
|
+
home: opts.home,
|
|
1805
|
+
projectDir: scope.requiresProjectDir ? opts.cwd : void 0
|
|
1806
|
+
});
|
|
1807
|
+
const exists3 = existsSync(resolved.absolute);
|
|
1808
|
+
let hasMcphEntry = false;
|
|
1809
|
+
let malformed = false;
|
|
1810
|
+
if (exists3) {
|
|
1811
|
+
try {
|
|
1812
|
+
const raw = await readFile3(resolved.absolute, "utf8");
|
|
1813
|
+
if (raw.trim().length > 0) {
|
|
1814
|
+
const parsed = parseJsonc(raw);
|
|
1815
|
+
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
1816
|
+
const container = walkContainer(parsed, resolved.containerPath);
|
|
1817
|
+
if (container) hasMcphEntry = ENTRY_NAME in container;
|
|
1818
|
+
} else {
|
|
1819
|
+
malformed = true;
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1822
|
+
} catch {
|
|
1823
|
+
malformed = true;
|
|
1824
|
+
}
|
|
1825
|
+
}
|
|
1826
|
+
result.push({
|
|
1827
|
+
clientId: target.clientId,
|
|
1828
|
+
scope: scope.scope,
|
|
1829
|
+
path: resolved.absolute,
|
|
1830
|
+
exists: exists3,
|
|
1831
|
+
hasMcphEntry,
|
|
1832
|
+
malformed,
|
|
1833
|
+
unavailable: false
|
|
1834
|
+
});
|
|
1835
|
+
}
|
|
1836
|
+
}
|
|
1837
|
+
return result;
|
|
1838
|
+
}
|
|
1772
1839
|
async function fetchLatestVersion(override) {
|
|
1773
1840
|
if (override) {
|
|
1774
1841
|
try {
|
|
@@ -1950,7 +2017,7 @@ import { homedir as homedir5 } from "os";
|
|
|
1950
2017
|
import { dirname as dirname2 } from "path";
|
|
1951
2018
|
import { join as join5, resolve as resolve2 } from "path";
|
|
1952
2019
|
import { createInterface } from "readline/promises";
|
|
1953
|
-
var USAGE = "Usage: mcph install <claude-code|claude-desktop|cursor|vscode> [--scope user|project|local]\n [--token <mcp_pat_\u2026>] [--project-dir <path>] [--os macos|linux|windows]\n [--force | --skip] [--dry-run] [--no-mcph-config]";
|
|
2020
|
+
var USAGE = "Usage: mcph install <claude-code|claude-desktop|cursor|vscode> [--scope user|project|local]\n [--token <mcp_pat_\u2026>] [--project-dir <path>] [--os macos|linux|windows]\n [--force | --skip] [--dry-run] [--no-mcph-config]\n mcph install --list (detect clients; no writes)\n mcph install --all [--token <mcp_pat_\u2026>] (install into every detected client)";
|
|
1954
2021
|
async function runInstall(opts) {
|
|
1955
2022
|
const stdout = opts.io?.stdout ?? process.stdout;
|
|
1956
2023
|
const stderr = opts.io?.stderr ?? process.stderr;
|
|
@@ -1965,10 +2032,21 @@ async function runInstall(opts) {
|
|
|
1965
2032
|
stderr.write(`${s}
|
|
1966
2033
|
`);
|
|
1967
2034
|
};
|
|
2035
|
+
if (opts.listOnly && opts.all) {
|
|
2036
|
+
err("mcph install: --list and --all are mutually exclusive");
|
|
2037
|
+
return { written: [], wouldWrite: [], messages, exitCode: 2 };
|
|
2038
|
+
}
|
|
2039
|
+
if (opts.listOnly) return runInstallList(opts, log2);
|
|
2040
|
+
if (opts.all) return runInstallAll(opts, log2, err);
|
|
1968
2041
|
if (opts.force && opts.skip) {
|
|
1969
2042
|
err("mcph install: --force and --skip are mutually exclusive");
|
|
1970
2043
|
return { written: [], wouldWrite: [], messages, exitCode: 2 };
|
|
1971
2044
|
}
|
|
2045
|
+
if (!opts.clientId) {
|
|
2046
|
+
err(`mcph install: client argument required
|
|
2047
|
+
${USAGE}`);
|
|
2048
|
+
return { written: [], wouldWrite: [], messages, exitCode: 2 };
|
|
2049
|
+
}
|
|
1972
2050
|
const target = INSTALL_TARGETS.find((t) => t.clientId === opts.clientId);
|
|
1973
2051
|
if (!target) {
|
|
1974
2052
|
err(`mcph install: unknown client ${opts.clientId}
|
|
@@ -2294,6 +2372,12 @@ function parseInstallArgs(argv) {
|
|
|
2294
2372
|
case "--no-mcph-config":
|
|
2295
2373
|
opts.skipMcphConfig = true;
|
|
2296
2374
|
break;
|
|
2375
|
+
case "--list":
|
|
2376
|
+
opts.listOnly = true;
|
|
2377
|
+
break;
|
|
2378
|
+
case "--all":
|
|
2379
|
+
opts.all = true;
|
|
2380
|
+
break;
|
|
2297
2381
|
case "-h":
|
|
2298
2382
|
case "--help":
|
|
2299
2383
|
return { ok: false, error: USAGE };
|
|
@@ -2303,6 +2387,16 @@ ${USAGE}` };
|
|
|
2303
2387
|
positional.push(a);
|
|
2304
2388
|
}
|
|
2305
2389
|
}
|
|
2390
|
+
if (opts.listOnly || opts.all) {
|
|
2391
|
+
if (positional.length > 0) {
|
|
2392
|
+
return {
|
|
2393
|
+
ok: false,
|
|
2394
|
+
error: `mcph install: ${opts.listOnly ? "--list" : "--all"} does not take a client argument.
|
|
2395
|
+
${USAGE}`
|
|
2396
|
+
};
|
|
2397
|
+
}
|
|
2398
|
+
return { ok: true, options: opts };
|
|
2399
|
+
}
|
|
2306
2400
|
if (positional.length !== 1)
|
|
2307
2401
|
return { ok: false, error: `Expected exactly one client argument, got ${positional.length}.
|
|
2308
2402
|
${USAGE}` };
|
|
@@ -2316,6 +2410,127 @@ ${USAGE}` };
|
|
|
2316
2410
|
opts.clientId = clientId;
|
|
2317
2411
|
return { ok: true, options: opts };
|
|
2318
2412
|
}
|
|
2413
|
+
async function runInstallList(opts, log2) {
|
|
2414
|
+
const home = opts.home ?? homedir5();
|
|
2415
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
2416
|
+
const os = opts.os ?? CURRENT_OS;
|
|
2417
|
+
const probes = await probeClientsAsync({ home, os, cwd });
|
|
2418
|
+
const rows = probes.map((p) => ({
|
|
2419
|
+
client: INSTALL_TARGETS.find((t) => t.clientId === p.clientId)?.label ?? p.clientId,
|
|
2420
|
+
scope: p.scope,
|
|
2421
|
+
path: displayPath(p.path, home),
|
|
2422
|
+
status: statusFor(p)
|
|
2423
|
+
}));
|
|
2424
|
+
const installed = probes.filter((p) => p.hasMcphEntry).length;
|
|
2425
|
+
const available = probes.filter((p) => !p.unavailable).length;
|
|
2426
|
+
log2(`${installed}/${available} client scopes have mcp.hosting configured on ${os}.`);
|
|
2427
|
+
log2("");
|
|
2428
|
+
const widths = {
|
|
2429
|
+
client: Math.max("CLIENT".length, ...rows.map((r) => r.client.length)),
|
|
2430
|
+
scope: Math.max("SCOPE".length, ...rows.map((r) => r.scope.length)),
|
|
2431
|
+
path: Math.max("PATH".length, ...rows.map((r) => r.path.length)),
|
|
2432
|
+
status: Math.max("STATUS".length, ...rows.map((r) => r.status.length))
|
|
2433
|
+
};
|
|
2434
|
+
const header = ` ${"CLIENT".padEnd(widths.client)} ${"SCOPE".padEnd(widths.scope)} ${"PATH".padEnd(widths.path)} ${"STATUS".padEnd(widths.status)}`;
|
|
2435
|
+
log2(header);
|
|
2436
|
+
for (const r of rows) {
|
|
2437
|
+
log2(
|
|
2438
|
+
` ${r.client.padEnd(widths.client)} ${r.scope.padEnd(widths.scope)} ${r.path.padEnd(widths.path)} ${r.status.padEnd(widths.status)}`
|
|
2439
|
+
);
|
|
2440
|
+
}
|
|
2441
|
+
log2("");
|
|
2442
|
+
log2("Install into a specific client: `mcph install <client> [--scope user|project|local]`");
|
|
2443
|
+
log2("Install into every available user-scope client: `mcph install --all`");
|
|
2444
|
+
return { written: [], wouldWrite: [], messages: [], exitCode: 0 };
|
|
2445
|
+
}
|
|
2446
|
+
function statusFor(p) {
|
|
2447
|
+
if (p.unavailable) return "unavailable";
|
|
2448
|
+
if (p.malformed) return "malformed";
|
|
2449
|
+
if (p.hasMcphEntry) return "installed";
|
|
2450
|
+
if (p.exists) return "other-entries";
|
|
2451
|
+
return "not installed";
|
|
2452
|
+
}
|
|
2453
|
+
function displayPath(abs, home) {
|
|
2454
|
+
if (abs === "(n/a)") return abs;
|
|
2455
|
+
if (home && abs.startsWith(home)) {
|
|
2456
|
+
const tail = abs.slice(home.length).replace(/^[\\/]/, "");
|
|
2457
|
+
return `~${process.platform === "win32" ? "\\" : "/"}${tail}`;
|
|
2458
|
+
}
|
|
2459
|
+
return abs;
|
|
2460
|
+
}
|
|
2461
|
+
async function runInstallAll(opts, log2, err) {
|
|
2462
|
+
const os = opts.os ?? CURRENT_OS;
|
|
2463
|
+
const targets = INSTALL_TARGETS.filter((t) => t.availableOn.includes(os));
|
|
2464
|
+
if (targets.length === 0) {
|
|
2465
|
+
err(`mcph install --all: no installable clients on ${os}.`);
|
|
2466
|
+
return { written: [], wouldWrite: [], messages: [], exitCode: 1 };
|
|
2467
|
+
}
|
|
2468
|
+
const plans = [];
|
|
2469
|
+
const skipped = [];
|
|
2470
|
+
for (const t of targets) {
|
|
2471
|
+
const userScope = t.scopes.find((s) => s.scope === "user");
|
|
2472
|
+
if (userScope) {
|
|
2473
|
+
plans.push({ clientId: t.clientId, scope: "user" });
|
|
2474
|
+
continue;
|
|
2475
|
+
}
|
|
2476
|
+
const firstNoProj = t.scopes.find((s) => !s.requiresProjectDir);
|
|
2477
|
+
if (firstNoProj) {
|
|
2478
|
+
plans.push({ clientId: t.clientId, scope: firstNoProj.scope });
|
|
2479
|
+
continue;
|
|
2480
|
+
}
|
|
2481
|
+
if (opts.projectDir) {
|
|
2482
|
+
plans.push({ clientId: t.clientId, scope: t.scopes[0].scope });
|
|
2483
|
+
continue;
|
|
2484
|
+
}
|
|
2485
|
+
skipped.push({
|
|
2486
|
+
clientId: t.clientId,
|
|
2487
|
+
reason: `requires --project-dir (scopes: ${t.scopes.map((s) => s.scope).join(", ")})`
|
|
2488
|
+
});
|
|
2489
|
+
}
|
|
2490
|
+
log2(`Installing into ${plans.length} client${plans.length === 1 ? "" : "s"}\u2026`);
|
|
2491
|
+
if (skipped.length > 0) {
|
|
2492
|
+
for (const s of skipped) log2(` skip ${s.clientId}: ${s.reason}`);
|
|
2493
|
+
}
|
|
2494
|
+
log2("");
|
|
2495
|
+
const aggregateWritten = [];
|
|
2496
|
+
const aggregateWouldWrite = [];
|
|
2497
|
+
const aggregateMessages = [];
|
|
2498
|
+
let failed = 0;
|
|
2499
|
+
let succeeded = 0;
|
|
2500
|
+
for (const plan of plans) {
|
|
2501
|
+
log2(`\u2500\u2500 ${plan.clientId} (${plan.scope}) \u2500\u2500`);
|
|
2502
|
+
const result = await runInstall({
|
|
2503
|
+
...opts,
|
|
2504
|
+
listOnly: false,
|
|
2505
|
+
all: false,
|
|
2506
|
+
clientId: plan.clientId,
|
|
2507
|
+
scope: plan.scope
|
|
2508
|
+
});
|
|
2509
|
+
aggregateWritten.push(...result.written);
|
|
2510
|
+
aggregateWouldWrite.push(...result.wouldWrite);
|
|
2511
|
+
aggregateMessages.push(...result.messages);
|
|
2512
|
+
if (result.exitCode === 0) succeeded += 1;
|
|
2513
|
+
else failed += 1;
|
|
2514
|
+
log2("");
|
|
2515
|
+
}
|
|
2516
|
+
const totalPlanned = plans.length;
|
|
2517
|
+
if (failed === 0) {
|
|
2518
|
+
log2(`\u2713 ${succeeded}/${totalPlanned} clients installed successfully.`);
|
|
2519
|
+
return {
|
|
2520
|
+
written: aggregateWritten,
|
|
2521
|
+
wouldWrite: aggregateWouldWrite,
|
|
2522
|
+
messages: aggregateMessages,
|
|
2523
|
+
exitCode: 0
|
|
2524
|
+
};
|
|
2525
|
+
}
|
|
2526
|
+
err(`${failed}/${totalPlanned} client install${failed === 1 ? "" : "s"} failed. ${succeeded} succeeded.`);
|
|
2527
|
+
return {
|
|
2528
|
+
written: aggregateWritten,
|
|
2529
|
+
wouldWrite: aggregateWouldWrite,
|
|
2530
|
+
messages: aggregateMessages,
|
|
2531
|
+
exitCode: 1
|
|
2532
|
+
};
|
|
2533
|
+
}
|
|
2319
2534
|
var INSTALL_USAGE = USAGE;
|
|
2320
2535
|
|
|
2321
2536
|
// src/reset-learning-cmd.ts
|
|
@@ -4519,7 +4734,7 @@ function categorizeSpawnError(err) {
|
|
|
4519
4734
|
}
|
|
4520
4735
|
async function connectToUpstream(config, onDisconnect, onListChanged) {
|
|
4521
4736
|
const client = new Client(
|
|
4522
|
-
{ name: "mcph", version: true ? "0.
|
|
4737
|
+
{ name: "mcph", version: true ? "0.44.0" : "dev" },
|
|
4523
4738
|
{ capabilities: {} }
|
|
4524
4739
|
);
|
|
4525
4740
|
let transport;
|
|
@@ -5000,7 +5215,7 @@ var ConnectServer = class _ConnectServer {
|
|
|
5000
5215
|
this.apiUrl = apiUrl6;
|
|
5001
5216
|
this.token = token6;
|
|
5002
5217
|
this.server = new Server(
|
|
5003
|
-
{ name: "mcph", version: true ? "0.
|
|
5218
|
+
{ name: "mcph", version: true ? "0.44.0" : "dev" },
|
|
5004
5219
|
{
|
|
5005
5220
|
capabilities: {
|
|
5006
5221
|
tools: { listChanged: true },
|
|
@@ -7055,24 +7270,33 @@ To load the top pack in one step, call \`mcp_connect_activate\` with namespaces=
|
|
|
7055
7270
|
// src/servers-cmd.ts
|
|
7056
7271
|
function parseServersArgs(argv) {
|
|
7057
7272
|
let json = false;
|
|
7273
|
+
let filter;
|
|
7058
7274
|
for (const a of argv) {
|
|
7059
7275
|
if (a === "--json") {
|
|
7060
7276
|
json = true;
|
|
7061
7277
|
} else if (a === "--help" || a === "-h") {
|
|
7062
7278
|
return { ok: false, error: SERVERS_USAGE };
|
|
7063
|
-
} else {
|
|
7279
|
+
} else if (a.startsWith("-")) {
|
|
7064
7280
|
return { ok: false, error: `mcph servers: unknown argument "${a}"
|
|
7065
7281
|
|
|
7282
|
+
${SERVERS_USAGE}` };
|
|
7283
|
+
} else if (filter === void 0) {
|
|
7284
|
+
filter = a;
|
|
7285
|
+
} else {
|
|
7286
|
+
return { ok: false, error: `mcph servers: unexpected extra argument "${a}"
|
|
7287
|
+
|
|
7066
7288
|
${SERVERS_USAGE}` };
|
|
7067
7289
|
}
|
|
7068
7290
|
}
|
|
7069
|
-
return { ok: true, options: { json } };
|
|
7291
|
+
return { ok: true, options: { json, ...filter !== void 0 ? { filter } : {} } };
|
|
7070
7292
|
}
|
|
7071
|
-
var SERVERS_USAGE = `Usage: mcph servers [--json]
|
|
7293
|
+
var SERVERS_USAGE = `Usage: mcph servers [<namespace-filter>] [--json]
|
|
7072
7294
|
|
|
7073
7295
|
List the servers configured in your mcp.hosting dashboard.
|
|
7074
7296
|
|
|
7075
|
-
|
|
7297
|
+
<namespace-filter> Case-insensitive substring filter on namespace (e.g.,
|
|
7298
|
+
\`mcph servers git\` matches github + gitlab).
|
|
7299
|
+
--json Emit machine-readable JSON instead of a table.`;
|
|
7076
7300
|
async function runServersCommand(opts = {}) {
|
|
7077
7301
|
const write = opts.out ?? ((s) => process.stdout.write(s));
|
|
7078
7302
|
const writeErr = opts.err ?? ((s) => process.stderr.write(s));
|
|
@@ -7109,11 +7333,19 @@ async function runServersCommand(opts = {}) {
|
|
|
7109
7333
|
printErr("mcph servers: backend returned no data (unexpected 304).");
|
|
7110
7334
|
return { exitCode: 2, lines };
|
|
7111
7335
|
}
|
|
7336
|
+
const filtered = opts.filter ? {
|
|
7337
|
+
...backend,
|
|
7338
|
+
servers: backend.servers.filter((s) => s.namespace.toLowerCase().includes(opts.filter.toLowerCase()))
|
|
7339
|
+
} : backend;
|
|
7112
7340
|
if (opts.json) {
|
|
7113
|
-
print(JSON.stringify(
|
|
7341
|
+
print(JSON.stringify(filtered, null, 2));
|
|
7342
|
+
return { exitCode: 0, lines };
|
|
7343
|
+
}
|
|
7344
|
+
if (opts.filter && filtered.servers.length === 0) {
|
|
7345
|
+
print(`No servers match "${opts.filter}". Run \`mcph servers\` to see the full list.`);
|
|
7114
7346
|
return { exitCode: 0, lines };
|
|
7115
7347
|
}
|
|
7116
|
-
renderTable(
|
|
7348
|
+
renderTable(filtered, print);
|
|
7117
7349
|
return { exitCode: 0, lines };
|
|
7118
7350
|
}
|
|
7119
7351
|
function renderTable(cfg, print) {
|
|
@@ -7259,7 +7491,7 @@ ${installBlock}
|
|
|
7259
7491
|
);
|
|
7260
7492
|
process.exit(0);
|
|
7261
7493
|
} else if (subcommand === "--version" || subcommand === "-V") {
|
|
7262
|
-
process.stdout.write(`mcph ${true ? "0.
|
|
7494
|
+
process.stdout.write(`mcph ${true ? "0.44.0" : "dev"}
|
|
7263
7495
|
`);
|
|
7264
7496
|
process.exit(0);
|
|
7265
7497
|
} else if (subcommand && !subcommand.startsWith("-")) {
|
package/package.json
CHANGED