@yawlabs/mcph 0.27.0 → 0.28.1
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 +81 -0
- package/README.md +13 -3
- package/dist/index.js +185 -5
- package/package.json +3 -2
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
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
|
+
|
|
5
|
+
## 0.28.1 — 2026-04-18
|
|
6
|
+
|
|
7
|
+
Docs-only release.
|
|
8
|
+
|
|
9
|
+
- First-ever `CHANGELOG.md`, covering 0.5.0 → 0.28.0. Linked from `README.md`.
|
|
10
|
+
- README catches up with the meta-tools shipped in the 0.20 – 0.28 arc: `mcp_connect_read_tool`, `mcp_connect_exec`, `mcp_connect_bundles` are now documented in the top-level list. Corrected "session-local" phrasing on the Learning ranker signal (cross-session since v0.23.0).
|
|
11
|
+
- New "Multi-device sync" section under "Config sync" — same token, same servers across every machine; no dotfile repos for secrets.
|
|
12
|
+
- Phase 2 "Multi-device config sync" marked shipped in `ROADMAP.md` (docs-only; backing behavior already worked).
|
|
13
|
+
- `package.json` `files` array now includes `CHANGELOG.md` so release notes ship with the npm tarball.
|
|
14
|
+
|
|
15
|
+
## 0.28.0 — 2026-04-18
|
|
16
|
+
|
|
17
|
+
Phase 3 opener. Two client-only intelligence features.
|
|
18
|
+
|
|
19
|
+
- **Tool deduplication** — `mcp_connect_discover` now surfaces an "Overlapping tools" block when two or more currently-connected servers expose the same bare tool name. Top 5 overlaps, sorted by namespace count descending, with a dispatch-to-disambiguate hint.
|
|
20
|
+
- **Curated bundles (`mcp_connect_bundles`)** — New meta-tool returning hand-picked multi-server presets: `devops-incident`, `pr-review`, `growth-stack`, `data-ops`, `product-release`, `support-ops`. `action: "list"` (default) returns all bundles; `action: "match"` partitions them into "ready to activate now" vs. "partially installed" against the user's current config.
|
|
21
|
+
|
|
22
|
+
## 0.27.0 — 2026-04-18
|
|
23
|
+
|
|
24
|
+
Four Phase 2 items shipped together.
|
|
25
|
+
|
|
26
|
+
- **Automatic load (`MCPH_AUTO_LOAD`)** — Opt-in env flag. On startup, after persistence hydration, activates every namespace in the top recurring pack (by frequency, tie-break recency) from pack history, provided every namespace is installed. Silent no-op otherwise.
|
|
27
|
+
- **Per-tool filter on `mcp_connect_activate`** — Pass `tools: [...]` to expose only the named tools via `tools/list`. Hidden tools stay reachable through `mcp_connect_dispatch` (routes are unfiltered). Re-activate without `tools` to clear the filter. `discover()` shows a `(filtered: K of N)` indicator on filtered connections.
|
|
28
|
+
- **Orchestration pipeline (`mcp_connect_exec`)** — Declarative multi-step tool-call pipeline. Each step names a namespaced tool plus args; `{"$ref": "<stepId>[.path]"}` markers in args splice a prior step's output into the next step's input. No eval / no expression language — only sequential dispatch and dot/bracket path resolution. Capped at 16 steps; any step failure fails the pipeline and returns completed outputs as `partial`.
|
|
29
|
+
- **Marketplace pointer** — `discover()` appends `https://mcp.hosting/explore` for users with fewer than 5 installed servers. URL hint only; a full marketplace meta-tool is parked until the backend ships a catalog API.
|
|
30
|
+
|
|
31
|
+
## 0.26.0 — 2026-04-18
|
|
32
|
+
|
|
33
|
+
- **Recurring packs block in `discover()`** — When pack history and installed config overlap, `discover()` now surfaces an "Recurring packs" block at the top of its output with a ready-to-run `mcp_connect_activate` call. Saves the second `mcp_connect_suggest` round-trip when the signal is already there.
|
|
34
|
+
|
|
35
|
+
## 0.25.1 — 2026-04-18
|
|
36
|
+
|
|
37
|
+
- Truthed up "this session" phrasing across user-facing strings and tool descriptions. With cross-session persistence (v0.23.0) shipping, counts and pack history are no longer session-scoped; the copy now matches.
|
|
38
|
+
|
|
39
|
+
## 0.25.0 — 2026-04-18
|
|
40
|
+
|
|
41
|
+
- `mcp_connect_suggest` now emits a ready-to-run `mcp_connect_activate` call with a verbatim `namespaces=[...]` JSON array, rather than pointing at `mcp_connect_dispatch` (the wrong primitive for loading a pack).
|
|
42
|
+
|
|
43
|
+
## 0.24.0 — 2026-04-18
|
|
44
|
+
|
|
45
|
+
- **`mcph doctor` STATE section** — Prints `~/.mcph/state.json` path, last-saved age, learning count, pack history count; shows "disabled" when persistence is opted out.
|
|
46
|
+
- **`MCPH_DISABLE_PERSISTENCE` opt-out** — Env flag skips both load and save. Useful for CI, sandboxed containers, or users who don't want a state file.
|
|
47
|
+
|
|
48
|
+
## 0.23.0 — 2026-04-18
|
|
49
|
+
|
|
50
|
+
- **Cross-session persistence** — Learning counts (`succeeded`/`dispatched`/`lastUsedAt` per namespace) and pack history (co-activation chains) now round-trip through `~/.mcph/state.json`. Schema-versioned, atomic write-rename.
|
|
51
|
+
|
|
52
|
+
## 0.22.0 — 2026-04-17
|
|
53
|
+
|
|
54
|
+
- **Inline usage hints in `discover()`** — `used Nx` success counts and "often loaded with X, Y" co-activation peers are surfaced per-server in discover output.
|
|
55
|
+
|
|
56
|
+
## 0.21.0 — 2026-04-17
|
|
57
|
+
|
|
58
|
+
- **Concurrent server cap** — Default max 6 simultaneously-active servers; `MCPH_SERVER_CAP` env override. Hard cap both as context protection and a business lever.
|
|
59
|
+
|
|
60
|
+
## 0.20.0 — 2026-04-17
|
|
61
|
+
|
|
62
|
+
- **`mcp_connect_read_tool`** — Schema-on-demand: return a single tool's schema + docs without activating its server. For servers with large tool catalogs where the model only needs 1–2 tools, reads 1–2 schemas instead of loading the entire catalog.
|
|
63
|
+
|
|
64
|
+
## 0.19.x and earlier
|
|
65
|
+
|
|
66
|
+
- v0.19.0 — internal refactor around config reconciliation.
|
|
67
|
+
- v0.18.0 — analytics uploads for tool-call patterns, load/unload events, error rates.
|
|
68
|
+
- v0.17.0 — resource + prompt proxying (beyond tools).
|
|
69
|
+
- v0.16.0 — error tracking surfaced in `discover()`.
|
|
70
|
+
- v0.15.x — `install` command gates success on config refresh; misc fixes.
|
|
71
|
+
- v0.14.0 — auto-allow mcph tools in Claude Code settings + discover dedup.
|
|
72
|
+
- v0.13.0 — deferred tools: advertise inactive-but-cached servers in `tools/list`.
|
|
73
|
+
- v0.12.x — legacy-config migrator + `doctor` freshness checks.
|
|
74
|
+
- v0.11.x — stability patches.
|
|
75
|
+
- v0.10.x — 7-feature bundle, adaptive routing, policy profiles.
|
|
76
|
+
- v0.9.0 — `mcph compliance` subcommand.
|
|
77
|
+
- v0.8.0 — runtime detection + test runner + error deep-links.
|
|
78
|
+
- v0.7.0 — two-stage retrieval: BM25 + semantic rerank.
|
|
79
|
+
- v0.6.0 — BM25 dispatch + auto-warm discover + stderr capture.
|
|
80
|
+
- v0.5.0 — `MCPH_POLL_INTERVAL` env var.
|
|
81
|
+
- v0.1.x – v0.4.x — initial public release, core meta-tools, namespace routing, config polling.
|
package/README.md
CHANGED
|
@@ -27,16 +27,19 @@ Your MCP client (Claude Code, Cursor, etc.)
|
|
|
27
27
|
- **`mcp_connect_install`** — install a new MCP server on your mcp.hosting account.
|
|
28
28
|
- **`mcp_connect_import`** — bulk-import servers from an existing client config (`claude_desktop_config.json`, `mcp.json`, etc.).
|
|
29
29
|
- **`mcp_connect_health`** — show call counts, error rates, and latency per loaded server.
|
|
30
|
-
- **`mcp_connect_suggest`** — surface recurring multi-server workflows mcph has
|
|
30
|
+
- **`mcp_connect_suggest`** — surface recurring multi-server workflows mcph has learned from persisted pack history. When you repeatedly use `gh` → `linear` → `slack` for the same kind of task, `suggest` lists the pattern with a ready-to-run `activate` call so you can load the whole pack at once.
|
|
31
|
+
- **`mcp_connect_read_tool`** — return a single tool's schema + docs without activating its server. Reads 1–2 schemas instead of loading a whole catalog when the model only needs a couple of tools from a big server.
|
|
32
|
+
- **`mcp_connect_exec`** — run a short declarative pipeline of tool calls in one round-trip. Steps name namespaced tools + args; `{"$ref": "<stepId>[.path]"}` markers splice prior outputs into later inputs. No eval — only dot/bracket path resolution. Capped at 16 steps.
|
|
33
|
+
- **`mcp_connect_bundles`** — list curated multi-server presets (DevOps incident, PR review, growth stack, data ops, etc.) and/or match them against your current config. Pair it with `mcp_connect_activate` to load a whole bundle at once.
|
|
31
34
|
|
|
32
35
|
Installing a server puts it on your account; loading it brings its tools into the current session's context. mcph loads servers lazily so your context window stays clean.
|
|
33
36
|
|
|
34
37
|
Ranking is two-stage when the backend has a Voyage embeddings key configured: a local BM25 pass narrows to a shortlist, then a `/api/connect/rerank` call semantically reorders. With no key on the backend it gracefully degrades to BM25-only — `dispatch` and `discover(context)` keep working, just with slightly weaker ranking on ambiguous queries.
|
|
35
38
|
|
|
36
|
-
On top of the ranker, mcph applies three
|
|
39
|
+
On top of the ranker, mcph applies three client-side signals to dispatch scores:
|
|
37
40
|
|
|
38
41
|
- **Health-aware**: servers that have recently failed to load or have high error rates get down-ranked. Never boosts above raw — "all else equal, prefer the one that works".
|
|
39
|
-
- **Learning**: servers that have succeeded
|
|
42
|
+
- **Learning**: servers that have succeeded before get a small (+10% max) nudge, so the router remembers what's been useful. Success counts persist across restarts via `~/.mcph/state.json` (opt out with `MCPH_DISABLE_PERSISTENCE=1`).
|
|
40
43
|
- **Sampling tiebreak**: when the top two candidates are within 10% of each other and your client supports [MCP sampling](https://modelcontextprotocol.io/specification/server/sampling), mcph asks your client's LLM to pick. Uses the model you're already running — no extra provider key, no extra cost to mcph.
|
|
41
44
|
|
|
42
45
|
## Install
|
|
@@ -264,6 +267,12 @@ When a load fails (missing token, runtime not on PATH, server crashes on init),
|
|
|
264
267
|
|
|
265
268
|
mcph polls [mcp.hosting](https://mcp.hosting) every 60 seconds for config changes. When you add, remove, or modify a server on the dashboard, mcph picks it up automatically — no restart needed.
|
|
266
269
|
|
|
270
|
+
### Multi-device sync
|
|
271
|
+
|
|
272
|
+
Because every mcph install reads the same account's server list, the same token gives you the same servers across every machine. Install mcph on a second laptop with the same `mcp_pat_...`, and within 60 seconds it sees the same GitHub/Slack/Stripe/etc. servers you configured from the first. Tokens, environment variables, and credentials stay in the dashboard — you don't have to sync a JSON file across machines, copy secrets into a dotfile repo, or re-paste an API key per device.
|
|
273
|
+
|
|
274
|
+
Rotate a credential in one place (the dashboard), every machine picks up the new value on the next poll. Revoke a token in Settings → API Tokens, every install stops working immediately (the token is the only thing authenticating the config pull). This is why `~/.mcph/config.json` holds a token, not a server list — the server list is the cloud's concern.
|
|
275
|
+
|
|
267
276
|
## Environment variables
|
|
268
277
|
|
|
269
278
|
| Variable | Required | Description |
|
|
@@ -301,4 +310,5 @@ The popular Python-based MCP servers (`fetch`, `sqlite`, `time`, `sentry`, etc.)
|
|
|
301
310
|
|
|
302
311
|
- [mcp.hosting](https://mcp.hosting) — Dashboard and server management
|
|
303
312
|
- [@yawlabs/mcp-compliance](https://www.npmjs.com/package/@yawlabs/mcp-compliance) — Test your MCP servers for spec compliance
|
|
313
|
+
- [CHANGELOG](./CHANGELOG.md) — Release notes
|
|
304
314
|
- [GitHub](https://github.com/YawLabs/mcph) — Source code and issues
|
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.28.1" : "dev";
|
|
950
950
|
async function runDoctor(opts = {}) {
|
|
951
951
|
const lines = [];
|
|
952
952
|
const write = opts.out ?? ((s) => process.stdout.write(s));
|
|
@@ -1761,6 +1761,70 @@ async function shutdownAnalytics() {
|
|
|
1761
1761
|
dispatchBuffer.length = 0;
|
|
1762
1762
|
}
|
|
1763
1763
|
|
|
1764
|
+
// src/bundles.ts
|
|
1765
|
+
var CURATED_BUNDLES = [
|
|
1766
|
+
{
|
|
1767
|
+
id: "devops-incident",
|
|
1768
|
+
name: "DevOps Incident Triage",
|
|
1769
|
+
description: "GitHub + PagerDuty + Slack for on-call triage",
|
|
1770
|
+
namespaces: ["github", "pagerduty", "slack"],
|
|
1771
|
+
category: "ops"
|
|
1772
|
+
},
|
|
1773
|
+
{
|
|
1774
|
+
id: "pr-review",
|
|
1775
|
+
name: "PR Review",
|
|
1776
|
+
description: "GitHub + Linear for issue-to-PR traceability",
|
|
1777
|
+
namespaces: ["github", "linear"],
|
|
1778
|
+
category: "dev"
|
|
1779
|
+
},
|
|
1780
|
+
{
|
|
1781
|
+
id: "growth-stack",
|
|
1782
|
+
name: "Growth Stack",
|
|
1783
|
+
description: "HubSpot + Slack + GA for lifecycle + funnel signals",
|
|
1784
|
+
namespaces: ["hubspot", "slack", "ga"],
|
|
1785
|
+
category: "growth"
|
|
1786
|
+
},
|
|
1787
|
+
{
|
|
1788
|
+
id: "data-ops",
|
|
1789
|
+
name: "Data Ops",
|
|
1790
|
+
description: "Postgres + S3 + Snowflake for pipeline debugging",
|
|
1791
|
+
namespaces: ["postgres", "s3", "snowflake"],
|
|
1792
|
+
category: "data"
|
|
1793
|
+
},
|
|
1794
|
+
{
|
|
1795
|
+
id: "product-release",
|
|
1796
|
+
name: "Product Release",
|
|
1797
|
+
description: "GitHub + Linear + Slack for ship-day coordination",
|
|
1798
|
+
namespaces: ["github", "linear", "slack"],
|
|
1799
|
+
category: "dev"
|
|
1800
|
+
},
|
|
1801
|
+
{
|
|
1802
|
+
id: "support-ops",
|
|
1803
|
+
name: "Support Ops",
|
|
1804
|
+
description: "Zendesk + Slack + HubSpot for escalation handoffs",
|
|
1805
|
+
namespaces: ["zendesk", "slack", "hubspot"],
|
|
1806
|
+
category: "ops"
|
|
1807
|
+
}
|
|
1808
|
+
];
|
|
1809
|
+
function matchBundles(installedNamespaces) {
|
|
1810
|
+
const installed = new Set(installedNamespaces);
|
|
1811
|
+
const ready = [];
|
|
1812
|
+
const partial = [];
|
|
1813
|
+
for (const bundle of CURATED_BUNDLES) {
|
|
1814
|
+
const have = bundle.namespaces.filter((ns) => installed.has(ns));
|
|
1815
|
+
const missing = bundle.namespaces.filter((ns) => !installed.has(ns));
|
|
1816
|
+
if (missing.length === 0) {
|
|
1817
|
+
ready.push(bundle);
|
|
1818
|
+
} else if (have.length > 0) {
|
|
1819
|
+
partial.push({ bundle, have, missing });
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1822
|
+
return { ready, partial };
|
|
1823
|
+
}
|
|
1824
|
+
function bundleActivateHint(bundle) {
|
|
1825
|
+
return `mcp_connect_activate({ namespaces: ${JSON.stringify(bundle.namespaces)} })`;
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1764
1828
|
// src/cost-estimate.ts
|
|
1765
1829
|
var BYTES_PER_TOKEN = 4;
|
|
1766
1830
|
var CACHED_TOOL_SCHEMA_PAD_BYTES = 200;
|
|
@@ -2461,6 +2525,27 @@ var META_TOOLS = {
|
|
|
2461
2525
|
openWorldHint: false
|
|
2462
2526
|
}
|
|
2463
2527
|
},
|
|
2528
|
+
bundles: {
|
|
2529
|
+
name: "mcp_connect_bundles",
|
|
2530
|
+
description: "List curated multi-server 'bundles' \u2014 presets like `pr-review` (github + linear) or `devops-incident` (github + pagerduty + slack) that commonly ship together. Use this BEFORE mcp_connect_discover when the user's intent maps to a known workflow (on-call triage, PR review, data pipeline debugging) \u2014 it returns a ready-to-run `mcp_connect_activate namespaces=[...]` call per bundle. With `action=\"match\"` (recommended after the user's installed list is known) the response partitions bundles into READY (every namespace already installed \u2014 activate now) and PARTIAL (some installed, some missing \u2014 shows the missing names and the mcp.hosting/explore install URL). With `action=\"list\"` (default) it returns the full curated catalog. Bundles are static client-side data, not a network call.",
|
|
2531
|
+
inputSchema: {
|
|
2532
|
+
type: "object",
|
|
2533
|
+
properties: {
|
|
2534
|
+
action: {
|
|
2535
|
+
type: "string",
|
|
2536
|
+
enum: ["list", "match"],
|
|
2537
|
+
description: 'Either "list" (return the full curated catalog; default) or "match" (partition bundles against installed servers into ready-to-activate vs partially-installed).'
|
|
2538
|
+
}
|
|
2539
|
+
}
|
|
2540
|
+
},
|
|
2541
|
+
annotations: {
|
|
2542
|
+
title: "Curated Server Bundles",
|
|
2543
|
+
readOnlyHint: true,
|
|
2544
|
+
destructiveHint: false,
|
|
2545
|
+
idempotentHint: true,
|
|
2546
|
+
openWorldHint: false
|
|
2547
|
+
}
|
|
2548
|
+
},
|
|
2464
2549
|
exec: {
|
|
2465
2550
|
name: "mcp_connect_exec",
|
|
2466
2551
|
description: "Run a short DECLARATIVE pipeline of upstream tool calls in a single round-trip. Use this when you already know the exact 2-4 tool calls to make and one call's output feeds another's args \u2014 e.g. `a = gh_list_prs(); b = gh_get_pr(a[0].number); return b`. NOT a code sandbox: there is no expression language, no loops, no branching, no arithmetic. The only control flow is sequential step execution; the only data-flow primitive is `{\"$ref\": \"<stepId>[.path.to.value]\"}` which substitutes a prior step's output (or a nested field of it) into the next step's args. Paths support dot keys and `[N]` / `.N` array indexing. Each step's `tool` must be a namespaced, already-loaded tool name (the exec does not auto-activate \u2014 call `mcp_connect_activate` first). Max 16 steps per exec. If any step fails, the whole pipeline fails and returns `{ ok: false, failedStep, error, partial: { ...completed outputs } }`. On success returns `{ ok: true, result: <return-step output>, steps: { ...all outputs } }`. Prefer this over back-to-back tool calls when the chain is deterministic \u2014 it saves prompt-token replay and client round-trips.",
|
|
@@ -2581,7 +2666,8 @@ var META_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
|
2581
2666
|
META_TOOLS.install.name,
|
|
2582
2667
|
META_TOOLS.read_tool.name,
|
|
2583
2668
|
META_TOOLS.suggest.name,
|
|
2584
|
-
META_TOOLS.exec.name
|
|
2669
|
+
META_TOOLS.exec.name,
|
|
2670
|
+
META_TOOLS.bundles.name
|
|
2585
2671
|
]);
|
|
2586
2672
|
|
|
2587
2673
|
// src/pack-detect.ts
|
|
@@ -3705,7 +3791,7 @@ function categorizeSpawnError(err) {
|
|
|
3705
3791
|
}
|
|
3706
3792
|
async function connectToUpstream(config, onDisconnect, onListChanged) {
|
|
3707
3793
|
const client = new Client(
|
|
3708
|
-
{ name: "mcph", version: true ? "0.
|
|
3794
|
+
{ name: "mcph", version: true ? "0.28.1" : "dev" },
|
|
3709
3795
|
{ capabilities: {} }
|
|
3710
3796
|
);
|
|
3711
3797
|
let transport;
|
|
@@ -4189,12 +4275,37 @@ function tokenizeForSummary(text) {
|
|
|
4189
4275
|
text.toLowerCase().split(/[^a-z0-9]+/).filter((w) => w.length >= 2)
|
|
4190
4276
|
);
|
|
4191
4277
|
}
|
|
4278
|
+
function computeToolOverlaps(connections) {
|
|
4279
|
+
const byName = /* @__PURE__ */ new Map();
|
|
4280
|
+
for (const conn of connections) {
|
|
4281
|
+
if (conn.status !== "connected") continue;
|
|
4282
|
+
const ns = conn.config.namespace;
|
|
4283
|
+
for (const tool of conn.tools) {
|
|
4284
|
+
let set = byName.get(tool.name);
|
|
4285
|
+
if (!set) {
|
|
4286
|
+
set = /* @__PURE__ */ new Set();
|
|
4287
|
+
byName.set(tool.name, set);
|
|
4288
|
+
}
|
|
4289
|
+
set.add(ns);
|
|
4290
|
+
}
|
|
4291
|
+
}
|
|
4292
|
+
const overlaps = [];
|
|
4293
|
+
for (const [bareName, nsSet] of byName) {
|
|
4294
|
+
if (nsSet.size < 2) continue;
|
|
4295
|
+
overlaps.push({ bareName, namespaces: [...nsSet].sort() });
|
|
4296
|
+
}
|
|
4297
|
+
overlaps.sort((a, b) => {
|
|
4298
|
+
if (b.namespaces.length !== a.namespaces.length) return b.namespaces.length - a.namespaces.length;
|
|
4299
|
+
return a.bareName.localeCompare(b.bareName);
|
|
4300
|
+
});
|
|
4301
|
+
return overlaps;
|
|
4302
|
+
}
|
|
4192
4303
|
var ConnectServer = class _ConnectServer {
|
|
4193
4304
|
constructor(apiUrl6, token6) {
|
|
4194
4305
|
this.apiUrl = apiUrl6;
|
|
4195
4306
|
this.token = token6;
|
|
4196
4307
|
this.server = new Server(
|
|
4197
|
-
{ name: "mcph", version: true ? "0.
|
|
4308
|
+
{ name: "mcph", version: true ? "0.28.1" : "dev" },
|
|
4198
4309
|
{
|
|
4199
4310
|
capabilities: {
|
|
4200
4311
|
tools: { listChanged: true },
|
|
@@ -4661,6 +4772,11 @@ var ConnectServer = class _ConnectServer {
|
|
|
4661
4772
|
});
|
|
4662
4773
|
return this.attachGuideNudge(result2);
|
|
4663
4774
|
}
|
|
4775
|
+
if (name === META_TOOLS.bundles.name) {
|
|
4776
|
+
const action = args.action === "match" ? "match" : "list";
|
|
4777
|
+
recordConnectEvent({ namespace: null, toolName: null, action: "bundles", latencyMs: null, success: true });
|
|
4778
|
+
return this.attachGuideNudge(this.handleBundles(action));
|
|
4779
|
+
}
|
|
4664
4780
|
let routes = this.toolRoutes;
|
|
4665
4781
|
let route = routes.get(name);
|
|
4666
4782
|
if (route?.deferred) {
|
|
@@ -5059,6 +5175,16 @@ var ConnectServer = class _ConnectServer {
|
|
|
5059
5175
|
}
|
|
5060
5176
|
}
|
|
5061
5177
|
}
|
|
5178
|
+
const overlaps = computeToolOverlaps(this.connections.values());
|
|
5179
|
+
if (overlaps.length > 0) {
|
|
5180
|
+
lines.push("\nOverlapping tools (same bare name in multiple servers):");
|
|
5181
|
+
const top = overlaps.slice(0, 5);
|
|
5182
|
+
for (let i = 0; i < top.length; i++) {
|
|
5183
|
+
const o = top[i];
|
|
5184
|
+
const suffix = i === 0 ? " (use mcp_connect_dispatch to disambiguate)" : "";
|
|
5185
|
+
lines.push(` ${o.bareName} \u2014 available in: ${o.namespaces.join(", ")}${suffix}`);
|
|
5186
|
+
}
|
|
5187
|
+
}
|
|
5062
5188
|
const inactive = this.config.servers.filter((s) => !s.isActive);
|
|
5063
5189
|
if (inactive.length > 0) {
|
|
5064
5190
|
lines.push("\nDisabled servers:");
|
|
@@ -5928,6 +6054,60 @@ Use mcp_connect_discover to see imported servers.`
|
|
|
5928
6054
|
To load the top pack in one step, call \`mcp_connect_activate\` with namespaces=${nsJson}.`);
|
|
5929
6055
|
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
5930
6056
|
}
|
|
6057
|
+
// Curated multi-server bundles. Static client-side data (see bundles.ts)
|
|
6058
|
+
// — no network call. `action=list` prints every bundle with a ready-to-
|
|
6059
|
+
// run `mcp_connect_activate` snippet; `action=match` cross-references
|
|
6060
|
+
// the installed server list and partitions into fully-ready vs
|
|
6061
|
+
// partially-installed so the caller only sees bundles that are actually
|
|
6062
|
+
// actionable on this account.
|
|
6063
|
+
handleBundles(action) {
|
|
6064
|
+
if (action === "list") {
|
|
6065
|
+
const lines2 = [`Curated server bundles (${CURATED_BUNDLES.length}):
|
|
6066
|
+
`];
|
|
6067
|
+
for (const bundle of CURATED_BUNDLES) {
|
|
6068
|
+
lines2.push(` ${bundle.id} \u2014 ${bundle.description}`);
|
|
6069
|
+
lines2.push(` namespaces: ${JSON.stringify(bundle.namespaces)}`);
|
|
6070
|
+
lines2.push(` activate: ${bundleActivateHint(bundle)}`);
|
|
6071
|
+
}
|
|
6072
|
+
lines2.push("");
|
|
6073
|
+
lines2.push(
|
|
6074
|
+
'Call mcp_connect_bundles with action="match" to filter these against servers already installed on this account.'
|
|
6075
|
+
);
|
|
6076
|
+
return { content: [{ type: "text", text: lines2.join("\n") }] };
|
|
6077
|
+
}
|
|
6078
|
+
const installedNamespaces = this.getProfiledActiveServers().map((s) => s.namespace);
|
|
6079
|
+
const { ready, partial } = matchBundles(installedNamespaces);
|
|
6080
|
+
if (ready.length === 0 && partial.length === 0) {
|
|
6081
|
+
return {
|
|
6082
|
+
content: [
|
|
6083
|
+
{
|
|
6084
|
+
type: "text",
|
|
6085
|
+
text: "No curated bundles match your currently installed servers. Browse https://mcp.hosting/explore to add the servers a bundle needs, then re-run mcp_connect_bundles."
|
|
6086
|
+
}
|
|
6087
|
+
]
|
|
6088
|
+
};
|
|
6089
|
+
}
|
|
6090
|
+
const lines = [];
|
|
6091
|
+
if (ready.length > 0) {
|
|
6092
|
+
lines.push("Bundles ready to activate now:");
|
|
6093
|
+
for (const bundle of ready) {
|
|
6094
|
+
lines.push(` ${bundle.id} \u2014 ${bundle.description}`);
|
|
6095
|
+
lines.push(` namespaces: ${JSON.stringify(bundle.namespaces)}`);
|
|
6096
|
+
lines.push(` activate: ${bundleActivateHint(bundle)}`);
|
|
6097
|
+
}
|
|
6098
|
+
}
|
|
6099
|
+
if (partial.length > 0) {
|
|
6100
|
+
if (ready.length > 0) lines.push("");
|
|
6101
|
+
lines.push("Bundles partially installed:");
|
|
6102
|
+
for (const entry of partial) {
|
|
6103
|
+
const { bundle, have, missing } = entry;
|
|
6104
|
+
lines.push(` ${bundle.id} \u2014 ${bundle.description}`);
|
|
6105
|
+
lines.push(` have: ${have.join(", ")}`);
|
|
6106
|
+
lines.push(` missing: ${missing.join(", ")} (install from https://mcp.hosting/explore)`);
|
|
6107
|
+
}
|
|
6108
|
+
}
|
|
6109
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
6110
|
+
}
|
|
5931
6111
|
// Declarative pipeline executor. Runs N tool calls in order, binding
|
|
5932
6112
|
// each output under the step's id (or positional index), and lets
|
|
5933
6113
|
// later steps splice those outputs into their args via
|
|
@@ -6160,7 +6340,7 @@ ${installBlock}
|
|
|
6160
6340
|
);
|
|
6161
6341
|
process.exit(0);
|
|
6162
6342
|
} else if (subcommand === "--version" || subcommand === "-V") {
|
|
6163
|
-
process.stdout.write(`mcph ${true ? "0.
|
|
6343
|
+
process.stdout.write(`mcph ${true ? "0.28.1" : "dev"}
|
|
6164
6344
|
`);
|
|
6165
6345
|
process.exit(0);
|
|
6166
6346
|
} else {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yawlabs/mcph",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.28.1",
|
|
4
4
|
"description": "mcp.hosting — one install, all your MCP servers, managed from the cloud",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"author": "Yaw Labs <contact@yaw.sh> (https://yaw.sh)",
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
"files": [
|
|
12
12
|
"dist",
|
|
13
13
|
"!dist/**/*.test.*",
|
|
14
|
-
"README.md"
|
|
14
|
+
"README.md",
|
|
15
|
+
"CHANGELOG.md"
|
|
15
16
|
],
|
|
16
17
|
"scripts": {
|
|
17
18
|
"build": "tsup",
|