autorouter-mcp 0.2.5 → 0.2.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/README.md +28 -0
- package/dist/cli.js +246 -2
- package/package.json +1 -1
- package/server.json +2 -2
package/README.md
CHANGED
|
@@ -364,12 +364,40 @@ autorouter adopt --target claude --servers-only # skip skills and plugins
|
|
|
364
364
|
autorouter adopt --target claude --keep project-tools --keep-plugin ui-toolkit
|
|
365
365
|
autorouter restore --target claude # undo the most recent adopt
|
|
366
366
|
|
|
367
|
+
autorouter update # upgrade in place
|
|
368
|
+
autorouter update --check # is there a newer version?
|
|
369
|
+
|
|
367
370
|
autorouter login # which servers need a grant
|
|
368
371
|
autorouter login remote-server # authorize one (opens a browser)
|
|
369
372
|
autorouter login remote-server --device # headless: enter a code elsewhere
|
|
370
373
|
autorouter logout remote-server # forget a stored grant
|
|
371
374
|
```
|
|
372
375
|
|
|
376
|
+
## Updating
|
|
377
|
+
|
|
378
|
+
`autorouter update` upgrades in place, using the package manager that installed
|
|
379
|
+
the copy you are running:
|
|
380
|
+
|
|
381
|
+
```sh
|
|
382
|
+
autorouter update # detect, then upgrade
|
|
383
|
+
autorouter update --check # report the available version, install nothing
|
|
384
|
+
autorouter update --dry-run # print the command, do not run it
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
Which manager to use is decided by where the running file sits on disk, not by
|
|
388
|
+
what happens to be on `PATH` — running `npm i -g` against a pnpm-managed global
|
|
389
|
+
installs a second copy that shadows the first, and you would then be upgrading
|
|
390
|
+
one install while running the other.
|
|
391
|
+
|
|
392
|
+
Three cases do not run a package manager, and say so instead: a copy unpacked by
|
|
393
|
+
`npx`/`bunx`/`pnpm dlx` (nothing to upgrade — those refetch every run), a git
|
|
394
|
+
checkout (`git pull && bun install && bun run build`), and a layout it cannot
|
|
395
|
+
identify. All three still report whether a newer version exists.
|
|
396
|
+
|
|
397
|
+
The router is a long-lived stdio server, so a harness that already has it
|
|
398
|
+
running keeps the old build until it respawns it — restart the harness, or
|
|
399
|
+
reconnect the MCP server, after updating.
|
|
400
|
+
|
|
373
401
|
## OAuth servers
|
|
374
402
|
|
|
375
403
|
Some remote MCP servers carry no credentials in their visible configuration.
|
package/dist/cli.js
CHANGED
|
@@ -19474,6 +19474,18 @@ function run(command, args, opts = {}) {
|
|
|
19474
19474
|
child.stdin.end();
|
|
19475
19475
|
});
|
|
19476
19476
|
}
|
|
19477
|
+
function runStreaming(command, args, opts = {}) {
|
|
19478
|
+
return new Promise((resolve, reject) => {
|
|
19479
|
+
const child = spawn2(command, args, {
|
|
19480
|
+
stdio: "inherit",
|
|
19481
|
+
cwd: opts.cwd,
|
|
19482
|
+
env: opts.env,
|
|
19483
|
+
shell: process.platform === "win32"
|
|
19484
|
+
});
|
|
19485
|
+
child.on("error", reject);
|
|
19486
|
+
child.on("close", (code) => resolve(code));
|
|
19487
|
+
});
|
|
19488
|
+
}
|
|
19477
19489
|
function which(command) {
|
|
19478
19490
|
const isWindows = process.platform === "win32";
|
|
19479
19491
|
const exts = isWindows ? (process.env.PATHEXT ?? ".EXE;.CMD;.BAT;.COM").split(";").filter(Boolean) : [""];
|
|
@@ -22585,8 +22597,224 @@ function summarizeScopes(scope, limit = 6) {
|
|
|
22585
22597
|
return `scope: ${all.length} granted, ${writes.length} of them write (${writes.slice(0, 3).join(", ") || "none"}${writes.length > 3 ? ", …" : ""})`;
|
|
22586
22598
|
}
|
|
22587
22599
|
|
|
22600
|
+
// src/cli/update.ts
|
|
22601
|
+
import { existsSync, realpathSync } from "node:fs";
|
|
22602
|
+
import { fileURLToPath } from "node:url";
|
|
22603
|
+
import { dirname as dirname3, join as join18, resolve as resolve2, sep } from "node:path";
|
|
22604
|
+
var PACKAGE_NAME = "autorouter-mcp";
|
|
22605
|
+
function entryPath() {
|
|
22606
|
+
let raw;
|
|
22607
|
+
try {
|
|
22608
|
+
raw = fileURLToPath(import.meta.url);
|
|
22609
|
+
} catch {
|
|
22610
|
+
raw = process.argv[1] ?? "";
|
|
22611
|
+
}
|
|
22612
|
+
try {
|
|
22613
|
+
return realpathSync(raw);
|
|
22614
|
+
} catch {
|
|
22615
|
+
return raw;
|
|
22616
|
+
}
|
|
22617
|
+
}
|
|
22618
|
+
function packageRoot(from, exists) {
|
|
22619
|
+
let dir = dirname3(from);
|
|
22620
|
+
for (let i = 0;i < 12; i++) {
|
|
22621
|
+
if (exists(join18(dir, "package.json")))
|
|
22622
|
+
return dir;
|
|
22623
|
+
const parent = dirname3(dir);
|
|
22624
|
+
if (parent === dir)
|
|
22625
|
+
break;
|
|
22626
|
+
dir = parent;
|
|
22627
|
+
}
|
|
22628
|
+
return;
|
|
22629
|
+
}
|
|
22630
|
+
function managerFromLockfile(dir, exists) {
|
|
22631
|
+
if (exists(join18(dir, "bun.lock")) || exists(join18(dir, "bun.lockb")))
|
|
22632
|
+
return "bun";
|
|
22633
|
+
if (exists(join18(dir, "pnpm-lock.yaml")))
|
|
22634
|
+
return "pnpm";
|
|
22635
|
+
if (exists(join18(dir, "yarn.lock")))
|
|
22636
|
+
return "yarn";
|
|
22637
|
+
return "npm";
|
|
22638
|
+
}
|
|
22639
|
+
function detectInstall(entry, exists = existsSync) {
|
|
22640
|
+
const root = packageRoot(entry, exists);
|
|
22641
|
+
if (!root)
|
|
22642
|
+
return { kind: "unknown", dir: dirname3(entry) };
|
|
22643
|
+
const path = root.split(sep).join("/");
|
|
22644
|
+
if (exists(join18(root, ".git")))
|
|
22645
|
+
return { kind: "source", dir: root };
|
|
22646
|
+
const transient = path.includes("/_npx/") && "npx" || /\/dlx(-|\/)/.test(path) && "pnpm dlx" || path.includes("/.bun/install/cache/") && "bunx" || null;
|
|
22647
|
+
if (transient)
|
|
22648
|
+
return { kind: "transient", runner: transient, dir: root };
|
|
22649
|
+
const nodeModules = dirname3(root);
|
|
22650
|
+
const parent = dirname3(nodeModules);
|
|
22651
|
+
const globalManager = path.includes("/.bun/install/global/") ? "bun" : /\/pnpm\/global\/|\/pnpm\/[0-9]+\/node_modules\//.test(path) ? "pnpm" : /\/\.config\/yarn\/global\/|\/\.yarn\/global\//.test(path) ? "yarn" : null;
|
|
22652
|
+
if (globalManager)
|
|
22653
|
+
return { kind: "global", manager: globalManager, dir: root };
|
|
22654
|
+
if (nodeModules.split(sep).pop() === "node_modules") {
|
|
22655
|
+
if (exists(join18(parent, "package.json"))) {
|
|
22656
|
+
return {
|
|
22657
|
+
kind: "project",
|
|
22658
|
+
manager: managerFromLockfile(parent, exists),
|
|
22659
|
+
dir: root,
|
|
22660
|
+
projectDir: parent
|
|
22661
|
+
};
|
|
22662
|
+
}
|
|
22663
|
+
return { kind: "global", manager: "npm", dir: root };
|
|
22664
|
+
}
|
|
22665
|
+
return { kind: "unknown", dir: root };
|
|
22666
|
+
}
|
|
22667
|
+
function updateCommand(install, pkg, version = "latest") {
|
|
22668
|
+
const spec = `${pkg}@${version}`;
|
|
22669
|
+
if (install.kind === "global") {
|
|
22670
|
+
switch (install.manager) {
|
|
22671
|
+
case "bun":
|
|
22672
|
+
return ["bun", "add", "-g", spec];
|
|
22673
|
+
case "pnpm":
|
|
22674
|
+
return ["pnpm", "add", "-g", spec];
|
|
22675
|
+
case "yarn":
|
|
22676
|
+
return ["yarn", "global", "add", spec];
|
|
22677
|
+
default:
|
|
22678
|
+
return ["npm", "install", "-g", spec];
|
|
22679
|
+
}
|
|
22680
|
+
}
|
|
22681
|
+
if (install.kind === "project") {
|
|
22682
|
+
switch (install.manager) {
|
|
22683
|
+
case "bun":
|
|
22684
|
+
return ["bun", "add", spec];
|
|
22685
|
+
case "pnpm":
|
|
22686
|
+
return ["pnpm", "add", spec];
|
|
22687
|
+
case "yarn":
|
|
22688
|
+
return ["yarn", "add", spec];
|
|
22689
|
+
default:
|
|
22690
|
+
return ["npm", "install", spec];
|
|
22691
|
+
}
|
|
22692
|
+
}
|
|
22693
|
+
return null;
|
|
22694
|
+
}
|
|
22695
|
+
function compareVersions2(a, b) {
|
|
22696
|
+
const split = (v) => {
|
|
22697
|
+
const [core = "", pre] = v.replace(/^v/, "").split("-", 2);
|
|
22698
|
+
const parts = core.split(".").map((n) => Number.parseInt(n, 10) || 0);
|
|
22699
|
+
return { parts, pre };
|
|
22700
|
+
};
|
|
22701
|
+
const x = split(a);
|
|
22702
|
+
const y = split(b);
|
|
22703
|
+
for (let i = 0;i < 3; i++) {
|
|
22704
|
+
const d = (x.parts[i] ?? 0) - (y.parts[i] ?? 0);
|
|
22705
|
+
if (d !== 0)
|
|
22706
|
+
return d < 0 ? -1 : 1;
|
|
22707
|
+
}
|
|
22708
|
+
if (x.pre && !y.pre)
|
|
22709
|
+
return -1;
|
|
22710
|
+
if (!x.pre && y.pre)
|
|
22711
|
+
return 1;
|
|
22712
|
+
if (x.pre && y.pre && x.pre !== y.pre)
|
|
22713
|
+
return x.pre < y.pre ? -1 : 1;
|
|
22714
|
+
return 0;
|
|
22715
|
+
}
|
|
22716
|
+
async function latestVersion(pkg, fetchFn = fetch) {
|
|
22717
|
+
const base = (process.env.npm_config_registry ?? process.env.NPM_CONFIG_REGISTRY ?? "https://registry.npmjs.org").replace(/\/+$/, "");
|
|
22718
|
+
try {
|
|
22719
|
+
const res = await fetchFn(`${base}/${encodeURIComponent(pkg)}/latest`, {
|
|
22720
|
+
headers: { accept: "application/json" },
|
|
22721
|
+
signal: AbortSignal.timeout(1e4)
|
|
22722
|
+
});
|
|
22723
|
+
if (!res.ok)
|
|
22724
|
+
return { error: `registry returned ${res.status}` };
|
|
22725
|
+
const body = await res.json();
|
|
22726
|
+
return body.version ? { version: body.version } : { error: "registry returned no version" };
|
|
22727
|
+
} catch (err) {
|
|
22728
|
+
return { error: err instanceof Error ? err.message : String(err) };
|
|
22729
|
+
}
|
|
22730
|
+
}
|
|
22731
|
+
function cannotUpdate(install, pkg) {
|
|
22732
|
+
if (install.kind === "transient") {
|
|
22733
|
+
return `This copy was unpacked by ${install.runner}, which fetches the package fresh on every run —
|
|
22734
|
+
` + ` there is no install here to upgrade. You are already getting the latest each time.
|
|
22735
|
+
` + ` To keep a copy that does not re-download: npm install -g ${pkg}`;
|
|
22736
|
+
}
|
|
22737
|
+
if (install.kind === "source") {
|
|
22738
|
+
return `This is a checkout running from source (${install.dir}), not a package install.
|
|
22739
|
+
` + ` Update it with: git -C ${install.dir} pull && bun install && bun run build`;
|
|
22740
|
+
}
|
|
22741
|
+
return `Could not tell which package manager installed this copy (${install.dir}).
|
|
22742
|
+
` + ` Upgrade it the way you installed it, e.g. npm install -g ${pkg}@latest`;
|
|
22743
|
+
}
|
|
22744
|
+
function describe2(install) {
|
|
22745
|
+
switch (install.kind) {
|
|
22746
|
+
case "global":
|
|
22747
|
+
return `${install.manager} global install at ${install.dir}`;
|
|
22748
|
+
case "project":
|
|
22749
|
+
return `${install.manager} dependency of ${install.projectDir}`;
|
|
22750
|
+
case "transient":
|
|
22751
|
+
return `${install.runner} temporary copy at ${install.dir}`;
|
|
22752
|
+
case "source":
|
|
22753
|
+
return `source checkout at ${install.dir}`;
|
|
22754
|
+
default:
|
|
22755
|
+
return `unrecognized install at ${install.dir}`;
|
|
22756
|
+
}
|
|
22757
|
+
}
|
|
22758
|
+
async function runUpdate(opts) {
|
|
22759
|
+
const entry = opts.entry ?? entryPath();
|
|
22760
|
+
const install = detectInstall(entry, opts.exists);
|
|
22761
|
+
const manifest = install.kind === "unknown" ? null : await readJson(join18(install.dir, "package.json"));
|
|
22762
|
+
const pkg = manifest?.name ?? PACKAGE_NAME;
|
|
22763
|
+
const latest = await latestVersion(pkg, opts.fetchFn);
|
|
22764
|
+
if ("error" in latest) {
|
|
22765
|
+
return { ok: false, message: `Could not reach the registry to check for updates: ${latest.error}` };
|
|
22766
|
+
}
|
|
22767
|
+
const behind = compareVersions2(opts.current, latest.version) < 0;
|
|
22768
|
+
const status = behind ? `autorouter ${opts.current} → ${latest.version} available` : `autorouter ${opts.current} is up to date (latest is ${latest.version})`;
|
|
22769
|
+
const command = updateCommand(install, pkg, latest.version);
|
|
22770
|
+
if (!command) {
|
|
22771
|
+
return { ok: !behind, message: `${status}
|
|
22772
|
+
|
|
22773
|
+
${cannotUpdate(install, pkg)}` };
|
|
22774
|
+
}
|
|
22775
|
+
const printable = command.join(" ");
|
|
22776
|
+
if (opts.check) {
|
|
22777
|
+
return { ok: true, message: `${status}
|
|
22778
|
+
${describe2(install)}
|
|
22779
|
+
Update with: ${printable}` };
|
|
22780
|
+
}
|
|
22781
|
+
if (!behind && !opts.force) {
|
|
22782
|
+
return { ok: true, message: `${status}
|
|
22783
|
+
${describe2(install)}
|
|
22784
|
+
Re-install anyway with: --force` };
|
|
22785
|
+
}
|
|
22786
|
+
if (opts.dryRun) {
|
|
22787
|
+
return { ok: true, message: `${status}
|
|
22788
|
+
${describe2(install)}
|
|
22789
|
+
Would run: ${printable}` };
|
|
22790
|
+
}
|
|
22791
|
+
console.log(`${status}
|
|
22792
|
+
${describe2(install)}
|
|
22793
|
+
Running: ${printable}
|
|
22794
|
+
`);
|
|
22795
|
+
const code = await runStreaming(command[0], command.slice(1), {
|
|
22796
|
+
cwd: install.kind === "project" ? install.projectDir : undefined
|
|
22797
|
+
}).catch((err) => err);
|
|
22798
|
+
if (code instanceof Error) {
|
|
22799
|
+
return {
|
|
22800
|
+
ok: false,
|
|
22801
|
+
message: `Could not run ${command[0]}: ${code.message}
|
|
22802
|
+
` + ` Run it yourself: ${printable}`
|
|
22803
|
+
};
|
|
22804
|
+
}
|
|
22805
|
+
if (code !== 0) {
|
|
22806
|
+
return { ok: false, message: `${command[0]} exited with code ${code}. Nothing was changed by autorouter.` };
|
|
22807
|
+
}
|
|
22808
|
+
return {
|
|
22809
|
+
ok: true,
|
|
22810
|
+
message: `
|
|
22811
|
+
Updated to ${latest.version}. Restart any harness with the router running to pick it up` + `
|
|
22812
|
+
(it is a long-lived stdio server, so an open session keeps the old build).`
|
|
22813
|
+
};
|
|
22814
|
+
}
|
|
22815
|
+
|
|
22588
22816
|
// src/cli.ts
|
|
22589
|
-
var VERSION2 = "0.2.
|
|
22817
|
+
var VERSION2 = "0.2.6";
|
|
22590
22818
|
var USAGE = `autorouter — one search tool instead of every tool
|
|
22591
22819
|
|
|
22592
22820
|
autorouter serve Run as an MCP server over stdio (default)
|
|
@@ -22596,6 +22824,9 @@ var USAGE = `autorouter — one search tool instead of every tool
|
|
|
22596
22824
|
autorouter list [--kind K] List everything in the catalog
|
|
22597
22825
|
autorouter reindex Rebuild the catalog now
|
|
22598
22826
|
autorouter doctor Show what is reachable and what it saves
|
|
22827
|
+
autorouter update Upgrade via the package manager that
|
|
22828
|
+
installed this copy. --check to look
|
|
22829
|
+
without installing.
|
|
22599
22830
|
autorouter login [server] Authorize an OAuth server (opens a browser,
|
|
22600
22831
|
or prints a code on a headless box);
|
|
22601
22832
|
with no argument, lists what needs one
|
|
@@ -22623,6 +22854,8 @@ Options
|
|
|
22623
22854
|
--json machine-readable output
|
|
22624
22855
|
--yes init/adopt: do not prompt
|
|
22625
22856
|
--dry-run adopt: show what would move, change nothing
|
|
22857
|
+
update: print the upgrade command without running it
|
|
22858
|
+
--check update: report the available version, install nothing
|
|
22626
22859
|
--force adopt: proceed even if a server is unreachable
|
|
22627
22860
|
--keep S adopt: leave server S registered in the harness (comma-separated)
|
|
22628
22861
|
--keep-skill S adopt: leave skill S loaded (comma-separated)
|
|
@@ -22678,6 +22911,17 @@ async function main(argv) {
|
|
|
22678
22911
|
case "doctor":
|
|
22679
22912
|
console.log(await runDoctor(process.cwd()));
|
|
22680
22913
|
return 0;
|
|
22914
|
+
case "update":
|
|
22915
|
+
case "upgrade": {
|
|
22916
|
+
const result = await runUpdate({
|
|
22917
|
+
current: VERSION2,
|
|
22918
|
+
check: Boolean(flags.check),
|
|
22919
|
+
dryRun: Boolean(flags["dry-run"]),
|
|
22920
|
+
force: Boolean(flags.force)
|
|
22921
|
+
});
|
|
22922
|
+
console.log(result.message);
|
|
22923
|
+
return result.ok ? 0 : 1;
|
|
22924
|
+
}
|
|
22681
22925
|
case "init":
|
|
22682
22926
|
return await cmdInit(flags);
|
|
22683
22927
|
case "login":
|
|
@@ -23082,7 +23326,7 @@ function parseArgs(argv) {
|
|
|
23082
23326
|
command = `--${name}`;
|
|
23083
23327
|
continue;
|
|
23084
23328
|
}
|
|
23085
|
-
const boolean = ["raw", "json", "yes", "dry-run", "force", "servers-only", "read-only", "all-scopes", "list-scopes", "device", "manual"].includes(name) && !(name === "json" && command === "add");
|
|
23329
|
+
const boolean = ["raw", "json", "yes", "dry-run", "force", "servers-only", "read-only", "all-scopes", "list-scopes", "device", "manual", "check"].includes(name) && !(name === "json" && command === "add");
|
|
23086
23330
|
if (boolean) {
|
|
23087
23331
|
flags[name] = true;
|
|
23088
23332
|
} else if (inline !== undefined) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "autorouter-mcp",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.6",
|
|
4
4
|
"description": "One search tool instead of every tool: an MCP capability router for Claude Code, Codex, Cursor and anything else that speaks MCP.",
|
|
5
5
|
"mcpName": "io.github.Webb-Ventures/autorouter",
|
|
6
6
|
"license": "MIT",
|
package/server.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "io.github.Webb-Ventures/autorouter",
|
|
4
4
|
"title": "autorouter",
|
|
5
5
|
"description": "An MCP capability router: one search tool instead of every server's tool schema.",
|
|
6
|
-
"version": "0.2.
|
|
6
|
+
"version": "0.2.6",
|
|
7
7
|
"websiteUrl": "https://github.com/Webb-Ventures/autorouter",
|
|
8
8
|
"repository": {
|
|
9
9
|
"url": "https://github.com/Webb-Ventures/autorouter",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"registryType": "npm",
|
|
15
15
|
"registryBaseUrl": "https://registry.npmjs.org",
|
|
16
16
|
"identifier": "autorouter-mcp",
|
|
17
|
-
"version": "0.2.
|
|
17
|
+
"version": "0.2.6",
|
|
18
18
|
"transport": { "type": "stdio" },
|
|
19
19
|
"packageArguments": [{ "type": "positional", "value": "serve" }],
|
|
20
20
|
"environmentVariables": [
|