mcp-google-map 0.5.0 → 0.6.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/README.md +1 -1
- package/dist/cli.js +703 -498
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -19,6 +19,46 @@ var AUTO_UPDATE_DISABLED_ENV = "MCP_AUTO_UPDATE_DISABLED";
|
|
|
19
19
|
var DEFAULT_PACKAGE_NAME = "mcp-google-map";
|
|
20
20
|
var DEFAULT_REGISTRY_BASE_URL = "https://registry.npmjs.org";
|
|
21
21
|
var DEFAULT_UPDATE_CHECK_TIMEOUT_MS = 2e3;
|
|
22
|
+
async function probeLatestVersion(deps = {}) {
|
|
23
|
+
const env = deps.env ?? process.env;
|
|
24
|
+
if (isAutoUpdateDisabled(env)) {
|
|
25
|
+
return { status: "disabled" };
|
|
26
|
+
}
|
|
27
|
+
const currentVersion = deps.currentVersion;
|
|
28
|
+
if (!currentVersion) {
|
|
29
|
+
return { status: "unknown-version" };
|
|
30
|
+
}
|
|
31
|
+
const packageName = deps.packageName ?? DEFAULT_PACKAGE_NAME;
|
|
32
|
+
const registryBaseUrl = deps.registryBaseUrl ?? DEFAULT_REGISTRY_BASE_URL;
|
|
33
|
+
const timeoutMs = deps.timeoutMs ?? DEFAULT_UPDATE_CHECK_TIMEOUT_MS;
|
|
34
|
+
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
35
|
+
try {
|
|
36
|
+
const response = await fetchImpl(`${registryBaseUrl}/${packageName}/latest`, {
|
|
37
|
+
headers: { accept: "application/json" },
|
|
38
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
39
|
+
});
|
|
40
|
+
if (!response.ok) {
|
|
41
|
+
return {
|
|
42
|
+
status: "error",
|
|
43
|
+
message: `registry responded with HTTP ${response.status}.`
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
const payload = await response.json();
|
|
47
|
+
if (typeof payload?.version !== "string") {
|
|
48
|
+
return {
|
|
49
|
+
status: "error",
|
|
50
|
+
message: "registry response has no version field."
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
if (!isNewerVersion(payload.version, currentVersion)) {
|
|
54
|
+
return { status: "current", currentVersion };
|
|
55
|
+
}
|
|
56
|
+
return { status: "newer", currentVersion, latestVersion: payload.version };
|
|
57
|
+
} catch (error) {
|
|
58
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
59
|
+
return { status: "error", message };
|
|
60
|
+
}
|
|
61
|
+
}
|
|
22
62
|
function isAutoUpdateDisabled(env = process.env) {
|
|
23
63
|
const raw = env[AUTO_UPDATE_DISABLED_ENV];
|
|
24
64
|
return typeof raw === "string" && /^(1|true|yes)$/i.test(raw.trim());
|
|
@@ -40,54 +80,6 @@ function isNewerVersion(candidate, current) {
|
|
|
40
80
|
}
|
|
41
81
|
return false;
|
|
42
82
|
}
|
|
43
|
-
async function checkForUpdates(deps = {}) {
|
|
44
|
-
const logger = deps.logger ?? Logger;
|
|
45
|
-
const env = deps.env ?? process.env;
|
|
46
|
-
try {
|
|
47
|
-
if (isAutoUpdateDisabled(env)) {
|
|
48
|
-
return null;
|
|
49
|
-
}
|
|
50
|
-
const currentVersion = deps.currentVersion;
|
|
51
|
-
if (!currentVersion) {
|
|
52
|
-
logger.error(
|
|
53
|
-
`\u26A0\uFE0F [${DEFAULT_PACKAGE_NAME}] Automatic update check skipped: running package version is unknown.`
|
|
54
|
-
);
|
|
55
|
-
return null;
|
|
56
|
-
}
|
|
57
|
-
const packageName = deps.packageName ?? DEFAULT_PACKAGE_NAME;
|
|
58
|
-
const registryBaseUrl = deps.registryBaseUrl ?? DEFAULT_REGISTRY_BASE_URL;
|
|
59
|
-
const timeoutMs = deps.timeoutMs ?? DEFAULT_UPDATE_CHECK_TIMEOUT_MS;
|
|
60
|
-
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
61
|
-
const response = await fetchImpl(`${registryBaseUrl}/${packageName}/latest`, {
|
|
62
|
-
headers: { accept: "application/vnd.npm.install-v1+json" },
|
|
63
|
-
signal: AbortSignal.timeout(timeoutMs)
|
|
64
|
-
});
|
|
65
|
-
if (!response.ok) {
|
|
66
|
-
logger.error(
|
|
67
|
-
`\u26A0\uFE0F [${DEFAULT_PACKAGE_NAME}] Automatic update check failed: registry responded with HTTP ${response.status}.`
|
|
68
|
-
);
|
|
69
|
-
return null;
|
|
70
|
-
}
|
|
71
|
-
const payload = await response.json();
|
|
72
|
-
if (typeof payload?.version !== "string") {
|
|
73
|
-
logger.error(
|
|
74
|
-
`\u26A0\uFE0F [${DEFAULT_PACKAGE_NAME}] Automatic update check failed: registry response has no version field.`
|
|
75
|
-
);
|
|
76
|
-
return null;
|
|
77
|
-
}
|
|
78
|
-
if (!isNewerVersion(payload.version, currentVersion)) {
|
|
79
|
-
return null;
|
|
80
|
-
}
|
|
81
|
-
logger.log(
|
|
82
|
-
`\u26A0\uFE0F Update available: ${packageName} ${currentVersion} \u2192 ${payload.version}. Upgrade with: npm install -g ${packageName}@latest (or run npx -y ${packageName}@latest).`
|
|
83
|
-
);
|
|
84
|
-
return { currentVersion, latestVersion: payload.version };
|
|
85
|
-
} catch (error) {
|
|
86
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
87
|
-
logger.error(`\u26A0\uFE0F [${DEFAULT_PACKAGE_NAME}] Automatic update check failed: ${message}`);
|
|
88
|
-
return null;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
83
|
|
|
92
84
|
// src/utils/configResolver.ts
|
|
93
85
|
var NODE_ENGINE_FLOOR = 22;
|
|
@@ -1451,10 +1443,256 @@ var BaseMcpServer = class {
|
|
|
1451
1443
|
|
|
1452
1444
|
// src/cli.ts
|
|
1453
1445
|
import { fileURLToPath } from "url";
|
|
1454
|
-
import { dirname } from "path";
|
|
1455
|
-
import { readFileSync as
|
|
1446
|
+
import { dirname as dirname2 } from "path";
|
|
1447
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync4 } from "fs";
|
|
1456
1448
|
import { createInterface } from "readline";
|
|
1457
1449
|
|
|
1450
|
+
// src/utils/upgradeManager.ts
|
|
1451
|
+
import { spawnSync } from "child_process";
|
|
1452
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
1453
|
+
import { homedir } from "os";
|
|
1454
|
+
import { dirname, join as join2 } from "path";
|
|
1455
|
+
|
|
1456
|
+
// src/utils/cliSettings.ts
|
|
1457
|
+
import { mkdirSync, readFileSync, renameSync, writeFileSync } from "fs";
|
|
1458
|
+
import { join, resolve } from "path";
|
|
1459
|
+
var SETTINGS_PROGRAM_NAME = "mcp-google-map";
|
|
1460
|
+
var CLI_SETTINGS_RELATIVE = [".config", SETTINGS_PROGRAM_NAME, "config.json"];
|
|
1461
|
+
var MCP_SERVER_NAME = "mcp-google-map";
|
|
1462
|
+
function resolveCliSettingsPath(home) {
|
|
1463
|
+
const root = home ?? (process.env.HOME ?? process.cwd());
|
|
1464
|
+
return resolve(root, ...CLI_SETTINGS_RELATIVE);
|
|
1465
|
+
}
|
|
1466
|
+
function uniqueStrings(value) {
|
|
1467
|
+
if (!Array.isArray(value)) return [];
|
|
1468
|
+
const result = [];
|
|
1469
|
+
for (const item of value) {
|
|
1470
|
+
if (typeof item === "string" && item.length > 0 && !result.includes(item)) {
|
|
1471
|
+
result.push(item);
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
return result;
|
|
1475
|
+
}
|
|
1476
|
+
function loadCliSettings(home) {
|
|
1477
|
+
try {
|
|
1478
|
+
const raw = JSON.parse(readFileSync(resolveCliSettingsPath(home), "utf-8"));
|
|
1479
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
|
|
1480
|
+
return { enabled: [] };
|
|
1481
|
+
}
|
|
1482
|
+
return { enabled: uniqueStrings(raw.enabled) };
|
|
1483
|
+
} catch {
|
|
1484
|
+
return { enabled: [] };
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
function saveCliSettings(settings, home) {
|
|
1488
|
+
const path = resolveCliSettingsPath(home);
|
|
1489
|
+
const temporary = `${path}.tmp`;
|
|
1490
|
+
try {
|
|
1491
|
+
mkdirSync(join(path, ".."), { recursive: true });
|
|
1492
|
+
writeFileSync(temporary, JSON.stringify({ enabled: uniqueStrings(settings.enabled) }, null, 2) + "\n", "utf-8");
|
|
1493
|
+
renameSync(temporary, path);
|
|
1494
|
+
} catch (error) {
|
|
1495
|
+
throw new Error(`settings write failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1496
|
+
}
|
|
1497
|
+
return path;
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1500
|
+
// src/utils/upgradeManager.ts
|
|
1501
|
+
var ANSI_GREEN = "\x1B[92m";
|
|
1502
|
+
var ANSI_RED = "\x1B[91m";
|
|
1503
|
+
var ANSI_RESET = "\x1B[0m";
|
|
1504
|
+
var green = (text) => `${ANSI_GREEN}${text}${ANSI_RESET}`;
|
|
1505
|
+
var red = (text) => `${ANSI_RED}${text}${ANSI_RESET}`;
|
|
1506
|
+
var IDLE_CACHE_DIR_NAME = "mcp-google-map";
|
|
1507
|
+
var IDLE_CACHE_FILE_NAME = "check_version_idle-time.json";
|
|
1508
|
+
var IDLE_WINDOW_NEWER_MS = 60 * 60 * 1e3;
|
|
1509
|
+
var IDLE_WINDOW_DEFAULT_MS = 24 * 60 * 60 * 1e3;
|
|
1510
|
+
var PACKAGE_UPGRADE_COMMAND = [
|
|
1511
|
+
"npm",
|
|
1512
|
+
"install",
|
|
1513
|
+
"-g",
|
|
1514
|
+
`${DEFAULT_PACKAGE_NAME}@latest`
|
|
1515
|
+
];
|
|
1516
|
+
var PACKAGE_UNINSTALL_COMMAND = [
|
|
1517
|
+
"npm",
|
|
1518
|
+
"uninstall",
|
|
1519
|
+
"-g",
|
|
1520
|
+
DEFAULT_PACKAGE_NAME
|
|
1521
|
+
];
|
|
1522
|
+
var POST_UPGRADE_INSTALL_COMMAND = [
|
|
1523
|
+
"npx",
|
|
1524
|
+
"-y",
|
|
1525
|
+
DEFAULT_PACKAGE_NAME,
|
|
1526
|
+
"install"
|
|
1527
|
+
];
|
|
1528
|
+
function resolveIdleCachePath(home) {
|
|
1529
|
+
return join2(home ?? homedir(), ".cache", IDLE_CACHE_DIR_NAME, IDLE_CACHE_FILE_NAME);
|
|
1530
|
+
}
|
|
1531
|
+
function loadIdleCache(cachePath, fs = {}) {
|
|
1532
|
+
try {
|
|
1533
|
+
const readFile = fs.readFile ?? ((path) => readFileSync2(path, "utf-8"));
|
|
1534
|
+
const raw = readFile(cachePath);
|
|
1535
|
+
const parsed = JSON.parse(raw);
|
|
1536
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
1537
|
+
return {};
|
|
1538
|
+
}
|
|
1539
|
+
const record = parsed;
|
|
1540
|
+
const isFiniteNumber = (value) => typeof value === "number" && Number.isFinite(value);
|
|
1541
|
+
return {
|
|
1542
|
+
last_success_epoch: isFiniteNumber(record.last_success_epoch) ? record.last_success_epoch : void 0,
|
|
1543
|
+
last_success_human: typeof record.last_success_human === "string" ? record.last_success_human : void 0,
|
|
1544
|
+
idle_time_epoch: isFiniteNumber(record.idle_time_epoch) ? record.idle_time_epoch : void 0,
|
|
1545
|
+
idle_time_human: typeof record.idle_time_human === "string" ? record.idle_time_human : void 0
|
|
1546
|
+
};
|
|
1547
|
+
} catch {
|
|
1548
|
+
return {};
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
function persistIdleCache(cachePath, lastSuccess, idleUntil, fs = {}, logger = Logger) {
|
|
1552
|
+
const toIso = (epoch) => epoch === void 0 ? null : new Date(epoch).toISOString();
|
|
1553
|
+
const data = {
|
|
1554
|
+
last_success_epoch: lastSuccess ?? null,
|
|
1555
|
+
last_success_human: toIso(lastSuccess),
|
|
1556
|
+
idle_time_epoch: idleUntil,
|
|
1557
|
+
idle_time_human: toIso(idleUntil)
|
|
1558
|
+
};
|
|
1559
|
+
try {
|
|
1560
|
+
const mkdir = fs.mkdir ?? ((path) => mkdirSync2(path, { recursive: true }));
|
|
1561
|
+
const writeFile = fs.writeFile ?? ((path, data2) => writeFileSync2(path, data2));
|
|
1562
|
+
mkdir(dirname(cachePath));
|
|
1563
|
+
writeFile(cachePath, JSON.stringify(data, null, 2) + "\n");
|
|
1564
|
+
} catch (error) {
|
|
1565
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1566
|
+
logger.error(red(`Version cache error: ${message}`));
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
async function checkVersionIdle(deps = {}) {
|
|
1570
|
+
const logger = deps.logger ?? Logger;
|
|
1571
|
+
const now = deps.now ?? (() => Date.now());
|
|
1572
|
+
const cachePath = deps.cachePath ?? resolveIdleCachePath();
|
|
1573
|
+
const cache = loadIdleCache(cachePath, deps.fs);
|
|
1574
|
+
if (deps.force !== true) {
|
|
1575
|
+
const idle = cache.idle_time_epoch;
|
|
1576
|
+
if (typeof idle === "number" && idle > now()) {
|
|
1577
|
+
return { checked: false, status: "idle" };
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1580
|
+
const probe = await probeLatestVersion(deps);
|
|
1581
|
+
if (probe.status === "disabled") {
|
|
1582
|
+
return { checked: false, status: "disabled" };
|
|
1583
|
+
}
|
|
1584
|
+
let delayMs = IDLE_WINDOW_DEFAULT_MS;
|
|
1585
|
+
let lastSuccess = typeof cache.last_success_epoch === "number" ? cache.last_success_epoch : void 0;
|
|
1586
|
+
const result = { checked: true, status: probe.status };
|
|
1587
|
+
switch (probe.status) {
|
|
1588
|
+
case "unknown-version":
|
|
1589
|
+
logger.error(red("Version check error: running package version is unknown."));
|
|
1590
|
+
break;
|
|
1591
|
+
case "error":
|
|
1592
|
+
logger.error(red(`Version check error: ${probe.message}`));
|
|
1593
|
+
break;
|
|
1594
|
+
case "current":
|
|
1595
|
+
lastSuccess = now();
|
|
1596
|
+
result.currentVersion = probe.currentVersion;
|
|
1597
|
+
logger.error(red("Version check: no newer version available."));
|
|
1598
|
+
break;
|
|
1599
|
+
case "newer": {
|
|
1600
|
+
lastSuccess = now();
|
|
1601
|
+
delayMs = IDLE_WINDOW_NEWER_MS;
|
|
1602
|
+
result.currentVersion = probe.currentVersion;
|
|
1603
|
+
result.latestVersion = probe.latestVersion;
|
|
1604
|
+
logger.log(
|
|
1605
|
+
green(
|
|
1606
|
+
`Available Version: ${probe.latestVersion} | Installed Version: ${probe.currentVersion}`
|
|
1607
|
+
)
|
|
1608
|
+
);
|
|
1609
|
+
logger.log(
|
|
1610
|
+
green(
|
|
1611
|
+
`Upgrade with: npm install -g ${DEFAULT_PACKAGE_NAME}@latest (or run: ${DEFAULT_PACKAGE_NAME} --upgrade)`
|
|
1612
|
+
)
|
|
1613
|
+
);
|
|
1614
|
+
break;
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
const finishedAt = now();
|
|
1618
|
+
const idleUntil = finishedAt + delayMs;
|
|
1619
|
+
persistIdleCache(cachePath, lastSuccess, idleUntil, deps.fs, logger);
|
|
1620
|
+
result.idleUntilEpoch = idleUntil;
|
|
1621
|
+
result.lastSuccessEpoch = lastSuccess;
|
|
1622
|
+
return result;
|
|
1623
|
+
}
|
|
1624
|
+
function parseManagementCommand(args) {
|
|
1625
|
+
if (args.length !== 1) {
|
|
1626
|
+
return void 0;
|
|
1627
|
+
}
|
|
1628
|
+
const arg = args[0];
|
|
1629
|
+
if (arg === "--ver" || arg === "--version" || arg === "--upgrade" || arg === "--uninstall") {
|
|
1630
|
+
return arg;
|
|
1631
|
+
}
|
|
1632
|
+
return void 0;
|
|
1633
|
+
}
|
|
1634
|
+
function runPackageManagerCommand(command, deps = {}) {
|
|
1635
|
+
const logger = deps.logger ?? Logger;
|
|
1636
|
+
const spawnImpl = deps.spawn ?? spawnSync;
|
|
1637
|
+
const reportSpawnFailure = (message) => {
|
|
1638
|
+
logger.error(`Error executing ${command[0]}: ${message}`);
|
|
1639
|
+
logger.log(`Warning: run this command manually: ${command.join(" ")}`);
|
|
1640
|
+
return 1;
|
|
1641
|
+
};
|
|
1642
|
+
try {
|
|
1643
|
+
const result = spawnImpl(command[0], [...command.slice(1)], {
|
|
1644
|
+
stdio: "inherit",
|
|
1645
|
+
shell: process.platform === "win32"
|
|
1646
|
+
});
|
|
1647
|
+
if (result.error) {
|
|
1648
|
+
return reportSpawnFailure(result.error.message);
|
|
1649
|
+
}
|
|
1650
|
+
return result.status ?? 1;
|
|
1651
|
+
} catch (error) {
|
|
1652
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1653
|
+
return reportSpawnFailure(message);
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
function runPostUpgradeSkillInstall(options, deps = {}) {
|
|
1657
|
+
const enabled = loadCliSettings(options.home).enabled;
|
|
1658
|
+
if (enabled.length === 0) {
|
|
1659
|
+
return 0;
|
|
1660
|
+
}
|
|
1661
|
+
return runPackageManagerCommand(POST_UPGRADE_INSTALL_COMMAND, deps);
|
|
1662
|
+
}
|
|
1663
|
+
async function runVersionCommand(options, deps = {}) {
|
|
1664
|
+
await checkVersionIdle({
|
|
1665
|
+
...deps.idleCheck,
|
|
1666
|
+
force: true,
|
|
1667
|
+
currentVersion: options.currentVersion
|
|
1668
|
+
});
|
|
1669
|
+
const stdout = deps.stdout ?? process.stdout;
|
|
1670
|
+
stdout.write(`${options.currentVersion}
|
|
1671
|
+
`);
|
|
1672
|
+
return 0;
|
|
1673
|
+
}
|
|
1674
|
+
function runUpgradeCommand(options, deps = {}) {
|
|
1675
|
+
const upgraded = runPackageManagerCommand(PACKAGE_UPGRADE_COMMAND, deps);
|
|
1676
|
+
if (upgraded !== 0) {
|
|
1677
|
+
return upgraded;
|
|
1678
|
+
}
|
|
1679
|
+
return runPostUpgradeSkillInstall(options, deps);
|
|
1680
|
+
}
|
|
1681
|
+
function runUninstallCommand(deps = {}) {
|
|
1682
|
+
return runPackageManagerCommand(PACKAGE_UNINSTALL_COMMAND, deps);
|
|
1683
|
+
}
|
|
1684
|
+
async function runManagementCommand(command, options, deps = {}) {
|
|
1685
|
+
switch (command) {
|
|
1686
|
+
case "--ver":
|
|
1687
|
+
case "--version":
|
|
1688
|
+
return runVersionCommand(options, deps);
|
|
1689
|
+
case "--upgrade":
|
|
1690
|
+
return runUpgradeCommand(options, deps);
|
|
1691
|
+
case "--uninstall":
|
|
1692
|
+
return runUninstallCommand(deps);
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
|
|
1458
1696
|
// src/utils/toolProfiles.ts
|
|
1459
1697
|
var TOOL_API_PROFILES = {
|
|
1460
1698
|
maps_geocode: { tool: "maps_geocode", apis: ["geocoding"], dependsOnTools: [] },
|
|
@@ -1575,95 +1813,51 @@ function renderClientSnippet(options = {}) {
|
|
|
1575
1813
|
return { json, notes };
|
|
1576
1814
|
}
|
|
1577
1815
|
|
|
1578
|
-
// src/utils/cliSettings.ts
|
|
1579
|
-
import { mkdirSync, readFileSync, renameSync, writeFileSync } from "fs";
|
|
1580
|
-
import { join, resolve } from "path";
|
|
1581
|
-
var SETTINGS_PROGRAM_NAME = "mcp-google-map";
|
|
1582
|
-
var CLI_SETTINGS_RELATIVE = [".config", SETTINGS_PROGRAM_NAME, "config.json"];
|
|
1583
|
-
var MCP_SERVER_NAME = "mcp-google-map";
|
|
1584
|
-
function resolveCliSettingsPath(home) {
|
|
1585
|
-
const root = home ?? (process.env.HOME ?? process.cwd());
|
|
1586
|
-
return resolve(root, ...CLI_SETTINGS_RELATIVE);
|
|
1587
|
-
}
|
|
1588
|
-
function uniqueStrings(value) {
|
|
1589
|
-
if (!Array.isArray(value)) return [];
|
|
1590
|
-
const result = [];
|
|
1591
|
-
for (const item of value) {
|
|
1592
|
-
if (typeof item === "string" && item.length > 0 && !result.includes(item)) {
|
|
1593
|
-
result.push(item);
|
|
1594
|
-
}
|
|
1595
|
-
}
|
|
1596
|
-
return result;
|
|
1597
|
-
}
|
|
1598
|
-
function loadCliSettings(home) {
|
|
1599
|
-
try {
|
|
1600
|
-
const raw = JSON.parse(readFileSync(resolveCliSettingsPath(home), "utf-8"));
|
|
1601
|
-
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
|
|
1602
|
-
return { enabled: [] };
|
|
1603
|
-
}
|
|
1604
|
-
return { enabled: uniqueStrings(raw.enabled) };
|
|
1605
|
-
} catch {
|
|
1606
|
-
return { enabled: [] };
|
|
1607
|
-
}
|
|
1608
|
-
}
|
|
1609
|
-
function saveCliSettings(settings, home) {
|
|
1610
|
-
const path = resolveCliSettingsPath(home);
|
|
1611
|
-
const temporary = `${path}.tmp`;
|
|
1612
|
-
try {
|
|
1613
|
-
mkdirSync(join(path, ".."), { recursive: true });
|
|
1614
|
-
writeFileSync(temporary, JSON.stringify({ enabled: uniqueStrings(settings.enabled) }, null, 2) + "\n", "utf-8");
|
|
1615
|
-
renameSync(temporary, path);
|
|
1616
|
-
} catch (error) {
|
|
1617
|
-
throw new Error(`settings write failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1618
|
-
}
|
|
1619
|
-
return path;
|
|
1620
|
-
}
|
|
1621
|
-
|
|
1622
1816
|
// src/utils/cliTargets.ts
|
|
1623
1817
|
import {
|
|
1624
1818
|
cpSync,
|
|
1625
1819
|
existsSync as existsSync2,
|
|
1626
|
-
mkdirSync as
|
|
1627
|
-
readFileSync as
|
|
1820
|
+
mkdirSync as mkdirSync3,
|
|
1821
|
+
readFileSync as readFileSync3,
|
|
1628
1822
|
readdirSync,
|
|
1629
1823
|
renameSync as renameSync2,
|
|
1630
1824
|
rmSync,
|
|
1631
1825
|
statSync,
|
|
1632
|
-
writeFileSync as
|
|
1826
|
+
writeFileSync as writeFileSync3
|
|
1633
1827
|
} from "fs";
|
|
1634
|
-
import { join as
|
|
1828
|
+
import { join as join3, relative } from "path";
|
|
1635
1829
|
function homeRoot(home) {
|
|
1636
1830
|
return home ?? (process.env.HOME ?? process.cwd());
|
|
1637
1831
|
}
|
|
1638
1832
|
var PI_TARGET = {
|
|
1639
1833
|
id: "pi",
|
|
1640
1834
|
configFormat: "json",
|
|
1641
|
-
mcpConfigPath: (home) =>
|
|
1642
|
-
skillsDir: (home) =>
|
|
1835
|
+
mcpConfigPath: (home) => join3(homeRoot(home), ".pi", "agent", "mcp.json"),
|
|
1836
|
+
skillsDir: (home) => join3(homeRoot(home), ".pi", "agent", "skills")
|
|
1643
1837
|
};
|
|
1644
1838
|
var OPENCLAW_TARGET = {
|
|
1645
1839
|
id: "openclaw",
|
|
1646
1840
|
configFormat: "json",
|
|
1647
|
-
mcpConfigPath: (home) =>
|
|
1648
|
-
skillsDir: (home) =>
|
|
1841
|
+
mcpConfigPath: (home) => join3(homeRoot(home), ".openclaw", "openclaw.json"),
|
|
1842
|
+
skillsDir: (home) => join3(homeRoot(home), ".openclaw", "workspace", "skills")
|
|
1649
1843
|
};
|
|
1650
1844
|
var CODEX_TARGET = {
|
|
1651
1845
|
id: "codex",
|
|
1652
1846
|
configFormat: "toml",
|
|
1653
|
-
mcpConfigPath: (home) =>
|
|
1654
|
-
skillsDir: (home) =>
|
|
1847
|
+
mcpConfigPath: (home) => join3(homeRoot(home), ".codex", "config.toml"),
|
|
1848
|
+
skillsDir: (home) => join3(homeRoot(home), ".codex", "skills")
|
|
1655
1849
|
};
|
|
1656
1850
|
var ANTIGRAVITY_TARGET = {
|
|
1657
1851
|
id: "antigravity",
|
|
1658
1852
|
configFormat: "json",
|
|
1659
|
-
mcpConfigPath: (home) =>
|
|
1660
|
-
skillsDir: (home) =>
|
|
1853
|
+
mcpConfigPath: (home) => join3(homeRoot(home), ".gemini", "settings.json"),
|
|
1854
|
+
skillsDir: (home) => join3(homeRoot(home), ".gemini", "config", "skills")
|
|
1661
1855
|
};
|
|
1662
1856
|
var CLAUDE_TARGET = {
|
|
1663
1857
|
id: "claude",
|
|
1664
1858
|
configFormat: "json",
|
|
1665
|
-
mcpConfigPath: (home) =>
|
|
1666
|
-
skillsDir: (home) =>
|
|
1859
|
+
mcpConfigPath: (home) => join3(homeRoot(home), ".claude.json"),
|
|
1860
|
+
skillsDir: (home) => join3(homeRoot(home), ".claude", "skills")
|
|
1667
1861
|
};
|
|
1668
1862
|
var CLI_TARGETS = {
|
|
1669
1863
|
pi: PI_TARGET,
|
|
@@ -1680,7 +1874,7 @@ function supportedCliNames() {
|
|
|
1680
1874
|
}
|
|
1681
1875
|
function readJsonConfig(configPath) {
|
|
1682
1876
|
if (!existsSync2(configPath)) return {};
|
|
1683
|
-
const parsed = JSON.parse(
|
|
1877
|
+
const parsed = JSON.parse(readFileSync3(configPath, "utf-8"));
|
|
1684
1878
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
1685
1879
|
throw new Error(`configuration is not a JSON object: ${configPath}`);
|
|
1686
1880
|
}
|
|
@@ -1688,8 +1882,8 @@ function readJsonConfig(configPath) {
|
|
|
1688
1882
|
}
|
|
1689
1883
|
function writeJsonConfig(configPath, value) {
|
|
1690
1884
|
const temporary = `${configPath}.tmp`;
|
|
1691
|
-
|
|
1692
|
-
|
|
1885
|
+
mkdirSync3(join3(configPath, ".."), { recursive: true });
|
|
1886
|
+
writeFileSync3(temporary, JSON.stringify(value, null, 2) + "\n", "utf-8");
|
|
1693
1887
|
renameSync2(temporary, configPath);
|
|
1694
1888
|
}
|
|
1695
1889
|
function writeJsonMcpServerEntry(configPath, serverName, entry) {
|
|
@@ -1776,35 +1970,35 @@ function registrationToTomlLines(entry) {
|
|
|
1776
1970
|
return lines;
|
|
1777
1971
|
}
|
|
1778
1972
|
function writeTomlMcpServerSection(configPath, serverName, entry) {
|
|
1779
|
-
const previous = existsSync2(configPath) ?
|
|
1973
|
+
const previous = existsSync2(configPath) ? readFileSync3(configPath, "utf-8") : "";
|
|
1780
1974
|
const base = removeTomlSection(previous, serverName);
|
|
1781
1975
|
const table = [tomlSectionHeader(serverName), ...registrationToTomlLines(entry), ""].join("\n");
|
|
1782
1976
|
const content = base.length > 0 ? base.replace(/\n*$/, "\n") + table : table;
|
|
1783
1977
|
const temporary = `${configPath}.tmp`;
|
|
1784
|
-
|
|
1785
|
-
|
|
1978
|
+
mkdirSync3(join3(configPath, ".."), { recursive: true });
|
|
1979
|
+
writeFileSync3(temporary, content, "utf-8");
|
|
1786
1980
|
renameSync2(temporary, configPath);
|
|
1787
1981
|
}
|
|
1788
1982
|
function removeTomlMcpServerSection(configPath, serverName) {
|
|
1789
1983
|
if (!existsSync2(configPath)) return false;
|
|
1790
|
-
const previous =
|
|
1984
|
+
const previous = readFileSync3(configPath, "utf-8");
|
|
1791
1985
|
if (!hasTomlSection(previous, serverName)) return false;
|
|
1792
1986
|
const content = removeTomlSection(previous, serverName);
|
|
1793
1987
|
const temporary = `${configPath}.tmp`;
|
|
1794
|
-
|
|
1988
|
+
writeFileSync3(temporary, content, "utf-8");
|
|
1795
1989
|
renameSync2(temporary, configPath);
|
|
1796
1990
|
return true;
|
|
1797
1991
|
}
|
|
1798
1992
|
function hasTomlMcpServerSection(configPath, serverName) {
|
|
1799
1993
|
if (!existsSync2(configPath)) return false;
|
|
1800
|
-
return hasTomlSection(
|
|
1994
|
+
return hasTomlSection(readFileSync3(configPath, "utf-8"), serverName);
|
|
1801
1995
|
}
|
|
1802
1996
|
function listFilesRecursive(root) {
|
|
1803
1997
|
if (!existsSync2(root)) return [];
|
|
1804
1998
|
const files = [];
|
|
1805
1999
|
const walk = (dir) => {
|
|
1806
2000
|
for (const entry of readdirSync(dir)) {
|
|
1807
|
-
const full =
|
|
2001
|
+
const full = join3(dir, entry);
|
|
1808
2002
|
if (statSync(full).isDirectory()) {
|
|
1809
2003
|
walk(full);
|
|
1810
2004
|
} else {
|
|
@@ -1816,14 +2010,14 @@ function listFilesRecursive(root) {
|
|
|
1816
2010
|
return files;
|
|
1817
2011
|
}
|
|
1818
2012
|
function syncSkillTree(source, destination) {
|
|
1819
|
-
|
|
2013
|
+
mkdirSync3(destination, { recursive: true });
|
|
1820
2014
|
cpSync(source, destination, { recursive: true, force: true });
|
|
1821
2015
|
const expected = new Set(listFilesRecursive(source));
|
|
1822
2016
|
const existing = listFilesRecursive(destination);
|
|
1823
2017
|
existing.sort((a, b) => b.length - a.length);
|
|
1824
2018
|
for (const rel of existing) {
|
|
1825
2019
|
if (expected.has(rel)) continue;
|
|
1826
|
-
rmSync(
|
|
2020
|
+
rmSync(join3(destination, rel), { recursive: true, force: true });
|
|
1827
2021
|
}
|
|
1828
2022
|
}
|
|
1829
2023
|
function removeSkillDir(skillDir) {
|
|
@@ -1834,9 +2028,9 @@ function compareSkillTree(source, destination) {
|
|
|
1834
2028
|
if (packaged.length === 0) return "MISSING";
|
|
1835
2029
|
if (!existsSync2(destination)) return "MISSING";
|
|
1836
2030
|
for (const rel of packaged) {
|
|
1837
|
-
const target =
|
|
2031
|
+
const target = join3(destination, ...rel.split("/"));
|
|
1838
2032
|
if (!existsSync2(target)) return "MISSING";
|
|
1839
|
-
if (
|
|
2033
|
+
if (readFileSync3(target).toString("utf-8") !== readFileSync3(join3(source, ...rel.split("/"))).toString("utf-8")) {
|
|
1840
2034
|
return "STALE";
|
|
1841
2035
|
}
|
|
1842
2036
|
}
|
|
@@ -1867,8 +2061,8 @@ function applyServerRegistration(target, serverName, entry, home) {
|
|
|
1867
2061
|
}
|
|
1868
2062
|
|
|
1869
2063
|
// src/utils/cliInstaller.ts
|
|
1870
|
-
import { existsSync as existsSync3, readFileSync as
|
|
1871
|
-
import { join as
|
|
2064
|
+
import { existsSync as existsSync3, readFileSync as readFileSync4 } from "fs";
|
|
2065
|
+
import { join as join4, resolve as resolve3 } from "path";
|
|
1872
2066
|
function buildServerRegistration(apiKey) {
|
|
1873
2067
|
const env = {};
|
|
1874
2068
|
if (apiKey !== void 0 && apiKey.length > 0) {
|
|
@@ -1884,13 +2078,13 @@ function resolveSkillsResourcesRoot(packageRoot) {
|
|
|
1884
2078
|
return candidate;
|
|
1885
2079
|
}
|
|
1886
2080
|
function loadSkillsManifest(resourcesRoot) {
|
|
1887
|
-
const manifestPath =
|
|
2081
|
+
const manifestPath = join4(resourcesRoot, "manifest.json");
|
|
1888
2082
|
if (!existsSync3(manifestPath)) {
|
|
1889
2083
|
throw new Error(`skills manifest not found: ${manifestPath}`);
|
|
1890
2084
|
}
|
|
1891
2085
|
let parsed;
|
|
1892
2086
|
try {
|
|
1893
|
-
parsed = JSON.parse(
|
|
2087
|
+
parsed = JSON.parse(readFileSync4(manifestPath, "utf-8"));
|
|
1894
2088
|
} catch (error) {
|
|
1895
2089
|
throw new Error(`skills manifest is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
1896
2090
|
}
|
|
@@ -1903,8 +2097,8 @@ function loadSkillsManifest(resourcesRoot) {
|
|
|
1903
2097
|
if (typeof skill.id !== "string" || typeof skill.path !== "string") {
|
|
1904
2098
|
throw new Error(`skills manifest entry is missing id or path: ${manifestPath}`);
|
|
1905
2099
|
}
|
|
1906
|
-
if (!existsSync3(
|
|
1907
|
-
throw new Error(`manifest skill file not found: ${
|
|
2100
|
+
if (!existsSync3(join4(resourcesRoot, skill.path))) {
|
|
2101
|
+
throw new Error(`manifest skill file not found: ${join4(resourcesRoot, skill.path)}`);
|
|
1908
2102
|
}
|
|
1909
2103
|
return {
|
|
1910
2104
|
id: skill.id,
|
|
@@ -1929,7 +2123,7 @@ function installEnabledCliResources(options) {
|
|
|
1929
2123
|
applyServerRegistration(target, MCP_SERVER_NAME, entry, options.home);
|
|
1930
2124
|
let skills = 0;
|
|
1931
2125
|
for (const skill of manifest) {
|
|
1932
|
-
syncSkillTree(
|
|
2126
|
+
syncSkillTree(join4(options.resourcesRoot, skill.id), join4(target.skillsDir(options.home), skill.id));
|
|
1933
2127
|
skills += 1;
|
|
1934
2128
|
}
|
|
1935
2129
|
result.summaries.push({ cli: target.id, server: 1, skills });
|
|
@@ -1952,7 +2146,7 @@ function uninstallEnabledCliResources(options) {
|
|
|
1952
2146
|
applyServerRegistration(target, MCP_SERVER_NAME, void 0, options.home);
|
|
1953
2147
|
let skills = 0;
|
|
1954
2148
|
for (const skill of manifest) {
|
|
1955
|
-
removeSkillDir(
|
|
2149
|
+
removeSkillDir(join4(target.skillsDir(options.home), skill.id));
|
|
1956
2150
|
skills += 1;
|
|
1957
2151
|
}
|
|
1958
2152
|
result.summaries.push({ cli: target.id, server: 0, skills });
|
|
@@ -1964,7 +2158,7 @@ function uninstallEnabledCliResources(options) {
|
|
|
1964
2158
|
}
|
|
1965
2159
|
|
|
1966
2160
|
// src/utils/cliShow.ts
|
|
1967
|
-
import { join as
|
|
2161
|
+
import { join as join5 } from "path";
|
|
1968
2162
|
import { relative as relative2, resolve as resolve4 } from "path";
|
|
1969
2163
|
var MAX_CELL_WIDTH = 48;
|
|
1970
2164
|
var STATUS_OK = "OK";
|
|
@@ -2051,7 +2245,7 @@ function buildInstallRows(home, resourcesRoot) {
|
|
|
2051
2245
|
skill.id,
|
|
2052
2246
|
target.id,
|
|
2053
2247
|
"yes",
|
|
2054
|
-
compareSkillTree(
|
|
2248
|
+
compareSkillTree(join5(resourcesRoot, skill.id), join5(target.skillsDir(home), skill.id))
|
|
2055
2249
|
]);
|
|
2056
2250
|
}
|
|
2057
2251
|
}
|
|
@@ -2069,7 +2263,7 @@ function buildCliShowReport(options) {
|
|
|
2069
2263
|
|
|
2070
2264
|
// src/cli.ts
|
|
2071
2265
|
var __filename = fileURLToPath(import.meta.url);
|
|
2072
|
-
var __dirname =
|
|
2266
|
+
var __dirname = dirname2(__filename);
|
|
2073
2267
|
function extractEnvFileOption(argv) {
|
|
2074
2268
|
for (let i = 0; i < argv.length; i++) {
|
|
2075
2269
|
const arg = argv[i];
|
|
@@ -2466,8 +2660,8 @@ async function runDoctor(packageVersion, apiKey, live = false, runner = execTool
|
|
|
2466
2660
|
async function runSetup(options, deps = {}) {
|
|
2467
2661
|
const fsImpl = deps.fs ?? {
|
|
2468
2662
|
existsSync: (path) => existsSync4(path),
|
|
2469
|
-
readFileSync: (path, encoding) =>
|
|
2470
|
-
writeFileSync: (path, data, encoding) =>
|
|
2663
|
+
readFileSync: (path, encoding) => readFileSync5(path, encoding),
|
|
2664
|
+
writeFileSync: (path, data, encoding) => writeFileSync4(path, data, encoding)
|
|
2471
2665
|
};
|
|
2472
2666
|
const nodeVersion = deps.nodeVersion ?? process.versions.node;
|
|
2473
2667
|
const nodeMajor = Number.parseInt(nodeVersion.split(".")[0] ?? "0", 10);
|
|
@@ -2625,402 +2819,413 @@ if (isRunDirectly || isMainModule) {
|
|
|
2625
2819
|
let packageVersion = "0.0.0";
|
|
2626
2820
|
try {
|
|
2627
2821
|
const packageJsonPath = resolve5(__dirname, "../package.json");
|
|
2628
|
-
const packageJson = JSON.parse(
|
|
2822
|
+
const packageJson = JSON.parse(readFileSync5(packageJsonPath, "utf-8"));
|
|
2629
2823
|
packageVersion = packageJson.version;
|
|
2630
2824
|
} catch (e) {
|
|
2631
2825
|
packageVersion = "0.0.0";
|
|
2632
2826
|
}
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
["$0 doctor --live --probes geocoding,routes", "Probe only the selected APIs"]
|
|
2652
|
-
]),
|
|
2653
|
-
async (argv) => {
|
|
2654
|
-
const parsedProbes = parseDoctorProbes(argv.probes);
|
|
2655
|
-
if (parsedProbes.error) {
|
|
2656
|
-
process.stderr.write(JSON.stringify({ error: parsedProbes.error }, null, 2) + "\n");
|
|
2657
|
-
process.exitCode = 1;
|
|
2658
|
-
return;
|
|
2659
|
-
}
|
|
2660
|
-
const report = await runDoctor(packageVersion, argv.apikey, argv.live, void 0, {
|
|
2661
|
-
probes: parsedProbes.probes,
|
|
2662
|
-
config: resolveRuntimeConfig(process.env, { envFile: extractEnvFileOption(process.argv) })
|
|
2663
|
-
});
|
|
2664
|
-
process.stdout.write(JSON.stringify(report, null, 2) + "\n");
|
|
2665
|
-
process.exitCode = report.success ? 0 : 1;
|
|
2666
|
-
}
|
|
2667
|
-
).command(
|
|
2668
|
-
"setup",
|
|
2669
|
-
"Non-interactive onboarding: verify local readiness, optionally scaffold .env",
|
|
2670
|
-
(yargs2) => yargs2.option("write-env", {
|
|
2671
|
-
type: "boolean",
|
|
2672
|
-
default: false,
|
|
2673
|
-
description: "Create .env from .env.example in --env-dir when it does not exist (never overwrites)"
|
|
2674
|
-
}).option("env-dir", {
|
|
2675
|
-
type: "string",
|
|
2676
|
-
default: process.cwd(),
|
|
2677
|
-
description: "Directory where .env is looked up / created"
|
|
2678
|
-
}).example([
|
|
2679
|
-
["$0 setup", "Check readiness without writing anything"],
|
|
2680
|
-
["$0 setup --write-env", "Also scaffold .env from the package template"]
|
|
2681
|
-
]),
|
|
2682
|
-
async (argv) => {
|
|
2683
|
-
const report = await runSetup({
|
|
2684
|
-
packageDir: resolve5(__dirname, ".."),
|
|
2685
|
-
targetDir: argv.envDir,
|
|
2686
|
-
writeEnv: argv.writeEnv
|
|
2687
|
-
});
|
|
2688
|
-
process.stdout.write(JSON.stringify(report, null, 2) + "\n");
|
|
2689
|
-
process.exitCode = report.success ? 0 : 1;
|
|
2690
|
-
}
|
|
2691
|
-
).command(
|
|
2692
|
-
"snippet",
|
|
2693
|
-
"Print an MCP client registration snippet (JSON on stdout; never writes files)",
|
|
2694
|
-
(yargs2) => yargs2.option("client", {
|
|
2695
|
-
type: "string",
|
|
2696
|
-
choices: ["generic", "claude", "cursor", "pi"],
|
|
2697
|
-
default: "generic",
|
|
2698
|
-
description: "Target client (selects the documented target file)"
|
|
2699
|
-
}).option("transport", {
|
|
2700
|
-
type: "string",
|
|
2701
|
-
choices: ["stdio", "http"],
|
|
2702
|
-
default: "stdio",
|
|
2703
|
-
description: "MCP transport of the snippet"
|
|
2704
|
-
}).option("launcher", {
|
|
2705
|
-
type: "string",
|
|
2706
|
-
choices: ["npx", "node", "bin"],
|
|
2707
|
-
default: "npx",
|
|
2708
|
-
description: "stdio launcher command"
|
|
2709
|
-
}).option("command", {
|
|
2710
|
-
type: "string",
|
|
2711
|
-
description: "Absolute path to dist/cli.js (required for --launcher node)"
|
|
2712
|
-
}).option("host", {
|
|
2713
|
-
type: "string",
|
|
2714
|
-
default: "127.0.0.1",
|
|
2715
|
-
description: "Host used in the URL (transport http)"
|
|
2716
|
-
}).option("port", {
|
|
2717
|
-
type: "number",
|
|
2718
|
-
default: 3e3,
|
|
2719
|
-
description: "Port used in the URL (transport http)"
|
|
2720
|
-
}).option("env-key", {
|
|
2721
|
-
type: "string",
|
|
2722
|
-
description: "Emit a ${NAME} variable reference instead of a literal placeholder (expansion is client-specific)"
|
|
2723
|
-
}).option("name", {
|
|
2724
|
-
type: "string",
|
|
2725
|
-
default: "mcp-google-map",
|
|
2726
|
-
description: "Server name inside mcpServers"
|
|
2727
|
-
}).example([
|
|
2728
|
-
["$0 snippet --client claude", "stdio snippet using the npx launcher"],
|
|
2729
|
-
["$0 snippet --client pi --launcher node --command /abs/path/dist/cli.js", "stdio snippet with an explicit artifact"],
|
|
2730
|
-
["$0 snippet --transport http --port 3100", "HTTP URL snippet"]
|
|
2731
|
-
]),
|
|
2732
|
-
(argv) => {
|
|
2733
|
-
process.exitCode = runSnippetCommand({
|
|
2734
|
-
client: argv.client,
|
|
2735
|
-
transport: argv.transport,
|
|
2736
|
-
launcher: argv.launcher,
|
|
2737
|
-
commandPath: argv.command,
|
|
2738
|
-
host: argv.host,
|
|
2739
|
-
port: argv.port,
|
|
2740
|
-
envKey: argv.envKey,
|
|
2741
|
-
serverName: argv.name
|
|
2742
|
-
});
|
|
2743
|
-
}
|
|
2744
|
-
).command(
|
|
2745
|
-
"enable <cli>",
|
|
2746
|
-
"Enable MCP-server and skill installation for a supported CLI",
|
|
2747
|
-
(yargs2) => yargs2.positional("cli", {
|
|
2748
|
-
type: "string",
|
|
2749
|
-
describe: `CLI name: ${supportedCliNames()}`
|
|
2750
|
-
}).option("home", {
|
|
2751
|
-
type: "string",
|
|
2752
|
-
description: "Home-directory override for the settings file (default: user home)"
|
|
2753
|
-
}).example([["$0 enable pi", "Enable pi for install/uninstall/show"]]),
|
|
2754
|
-
(argv) => {
|
|
2755
|
-
process.exitCode = runEnableDisableCliCommand(argv.cli, true, {
|
|
2756
|
-
home: argv.home
|
|
2757
|
-
});
|
|
2758
|
-
}
|
|
2759
|
-
).command(
|
|
2760
|
-
"disable <cli>",
|
|
2761
|
-
"Disable MCP-server and skill installation for a supported CLI",
|
|
2762
|
-
(yargs2) => yargs2.positional("cli", {
|
|
2763
|
-
type: "string",
|
|
2764
|
-
describe: `CLI name: ${supportedCliNames()}`
|
|
2765
|
-
}).option("home", {
|
|
2766
|
-
type: "string",
|
|
2767
|
-
description: "Home-directory override for the settings file (default: user home)"
|
|
2768
|
-
}).example([["$0 disable claude", "Disable claude for install/uninstall/show"]]),
|
|
2769
|
-
(argv) => {
|
|
2770
|
-
process.exitCode = runEnableDisableCliCommand(argv.cli, false, {
|
|
2771
|
-
home: argv.home
|
|
2772
|
-
});
|
|
2773
|
-
}
|
|
2774
|
-
).command(
|
|
2775
|
-
"install",
|
|
2776
|
-
"Register the mcp-google-map stdio server and install packaged skills for every enabled CLI",
|
|
2777
|
-
(yargs2) => yargs2.option("home", {
|
|
2778
|
-
type: "string",
|
|
2779
|
-
description: "Home-directory override for CLI configuration paths (default: user home)"
|
|
2780
|
-
}).option("apikey", {
|
|
2781
|
-
alias: "k",
|
|
2782
|
-
type: "string",
|
|
2783
|
-
description: "Google Maps API key embedded in the registered entry (prefer GOOGLE_MAPS_API_KEY)",
|
|
2784
|
-
default: resolveRuntimeConfig(process.env, { envFile: extractEnvFileOption(process.argv) }).apiKey
|
|
2785
|
-
}).example([["$0 install", "Register the server and skills in every enabled CLI"]]),
|
|
2786
|
-
(argv) => {
|
|
2787
|
-
process.exitCode = runInstallUninstallCommand("install", {
|
|
2788
|
-
home: argv.home,
|
|
2789
|
-
apiKey: argv.apikey,
|
|
2790
|
-
resourcesRoot: resolveSkillsResourcesRoot(resolve5(__dirname, ".."))
|
|
2791
|
-
});
|
|
2792
|
-
}
|
|
2793
|
-
).command(
|
|
2794
|
-
"uninstall",
|
|
2795
|
-
"Remove the mcp-google-map server entry and managed skills from every enabled CLI",
|
|
2796
|
-
(yargs2) => yargs2.option("home", {
|
|
2797
|
-
type: "string",
|
|
2798
|
-
description: "Home-directory override for CLI configuration paths (default: user home)"
|
|
2799
|
-
}).example([["$0 uninstall", "Reciprocal removal of install for every enabled CLI"]]),
|
|
2800
|
-
(argv) => {
|
|
2801
|
-
process.exitCode = runInstallUninstallCommand("uninstall", {
|
|
2802
|
-
home: argv.home,
|
|
2803
|
-
resourcesRoot: resolveSkillsResourcesRoot(resolve5(__dirname, ".."))
|
|
2804
|
-
});
|
|
2805
|
-
}
|
|
2806
|
-
).command(
|
|
2807
|
-
"show",
|
|
2808
|
-
"Show CLI configuration and installation status tables (human-readable)",
|
|
2809
|
-
(yargs2) => yargs2.option("home", {
|
|
2810
|
-
type: "string",
|
|
2811
|
-
description: "Home-directory override for CLI configuration paths (default: user home)"
|
|
2812
|
-
}).example([["$0 show", "Print CLI Configuration and Installation Status tables"]]),
|
|
2813
|
-
(argv) => {
|
|
2814
|
-
process.exitCode = runShowCommand({
|
|
2815
|
-
home: argv.home,
|
|
2816
|
-
resourcesRoot: resolveSkillsResourcesRoot(resolve5(__dirname, ".."))
|
|
2817
|
-
});
|
|
2818
|
-
}
|
|
2819
|
-
).command(
|
|
2820
|
-
"exec <tool> [params]",
|
|
2821
|
-
"Execute a tool directly and output JSON",
|
|
2822
|
-
(yargs2) => {
|
|
2823
|
-
return yargs2.positional("tool", {
|
|
2824
|
-
type: "string",
|
|
2825
|
-
describe: `Tool name: ${EXEC_TOOLS.join(", ")}`
|
|
2826
|
-
}).positional("params", {
|
|
2827
|
+
const managementCommand = parseManagementCommand(hideBin(process.argv));
|
|
2828
|
+
if (managementCommand !== void 0) {
|
|
2829
|
+
void runManagementCommand(managementCommand, { currentVersion: packageVersion }).then((code) => {
|
|
2830
|
+
process.exitCode = code;
|
|
2831
|
+
}).catch((error) => {
|
|
2832
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2833
|
+
process.stderr.write(JSON.stringify({ error: message }, null, 2) + "\n");
|
|
2834
|
+
process.exitCode = 1;
|
|
2835
|
+
});
|
|
2836
|
+
} else {
|
|
2837
|
+
yargs(hideBin(process.argv)).command(
|
|
2838
|
+
"doctor",
|
|
2839
|
+
"Check standalone CLI readiness without starting an MCP server",
|
|
2840
|
+
(yargs2) => yargs2.option("live", {
|
|
2841
|
+
type: "boolean",
|
|
2842
|
+
default: false,
|
|
2843
|
+
description: "Make billable test requests to Geocoding, Places (New), and Routes APIs"
|
|
2844
|
+
}).option("probes", {
|
|
2827
2845
|
type: "string",
|
|
2828
|
-
|
|
2846
|
+
description: "Comma-separated subset of live probes: geocoding, places-new, routes (default: all three)"
|
|
2829
2847
|
}).option("apikey", {
|
|
2830
2848
|
alias: "k",
|
|
2831
2849
|
type: "string",
|
|
2832
|
-
description: "Google Maps API key",
|
|
2850
|
+
description: "Google Maps API key (prefer GOOGLE_MAPS_API_KEY to avoid shell history exposure)",
|
|
2833
2851
|
default: process.env.GOOGLE_MAPS_API_KEY
|
|
2834
2852
|
}).example([
|
|
2835
|
-
[
|
|
2836
|
-
[
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
) + "\n"
|
|
2853
|
-
);
|
|
2854
|
-
process.exitCode = 1;
|
|
2855
|
-
return;
|
|
2853
|
+
["$0 doctor", "Check local runtime and API key configuration without network calls"],
|
|
2854
|
+
["$0 doctor --live", "Also verify three Google Maps APIs with billable requests"],
|
|
2855
|
+
["$0 doctor --live --probes geocoding,routes", "Probe only the selected APIs"]
|
|
2856
|
+
]),
|
|
2857
|
+
async (argv) => {
|
|
2858
|
+
const parsedProbes = parseDoctorProbes(argv.probes);
|
|
2859
|
+
if (parsedProbes.error) {
|
|
2860
|
+
process.stderr.write(JSON.stringify({ error: parsedProbes.error }, null, 2) + "\n");
|
|
2861
|
+
process.exitCode = 1;
|
|
2862
|
+
return;
|
|
2863
|
+
}
|
|
2864
|
+
const report = await runDoctor(packageVersion, argv.apikey, argv.live, void 0, {
|
|
2865
|
+
probes: parsedProbes.probes,
|
|
2866
|
+
config: resolveRuntimeConfig(process.env, { envFile: extractEnvFileOption(process.argv) })
|
|
2867
|
+
});
|
|
2868
|
+
process.stdout.write(JSON.stringify(report, null, 2) + "\n");
|
|
2869
|
+
process.exitCode = report.success ? 0 : 1;
|
|
2856
2870
|
}
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2871
|
+
).command(
|
|
2872
|
+
"setup",
|
|
2873
|
+
"Non-interactive onboarding: verify local readiness, optionally scaffold .env",
|
|
2874
|
+
(yargs2) => yargs2.option("write-env", {
|
|
2875
|
+
type: "boolean",
|
|
2876
|
+
default: false,
|
|
2877
|
+
description: "Create .env from .env.example in --env-dir when it does not exist (never overwrites)"
|
|
2878
|
+
}).option("env-dir", {
|
|
2879
|
+
type: "string",
|
|
2880
|
+
default: process.cwd(),
|
|
2881
|
+
description: "Directory where .env is looked up / created"
|
|
2882
|
+
}).example([
|
|
2883
|
+
["$0 setup", "Check readiness without writing anything"],
|
|
2884
|
+
["$0 setup --write-env", "Also scaffold .env from the package template"]
|
|
2885
|
+
]),
|
|
2886
|
+
async (argv) => {
|
|
2887
|
+
const report = await runSetup({
|
|
2888
|
+
packageDir: resolve5(__dirname, ".."),
|
|
2889
|
+
targetDir: argv.envDir,
|
|
2890
|
+
writeEnv: argv.writeEnv
|
|
2891
|
+
});
|
|
2892
|
+
process.stdout.write(JSON.stringify(report, null, 2) + "\n");
|
|
2893
|
+
process.exitCode = report.success ? 0 : 1;
|
|
2863
2894
|
}
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2895
|
+
).command(
|
|
2896
|
+
"snippet",
|
|
2897
|
+
"Print an MCP client registration snippet (JSON on stdout; never writes files)",
|
|
2898
|
+
(yargs2) => yargs2.option("client", {
|
|
2899
|
+
type: "string",
|
|
2900
|
+
choices: ["generic", "claude", "cursor", "pi"],
|
|
2901
|
+
default: "generic",
|
|
2902
|
+
description: "Target client (selects the documented target file)"
|
|
2903
|
+
}).option("transport", {
|
|
2904
|
+
type: "string",
|
|
2905
|
+
choices: ["stdio", "http"],
|
|
2906
|
+
default: "stdio",
|
|
2907
|
+
description: "MCP transport of the snippet"
|
|
2908
|
+
}).option("launcher", {
|
|
2909
|
+
type: "string",
|
|
2910
|
+
choices: ["npx", "node", "bin"],
|
|
2911
|
+
default: "npx",
|
|
2912
|
+
description: "stdio launcher command"
|
|
2913
|
+
}).option("command", {
|
|
2871
2914
|
type: "string",
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
}).option("output", {
|
|
2875
|
-
alias: "o",
|
|
2915
|
+
description: "Absolute path to dist/cli.js (required for --launcher node)"
|
|
2916
|
+
}).option("host", {
|
|
2876
2917
|
type: "string",
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2918
|
+
default: "127.0.0.1",
|
|
2919
|
+
description: "Host used in the URL (transport http)"
|
|
2920
|
+
}).option("port", {
|
|
2880
2921
|
type: "number",
|
|
2881
|
-
|
|
2882
|
-
|
|
2922
|
+
default: 3e3,
|
|
2923
|
+
description: "Port used in the URL (transport http)"
|
|
2924
|
+
}).option("env-key", {
|
|
2925
|
+
type: "string",
|
|
2926
|
+
description: "Emit a ${NAME} variable reference instead of a literal placeholder (expansion is client-specific)"
|
|
2927
|
+
}).option("name", {
|
|
2928
|
+
type: "string",
|
|
2929
|
+
default: "mcp-google-map",
|
|
2930
|
+
description: "Server name inside mcpServers"
|
|
2931
|
+
}).example([
|
|
2932
|
+
["$0 snippet --client claude", "stdio snippet using the npx launcher"],
|
|
2933
|
+
["$0 snippet --client pi --launcher node --command /abs/path/dist/cli.js", "stdio snippet with an explicit artifact"],
|
|
2934
|
+
["$0 snippet --transport http --port 3100", "HTTP URL snippet"]
|
|
2935
|
+
]),
|
|
2936
|
+
(argv) => {
|
|
2937
|
+
process.exitCode = runSnippetCommand({
|
|
2938
|
+
client: argv.client,
|
|
2939
|
+
transport: argv.transport,
|
|
2940
|
+
launcher: argv.launcher,
|
|
2941
|
+
commandPath: argv.command,
|
|
2942
|
+
host: argv.host,
|
|
2943
|
+
port: argv.port,
|
|
2944
|
+
envKey: argv.envKey,
|
|
2945
|
+
serverName: argv.name
|
|
2946
|
+
});
|
|
2947
|
+
}
|
|
2948
|
+
).command(
|
|
2949
|
+
"enable <cli>",
|
|
2950
|
+
"Enable MCP-server and skill installation for a supported CLI",
|
|
2951
|
+
(yargs2) => yargs2.positional("cli", {
|
|
2952
|
+
type: "string",
|
|
2953
|
+
describe: `CLI name: ${supportedCliNames()}`
|
|
2954
|
+
}).option("home", {
|
|
2955
|
+
type: "string",
|
|
2956
|
+
description: "Home-directory override for the settings file (default: user home)"
|
|
2957
|
+
}).example([["$0 enable pi", "Enable pi for install/uninstall/show"]]),
|
|
2958
|
+
(argv) => {
|
|
2959
|
+
process.exitCode = runEnableDisableCliCommand(argv.cli, true, {
|
|
2960
|
+
home: argv.home
|
|
2961
|
+
});
|
|
2962
|
+
}
|
|
2963
|
+
).command(
|
|
2964
|
+
"disable <cli>",
|
|
2965
|
+
"Disable MCP-server and skill installation for a supported CLI",
|
|
2966
|
+
(yargs2) => yargs2.positional("cli", {
|
|
2967
|
+
type: "string",
|
|
2968
|
+
describe: `CLI name: ${supportedCliNames()}`
|
|
2969
|
+
}).option("home", {
|
|
2970
|
+
type: "string",
|
|
2971
|
+
description: "Home-directory override for the settings file (default: user home)"
|
|
2972
|
+
}).example([["$0 disable claude", "Disable claude for install/uninstall/show"]]),
|
|
2973
|
+
(argv) => {
|
|
2974
|
+
process.exitCode = runEnableDisableCliCommand(argv.cli, false, {
|
|
2975
|
+
home: argv.home
|
|
2976
|
+
});
|
|
2977
|
+
}
|
|
2978
|
+
).command(
|
|
2979
|
+
"install",
|
|
2980
|
+
"Register the mcp-google-map stdio server and install packaged skills for every enabled CLI",
|
|
2981
|
+
(yargs2) => yargs2.option("home", {
|
|
2982
|
+
type: "string",
|
|
2983
|
+
description: "Home-directory override for CLI configuration paths (default: user home)"
|
|
2883
2984
|
}).option("apikey", {
|
|
2884
2985
|
alias: "k",
|
|
2885
2986
|
type: "string",
|
|
2886
|
-
description: "Google Maps API key",
|
|
2887
|
-
default: process.env.
|
|
2888
|
-
}).example([
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
if (!argv.apikey) {
|
|
2896
|
-
console.error("Error: GOOGLE_MAPS_API_KEY not set. Use --apikey or set env var.");
|
|
2897
|
-
process.exit(1);
|
|
2987
|
+
description: "Google Maps API key embedded in the registered entry (prefer GOOGLE_MAPS_API_KEY)",
|
|
2988
|
+
default: resolveRuntimeConfig(process.env, { envFile: extractEnvFileOption(process.argv) }).apiKey
|
|
2989
|
+
}).example([["$0 install", "Register the server and skills in every enabled CLI"]]),
|
|
2990
|
+
(argv) => {
|
|
2991
|
+
process.exitCode = runInstallUninstallCommand("install", {
|
|
2992
|
+
home: argv.home,
|
|
2993
|
+
apiKey: argv.apikey,
|
|
2994
|
+
resourcesRoot: resolveSkillsResourcesRoot(resolve5(__dirname, ".."))
|
|
2995
|
+
});
|
|
2898
2996
|
}
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
}
|
|
2912
|
-
lines = readFileSync4(argv.input, "utf-8").split("\n").map((l) => l.trim()).filter((l) => l.length > 0);
|
|
2997
|
+
).command(
|
|
2998
|
+
"uninstall",
|
|
2999
|
+
"Remove the mcp-google-map server entry and managed skills from every enabled CLI",
|
|
3000
|
+
(yargs2) => yargs2.option("home", {
|
|
3001
|
+
type: "string",
|
|
3002
|
+
description: "Home-directory override for CLI configuration paths (default: user home)"
|
|
3003
|
+
}).example([["$0 uninstall", "Reciprocal removal of install for every enabled CLI"]]),
|
|
3004
|
+
(argv) => {
|
|
3005
|
+
process.exitCode = runInstallUninstallCommand("uninstall", {
|
|
3006
|
+
home: argv.home,
|
|
3007
|
+
resourcesRoot: resolveSkillsResourcesRoot(resolve5(__dirname, ".."))
|
|
3008
|
+
});
|
|
2913
3009
|
}
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
3010
|
+
).command(
|
|
3011
|
+
"show",
|
|
3012
|
+
"Show CLI configuration and installation status tables (human-readable)",
|
|
3013
|
+
(yargs2) => yargs2.option("home", {
|
|
3014
|
+
type: "string",
|
|
3015
|
+
description: "Home-directory override for CLI configuration paths (default: user home)"
|
|
3016
|
+
}).example([["$0 show", "Print CLI Configuration and Installation Status tables"]]),
|
|
3017
|
+
(argv) => {
|
|
3018
|
+
process.exitCode = runShowCommand({
|
|
3019
|
+
home: argv.home,
|
|
3020
|
+
resourcesRoot: resolveSkillsResourcesRoot(resolve5(__dirname, ".."))
|
|
3021
|
+
});
|
|
2917
3022
|
}
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
3023
|
+
).command(
|
|
3024
|
+
"exec <tool> [params]",
|
|
3025
|
+
"Execute a tool directly and output JSON",
|
|
3026
|
+
(yargs2) => {
|
|
3027
|
+
return yargs2.positional("tool", {
|
|
3028
|
+
type: "string",
|
|
3029
|
+
describe: `Tool name: ${EXEC_TOOLS.join(", ")}`
|
|
3030
|
+
}).positional("params", {
|
|
3031
|
+
type: "string",
|
|
3032
|
+
describe: "JSON parameters string"
|
|
3033
|
+
}).option("apikey", {
|
|
3034
|
+
alias: "k",
|
|
3035
|
+
type: "string",
|
|
3036
|
+
description: "Google Maps API key",
|
|
3037
|
+
default: process.env.GOOGLE_MAPS_API_KEY
|
|
3038
|
+
}).example([
|
|
3039
|
+
[`$0 exec geocode '{"address":"Tokyo Tower"}'`, "Geocode an address"],
|
|
3040
|
+
[
|
|
3041
|
+
`$0 exec search-nearby '{"center":{"value":"35.68,139.74","isCoordinates":true},"keyword":"restaurant"}'`,
|
|
3042
|
+
"Search nearby"
|
|
3043
|
+
],
|
|
3044
|
+
[`$0 exec search-places '{"query":"ramen in Tokyo"}'`, "Text search"]
|
|
3045
|
+
]);
|
|
3046
|
+
},
|
|
3047
|
+
async (argv) => {
|
|
3048
|
+
if (!argv.apikey) {
|
|
3049
|
+
process.stderr.write(
|
|
3050
|
+
JSON.stringify(
|
|
3051
|
+
{
|
|
3052
|
+
error: "GOOGLE_MAPS_API_KEY not set. Use --apikey or set GOOGLE_MAPS_API_KEY environment variable."
|
|
3053
|
+
},
|
|
3054
|
+
null,
|
|
3055
|
+
2
|
|
3056
|
+
) + "\n"
|
|
3057
|
+
);
|
|
3058
|
+
process.exitCode = 1;
|
|
3059
|
+
return;
|
|
2932
3060
|
}
|
|
2933
|
-
await Promise.all(executing);
|
|
2934
|
-
};
|
|
2935
|
-
const tasks = lines.map((address, index) => async () => {
|
|
2936
3061
|
try {
|
|
2937
|
-
const
|
|
2938
|
-
|
|
3062
|
+
const params = argv.params ? JSON.parse(argv.params) : {};
|
|
3063
|
+
process.exitCode = await runExecCommand(argv.tool, params, argv.apikey);
|
|
2939
3064
|
} catch (error) {
|
|
2940
|
-
|
|
3065
|
+
process.stderr.write(JSON.stringify({ error: error.message }, null, 2) + "\n");
|
|
3066
|
+
process.exitCode = 1;
|
|
2941
3067
|
}
|
|
2942
|
-
completed++;
|
|
2943
|
-
if (!argv.output) return;
|
|
2944
|
-
process.stderr.write(`\r ${completed}/${lines.length} geocoded`);
|
|
2945
|
-
});
|
|
2946
|
-
await semaphore(tasks, concurrency);
|
|
2947
|
-
if (argv.output) {
|
|
2948
|
-
process.stderr.write("\n");
|
|
2949
|
-
}
|
|
2950
|
-
const succeeded = results.filter((r) => r.success).length;
|
|
2951
|
-
const failed = results.filter((r) => !r.success).length;
|
|
2952
|
-
const summary = { total: lines.length, succeeded, failed, results };
|
|
2953
|
-
const json = JSON.stringify(summary, null, 2);
|
|
2954
|
-
if (argv.output) {
|
|
2955
|
-
writeFileSync3(argv.output, json, "utf-8");
|
|
2956
|
-
console.error(`Done: ${succeeded}/${lines.length} succeeded. Output: ${argv.output}`);
|
|
2957
|
-
} else {
|
|
2958
|
-
console.log(json);
|
|
2959
|
-
}
|
|
2960
|
-
process.exit(failed > 0 ? 1 : 0);
|
|
2961
|
-
}
|
|
2962
|
-
).command(
|
|
2963
|
-
"$0",
|
|
2964
|
-
"Start the MCP server (HTTP by default, --stdio for stdio mode)",
|
|
2965
|
-
(yargs2) => {
|
|
2966
|
-
return yargs2.option("port", {
|
|
2967
|
-
alias: "p",
|
|
2968
|
-
type: "number",
|
|
2969
|
-
description: "Port to run the MCP server on",
|
|
2970
|
-
default: process.env.MCP_SERVER_PORT ? parseInt(process.env.MCP_SERVER_PORT) : 3e3
|
|
2971
|
-
}).option("host", {
|
|
2972
|
-
type: "string",
|
|
2973
|
-
description: "Hostname to bind the server to (default 127.0.0.1 loopback; 0.0.0.0 for all interfaces)",
|
|
2974
|
-
default: process.env.MCP_SERVER_HOST || "127.0.0.1"
|
|
2975
|
-
}).option("apikey", {
|
|
2976
|
-
alias: "k",
|
|
2977
|
-
type: "string",
|
|
2978
|
-
description: "Google Maps API key",
|
|
2979
|
-
default: process.env.GOOGLE_MAPS_API_KEY
|
|
2980
|
-
}).option("stdio", {
|
|
2981
|
-
type: "boolean",
|
|
2982
|
-
description: "Use stdio transport instead of HTTP",
|
|
2983
|
-
default: false
|
|
2984
|
-
}).example([
|
|
2985
|
-
["$0", "Start HTTP server with default settings"],
|
|
2986
|
-
['$0 --port 3000 --apikey "your_api_key"', "Start HTTP with custom port and API key"],
|
|
2987
|
-
["$0 --host 0.0.0.0 --port 3000", "Start HTTP accessible from all interfaces"],
|
|
2988
|
-
["$0 --stdio", "Start in stdio mode (for Claude Desktop, Cursor, etc.)"]
|
|
2989
|
-
]);
|
|
2990
|
-
},
|
|
2991
|
-
async (argv) => {
|
|
2992
|
-
if (argv.apikey) {
|
|
2993
|
-
process.env.GOOGLE_MAPS_API_KEY = argv.apikey;
|
|
2994
3068
|
}
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3069
|
+
).command(
|
|
3070
|
+
"batch-geocode",
|
|
3071
|
+
"Geocode multiple addresses from a file (one address per line)",
|
|
3072
|
+
(yargs2) => {
|
|
3073
|
+
return yargs2.option("input", {
|
|
3074
|
+
alias: "i",
|
|
3075
|
+
type: "string",
|
|
3076
|
+
describe: "Input file path (one address per line). Use - for stdin.",
|
|
3077
|
+
demandOption: true
|
|
3078
|
+
}).option("output", {
|
|
3079
|
+
alias: "o",
|
|
3080
|
+
type: "string",
|
|
3081
|
+
describe: "Output file path (JSON). Defaults to stdout."
|
|
3082
|
+
}).option("concurrency", {
|
|
3083
|
+
alias: "c",
|
|
3084
|
+
type: "number",
|
|
3085
|
+
describe: "Max parallel requests",
|
|
3086
|
+
default: 20
|
|
3087
|
+
}).option("apikey", {
|
|
3088
|
+
alias: "k",
|
|
3089
|
+
type: "string",
|
|
3090
|
+
description: "Google Maps API key",
|
|
3091
|
+
default: process.env.GOOGLE_MAPS_API_KEY
|
|
3092
|
+
}).example([
|
|
3093
|
+
["$0 batch-geocode -i addresses.txt", "Geocode to stdout"],
|
|
3094
|
+
["$0 batch-geocode -i addresses.txt -o results.json", "Geocode to file"],
|
|
3095
|
+
["cat addresses.txt | $0 batch-geocode -i -", "Geocode from stdin"]
|
|
3096
|
+
]);
|
|
3097
|
+
},
|
|
3098
|
+
async (argv) => {
|
|
3004
3099
|
if (!argv.apikey) {
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
Logger.log("");
|
|
3100
|
+
console.error("Error: GOOGLE_MAPS_API_KEY not set. Use --apikey or set env var.");
|
|
3101
|
+
process.exit(1);
|
|
3008
3102
|
}
|
|
3009
|
-
|
|
3010
|
-
|
|
3103
|
+
let lines;
|
|
3104
|
+
if (argv.input === "-") {
|
|
3105
|
+
const rl = createInterface({ input: process.stdin });
|
|
3106
|
+
lines = [];
|
|
3107
|
+
for await (const line of rl) {
|
|
3108
|
+
const trimmed = line.trim();
|
|
3109
|
+
if (trimmed) lines.push(trimmed);
|
|
3110
|
+
}
|
|
3111
|
+
} else {
|
|
3112
|
+
if (!existsSync4(argv.input)) {
|
|
3113
|
+
console.error(`Error: File not found: ${argv.input}`);
|
|
3011
3114
|
process.exit(1);
|
|
3012
3115
|
}
|
|
3013
|
-
|
|
3014
|
-
|
|
3116
|
+
lines = readFileSync5(argv.input, "utf-8").split("\n").map((l) => l.trim()).filter((l) => l.length > 0);
|
|
3117
|
+
}
|
|
3118
|
+
if (lines.length === 0) {
|
|
3119
|
+
console.error("Error: No addresses found in input.");
|
|
3015
3120
|
process.exit(1);
|
|
3121
|
+
}
|
|
3122
|
+
const searcher = new PlacesSearcher(argv.apikey);
|
|
3123
|
+
const concurrency = Math.min(Math.max(argv.concurrency, 1), 50);
|
|
3124
|
+
const results = [];
|
|
3125
|
+
let completed = 0;
|
|
3126
|
+
const semaphore = async (tasks2, limit) => {
|
|
3127
|
+
const executing = [];
|
|
3128
|
+
for (const task of tasks2) {
|
|
3129
|
+
const p = task().then(() => {
|
|
3130
|
+
executing.splice(executing.indexOf(p), 1);
|
|
3131
|
+
});
|
|
3132
|
+
executing.push(p);
|
|
3133
|
+
if (executing.length >= limit) {
|
|
3134
|
+
await Promise.race(executing);
|
|
3135
|
+
}
|
|
3136
|
+
}
|
|
3137
|
+
await Promise.all(executing);
|
|
3138
|
+
};
|
|
3139
|
+
const tasks = lines.map((address, index) => async () => {
|
|
3140
|
+
try {
|
|
3141
|
+
const result = await searcher.geocode(address);
|
|
3142
|
+
results[index] = { address, ...result };
|
|
3143
|
+
} catch (error) {
|
|
3144
|
+
results[index] = { address, success: false, error: error.message };
|
|
3145
|
+
}
|
|
3146
|
+
completed++;
|
|
3147
|
+
if (!argv.output) return;
|
|
3148
|
+
process.stderr.write(`\r ${completed}/${lines.length} geocoded`);
|
|
3016
3149
|
});
|
|
3150
|
+
await semaphore(tasks, concurrency);
|
|
3151
|
+
if (argv.output) {
|
|
3152
|
+
process.stderr.write("\n");
|
|
3153
|
+
}
|
|
3154
|
+
const succeeded = results.filter((r) => r.success).length;
|
|
3155
|
+
const failed = results.filter((r) => !r.success).length;
|
|
3156
|
+
const summary = { total: lines.length, succeeded, failed, results };
|
|
3157
|
+
const json = JSON.stringify(summary, null, 2);
|
|
3158
|
+
if (argv.output) {
|
|
3159
|
+
writeFileSync4(argv.output, json, "utf-8");
|
|
3160
|
+
console.error(`Done: ${succeeded}/${lines.length} succeeded. Output: ${argv.output}`);
|
|
3161
|
+
} else {
|
|
3162
|
+
console.log(json);
|
|
3163
|
+
}
|
|
3164
|
+
process.exit(failed > 0 ? 1 : 0);
|
|
3017
3165
|
}
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3166
|
+
).command(
|
|
3167
|
+
"$0",
|
|
3168
|
+
"Start the MCP server (HTTP by default, --stdio for stdio mode)",
|
|
3169
|
+
(yargs2) => {
|
|
3170
|
+
return yargs2.option("port", {
|
|
3171
|
+
alias: "p",
|
|
3172
|
+
type: "number",
|
|
3173
|
+
description: "Port to run the MCP server on",
|
|
3174
|
+
default: process.env.MCP_SERVER_PORT ? parseInt(process.env.MCP_SERVER_PORT) : 3e3
|
|
3175
|
+
}).option("host", {
|
|
3176
|
+
type: "string",
|
|
3177
|
+
description: "Hostname to bind the server to (default 127.0.0.1 loopback; 0.0.0.0 for all interfaces)",
|
|
3178
|
+
default: process.env.MCP_SERVER_HOST || "127.0.0.1"
|
|
3179
|
+
}).option("apikey", {
|
|
3180
|
+
alias: "k",
|
|
3181
|
+
type: "string",
|
|
3182
|
+
description: "Google Maps API key",
|
|
3183
|
+
default: process.env.GOOGLE_MAPS_API_KEY
|
|
3184
|
+
}).option("stdio", {
|
|
3185
|
+
type: "boolean",
|
|
3186
|
+
description: "Use stdio transport instead of HTTP",
|
|
3187
|
+
default: false
|
|
3188
|
+
}).example([
|
|
3189
|
+
["$0", "Start HTTP server with default settings"],
|
|
3190
|
+
['$0 --port 3000 --apikey "your_api_key"', "Start HTTP with custom port and API key"],
|
|
3191
|
+
["$0 --host 0.0.0.0 --port 3000", "Start HTTP accessible from all interfaces"],
|
|
3192
|
+
["$0 --stdio", "Start in stdio mode (for Claude Desktop, Cursor, etc.)"]
|
|
3193
|
+
]);
|
|
3194
|
+
},
|
|
3195
|
+
async (argv) => {
|
|
3196
|
+
if (argv.apikey) {
|
|
3197
|
+
process.env.GOOGLE_MAPS_API_KEY = argv.apikey;
|
|
3198
|
+
}
|
|
3199
|
+
void checkVersionIdle({ currentVersion: packageVersion }).catch(() => void 0);
|
|
3200
|
+
const tools = filterTools(config_default[0].tools);
|
|
3201
|
+
if (argv.stdio) {
|
|
3202
|
+
const server = new BaseMcpServer(config_default[0].name, tools);
|
|
3203
|
+
await server.startStdio();
|
|
3204
|
+
} else {
|
|
3205
|
+
Logger.log("\u{1F5FA}\uFE0F Google Maps MCP Server");
|
|
3206
|
+
Logger.log(" A Model Context Protocol server for Google Maps services");
|
|
3207
|
+
Logger.log("");
|
|
3208
|
+
if (!argv.apikey) {
|
|
3209
|
+
Logger.log("\u26A0\uFE0F Google Maps API Key not found!");
|
|
3210
|
+
Logger.log(" Please provide --apikey parameter or set GOOGLE_MAPS_API_KEY in your .env file");
|
|
3211
|
+
Logger.log("");
|
|
3212
|
+
}
|
|
3213
|
+
startServer(argv.port, argv.apikey, argv.host).then((outcome) => {
|
|
3214
|
+
if (outcome.started === 0) {
|
|
3215
|
+
process.exit(1);
|
|
3216
|
+
}
|
|
3217
|
+
}).catch((error) => {
|
|
3218
|
+
Logger.error("\u274C Failed to start server:", error);
|
|
3219
|
+
process.exit(1);
|
|
3220
|
+
});
|
|
3221
|
+
}
|
|
3222
|
+
}
|
|
3223
|
+
).version(packageVersion).alias("version", "v").option("env-file", {
|
|
3224
|
+
type: "string",
|
|
3225
|
+
global: true,
|
|
3226
|
+
description: "Load environment variables from this exact file (error if missing; no implicit .env fallback)"
|
|
3227
|
+
}).help().parse();
|
|
3228
|
+
}
|
|
3024
3229
|
}
|
|
3025
3230
|
export {
|
|
3026
3231
|
DOCTOR_PROBES,
|