mcp-google-api 0.21.0 → 0.22.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 +37 -0
- package/README.md +141 -229
- package/dist/{chunk-VCFXCLJF.js → chunk-GRSJKPXN.js} +1 -3
- package/dist/chunk-GRSJKPXN.js.map +1 -0
- package/dist/cli.d.ts +23 -34
- package/dist/cli.js +399 -368
- package/dist/cli.js.map +1 -1
- package/dist/index.js +1 -1
- package/docs/CONFIGURATION.md +59 -78
- package/docs/TOOLS.md +1 -1
- package/package.json +2 -4
- package/.env.example +0 -17
- package/dist/chunk-VCFXCLJF.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -2,22 +2,117 @@
|
|
|
2
2
|
import {
|
|
3
3
|
Logger,
|
|
4
4
|
PlacesSearcher
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-GRSJKPXN.js";
|
|
6
6
|
|
|
7
7
|
// src/cli.ts
|
|
8
|
-
import { config as dotenvConfig2 } from "dotenv";
|
|
9
|
-
import { resolve as resolve5 } from "path";
|
|
10
8
|
import yargs from "yargs";
|
|
11
9
|
import { hideBin } from "yargs/helpers";
|
|
12
10
|
|
|
11
|
+
// src/utils/cliSettings.ts
|
|
12
|
+
import { mkdirSync, readFileSync, renameSync, writeFileSync } from "fs";
|
|
13
|
+
import { join, resolve } from "path";
|
|
14
|
+
var DEFAULT_SETTINGS_HOST = "127.0.0.1";
|
|
15
|
+
var DEFAULT_SETTINGS_PORT = 3e3;
|
|
16
|
+
var DEFAULT_SETTINGS_ENABLED_TOOLS = "*";
|
|
17
|
+
var SETTINGS_PROGRAM_NAME = "mcp-google-api";
|
|
18
|
+
var CLI_SETTINGS_RELATIVE = [".config", SETTINGS_PROGRAM_NAME, "config.json"];
|
|
19
|
+
var MCP_SERVER_NAME = "mcp-google-api";
|
|
20
|
+
var INSTALL_API_KEY_PLACEHOLDER = "__RUN_mcp-google-api_api-key_TO_CONFIGURE_YOUR_API_KEY__";
|
|
21
|
+
function resolveCliSettingsPath(home) {
|
|
22
|
+
const root = home ?? (process.env.HOME ?? process.cwd());
|
|
23
|
+
return resolve(root, ...CLI_SETTINGS_RELATIVE);
|
|
24
|
+
}
|
|
25
|
+
function normalizeEnabledTools(value) {
|
|
26
|
+
if (typeof value !== "string") {
|
|
27
|
+
return DEFAULT_SETTINGS_ENABLED_TOOLS;
|
|
28
|
+
}
|
|
29
|
+
const trimmed = value.trim();
|
|
30
|
+
return trimmed.length > 0 ? trimmed : DEFAULT_SETTINGS_ENABLED_TOOLS;
|
|
31
|
+
}
|
|
32
|
+
function normalizeHost(value) {
|
|
33
|
+
if (typeof value !== "string") {
|
|
34
|
+
return DEFAULT_SETTINGS_HOST;
|
|
35
|
+
}
|
|
36
|
+
const trimmed = value.trim();
|
|
37
|
+
return trimmed.length > 0 ? trimmed : DEFAULT_SETTINGS_HOST;
|
|
38
|
+
}
|
|
39
|
+
function normalizePort(value) {
|
|
40
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 1 || value > 65535) {
|
|
41
|
+
return DEFAULT_SETTINGS_PORT;
|
|
42
|
+
}
|
|
43
|
+
return value;
|
|
44
|
+
}
|
|
45
|
+
function defaultSettings() {
|
|
46
|
+
return {
|
|
47
|
+
enabled: [],
|
|
48
|
+
apiKey: "",
|
|
49
|
+
autoUpdate: true,
|
|
50
|
+
enabledTools: DEFAULT_SETTINGS_ENABLED_TOOLS,
|
|
51
|
+
host: DEFAULT_SETTINGS_HOST,
|
|
52
|
+
port: DEFAULT_SETTINGS_PORT
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function isUnsetApiKey(value) {
|
|
56
|
+
return value === void 0 || value === null || value.trim().length === 0 || value === INSTALL_API_KEY_PLACEHOLDER;
|
|
57
|
+
}
|
|
58
|
+
function uniqueStrings(value) {
|
|
59
|
+
if (!Array.isArray(value)) return [];
|
|
60
|
+
const result = [];
|
|
61
|
+
for (const item of value) {
|
|
62
|
+
if (typeof item === "string" && item.length > 0 && !result.includes(item)) {
|
|
63
|
+
result.push(item);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
68
|
+
function loadCliSettings(home) {
|
|
69
|
+
try {
|
|
70
|
+
const raw = JSON.parse(readFileSync(resolveCliSettingsPath(home), "utf-8"));
|
|
71
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
|
|
72
|
+
return defaultSettings();
|
|
73
|
+
}
|
|
74
|
+
const record = raw;
|
|
75
|
+
return {
|
|
76
|
+
enabled: uniqueStrings(record.enabled),
|
|
77
|
+
apiKey: typeof record.apiKey === "string" ? record.apiKey : "",
|
|
78
|
+
autoUpdate: typeof record.autoUpdate === "boolean" ? record.autoUpdate : true,
|
|
79
|
+
enabledTools: normalizeEnabledTools(record.enabledTools),
|
|
80
|
+
host: normalizeHost(record.host),
|
|
81
|
+
port: normalizePort(record.port)
|
|
82
|
+
};
|
|
83
|
+
} catch {
|
|
84
|
+
return defaultSettings();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function loadConfiguredApiKey(home) {
|
|
88
|
+
const apiKey = loadCliSettings(home).apiKey;
|
|
89
|
+
return isUnsetApiKey(apiKey) ? void 0 : apiKey;
|
|
90
|
+
}
|
|
91
|
+
function saveCliSettings(settings, home) {
|
|
92
|
+
const path = resolveCliSettingsPath(home);
|
|
93
|
+
const temporary = `${path}.tmp`;
|
|
94
|
+
const payload = {
|
|
95
|
+
enabled: uniqueStrings(settings.enabled),
|
|
96
|
+
apiKey: typeof settings.apiKey === "string" ? settings.apiKey : "",
|
|
97
|
+
autoUpdate: settings.autoUpdate === true,
|
|
98
|
+
enabledTools: normalizeEnabledTools(settings.enabledTools),
|
|
99
|
+
host: normalizeHost(settings.host),
|
|
100
|
+
port: normalizePort(settings.port)
|
|
101
|
+
};
|
|
102
|
+
try {
|
|
103
|
+
mkdirSync(join(path, ".."), { recursive: true });
|
|
104
|
+
writeFileSync(temporary, JSON.stringify(payload, null, 2) + "\n", "utf-8");
|
|
105
|
+
renameSync(temporary, path);
|
|
106
|
+
} catch (error) {
|
|
107
|
+
throw new Error(`settings write failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
108
|
+
}
|
|
109
|
+
return path;
|
|
110
|
+
}
|
|
111
|
+
|
|
13
112
|
// src/utils/configResolver.ts
|
|
14
|
-
import { config as dotenvConfig } from "dotenv";
|
|
15
|
-
import { existsSync } from "fs";
|
|
16
113
|
var NODE_ENGINE_FLOOR = 24;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
function resolveRuntimeConfig(env = process.env, overrides = {}) {
|
|
20
|
-
const envPort = env.MCP_SERVER_PORT?.trim() || void 0;
|
|
114
|
+
function resolveRuntimeConfig(settings, overrides = {}) {
|
|
115
|
+
const settingsPort = Number.isInteger(settings.port) && settings.port >= 1 && settings.port <= 65535 ? settings.port : void 0;
|
|
21
116
|
let port;
|
|
22
117
|
let portRaw;
|
|
23
118
|
let portSource;
|
|
@@ -25,30 +120,30 @@ function resolveRuntimeConfig(env = process.env, overrides = {}) {
|
|
|
25
120
|
port = overrides.port;
|
|
26
121
|
portRaw = String(overrides.port);
|
|
27
122
|
portSource = "flag";
|
|
28
|
-
} else if (
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
portSource = "environment";
|
|
123
|
+
} else if (settingsPort !== void 0) {
|
|
124
|
+
port = settingsPort;
|
|
125
|
+
portRaw = String(settingsPort);
|
|
126
|
+
portSource = "settings";
|
|
33
127
|
} else {
|
|
34
|
-
port =
|
|
35
|
-
portRaw =
|
|
36
|
-
portSource = "
|
|
37
|
-
}
|
|
38
|
-
const
|
|
39
|
-
const
|
|
128
|
+
port = void 0;
|
|
129
|
+
portRaw = String(settings.port);
|
|
130
|
+
portSource = "settings";
|
|
131
|
+
}
|
|
132
|
+
const flagApiKey = overrides.apiKey !== void 0 ? overrides.apiKey.trim() : void 0;
|
|
133
|
+
const settingsApiKey = isUnsetApiKey(settings.apiKey) ? void 0 : settings.apiKey;
|
|
134
|
+
const apiKey = flagApiKey !== void 0 && flagApiKey.length > 0 ? flagApiKey : settingsApiKey;
|
|
135
|
+
const apiKeySource = flagApiKey !== void 0 && flagApiKey.length > 0 ? "flag" : settingsApiKey !== void 0 ? "settings" : "none";
|
|
40
136
|
return {
|
|
41
137
|
apiKey,
|
|
42
|
-
apiKeySource
|
|
43
|
-
host: overrides.host ??
|
|
44
|
-
hostSource: overrides.host !== void 0 ? "flag" :
|
|
138
|
+
apiKeySource,
|
|
139
|
+
host: overrides.host ?? settings.host,
|
|
140
|
+
hostSource: overrides.host !== void 0 ? "flag" : "settings",
|
|
45
141
|
port,
|
|
46
142
|
portRaw,
|
|
47
143
|
portSource,
|
|
48
|
-
enabledToolsRaw,
|
|
49
|
-
enabledToolsSource:
|
|
50
|
-
autoUpdateDisabled:
|
|
51
|
-
envFile: overrides.envFile
|
|
144
|
+
enabledToolsRaw: settings.enabledTools,
|
|
145
|
+
enabledToolsSource: "settings",
|
|
146
|
+
autoUpdateDisabled: settings.autoUpdate !== true
|
|
52
147
|
};
|
|
53
148
|
}
|
|
54
149
|
function maskSecret(value) {
|
|
@@ -69,9 +164,8 @@ function describeRuntimeConfig(config) {
|
|
|
69
164
|
raw: config.portRaw ?? null,
|
|
70
165
|
source: config.portSource
|
|
71
166
|
},
|
|
72
|
-
enabledTools: { value: config.enabledToolsRaw
|
|
73
|
-
autoUpdateCheck: { enabled: !config.autoUpdateDisabled }
|
|
74
|
-
envFile: { value: config.envFile ?? null }
|
|
167
|
+
enabledTools: { value: config.enabledToolsRaw, source: config.enabledToolsSource },
|
|
168
|
+
autoUpdateCheck: { enabled: !config.autoUpdateDisabled }
|
|
75
169
|
};
|
|
76
170
|
}
|
|
77
171
|
function formatConfigDiagnostics(diagnostics) {
|
|
@@ -83,13 +177,6 @@ function formatConfigDiagnostics(diagnostics) {
|
|
|
83
177
|
`updateCheck=${diagnostics.autoUpdateCheck.enabled ? "enabled" : "disabled"}`
|
|
84
178
|
].join("; ");
|
|
85
179
|
}
|
|
86
|
-
function applyExplicitEnvFile(path) {
|
|
87
|
-
if (!existsSync(path)) {
|
|
88
|
-
return { ok: false, error: `env file not found: ${path}` };
|
|
89
|
-
}
|
|
90
|
-
dotenvConfig({ path });
|
|
91
|
-
return { ok: true };
|
|
92
|
-
}
|
|
93
180
|
function resolveEnabledToolNames(allToolNames, raw) {
|
|
94
181
|
const trimmed = raw?.trim();
|
|
95
182
|
if (!trimmed || trimmed === "*") {
|
|
@@ -1067,7 +1154,6 @@ var MAPS_TOOL_ANNOTATIONS = {
|
|
|
1067
1154
|
var serverConfigs = [
|
|
1068
1155
|
{
|
|
1069
1156
|
name: "MCP-Server",
|
|
1070
|
-
portEnvVar: "MCP_SERVER_PORT",
|
|
1071
1157
|
tools: [
|
|
1072
1158
|
{
|
|
1073
1159
|
name: SearchNearby.NAME,
|
|
@@ -1198,17 +1284,17 @@ var serverConfigs = [
|
|
|
1198
1284
|
]
|
|
1199
1285
|
}
|
|
1200
1286
|
];
|
|
1201
|
-
function filterTools(tools) {
|
|
1287
|
+
function filterTools(tools, raw = loadCliSettings().enabledTools) {
|
|
1202
1288
|
const resolution = resolveEnabledToolNames(
|
|
1203
1289
|
tools.map((tool) => tool.name),
|
|
1204
|
-
|
|
1290
|
+
raw
|
|
1205
1291
|
);
|
|
1206
1292
|
if (resolution.fallback) {
|
|
1207
|
-
Logger.error(`
|
|
1293
|
+
Logger.error(`enabledTools matched 0 tools. Available: ${tools.map((t) => t.name).join(", ")}`);
|
|
1208
1294
|
return tools;
|
|
1209
1295
|
}
|
|
1210
1296
|
if (resolution.matched !== tools.length) {
|
|
1211
|
-
Logger.log(`
|
|
1297
|
+
Logger.log(`enabledTools: ${resolution.matched}/${tools.length} tools active`);
|
|
1212
1298
|
}
|
|
1213
1299
|
const enabled = new Set(resolution.names);
|
|
1214
1300
|
return tools.filter((tool) => enabled.has(tool.name));
|
|
@@ -1400,8 +1486,8 @@ var BaseMcpServer = class {
|
|
|
1400
1486
|
|
|
1401
1487
|
// src/cli.ts
|
|
1402
1488
|
import { fileURLToPath } from "url";
|
|
1403
|
-
import { dirname as dirname3 } from "path";
|
|
1404
|
-
import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as
|
|
1489
|
+
import { dirname as dirname3, resolve as resolve5 } from "path";
|
|
1490
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync3, realpathSync } from "fs";
|
|
1405
1491
|
import { createInterface } from "readline";
|
|
1406
1492
|
|
|
1407
1493
|
// src/utils/upgradeManager.ts
|
|
@@ -1516,68 +1602,6 @@ async function probeLatestGitHubVersion(deps = {}) {
|
|
|
1516
1602
|
}
|
|
1517
1603
|
}
|
|
1518
1604
|
|
|
1519
|
-
// src/utils/cliSettings.ts
|
|
1520
|
-
import { mkdirSync, readFileSync, renameSync, writeFileSync } from "fs";
|
|
1521
|
-
import { join, resolve } from "path";
|
|
1522
|
-
var SETTINGS_PROGRAM_NAME = "mcp-google-api";
|
|
1523
|
-
var CLI_SETTINGS_RELATIVE = [".config", SETTINGS_PROGRAM_NAME, "config.json"];
|
|
1524
|
-
var MCP_SERVER_NAME = "mcp-google-api";
|
|
1525
|
-
var INSTALL_API_KEY_PLACEHOLDER = "__RUN_mcp-google-api_api-key_TO_CONFIGURE_YOUR_API_KEY__";
|
|
1526
|
-
function resolveCliSettingsPath(home) {
|
|
1527
|
-
const root = home ?? (process.env.HOME ?? process.cwd());
|
|
1528
|
-
return resolve(root, ...CLI_SETTINGS_RELATIVE);
|
|
1529
|
-
}
|
|
1530
|
-
function uniqueStrings(value) {
|
|
1531
|
-
if (!Array.isArray(value)) return [];
|
|
1532
|
-
const result = [];
|
|
1533
|
-
for (const item of value) {
|
|
1534
|
-
if (typeof item === "string" && item.length > 0 && !result.includes(item)) {
|
|
1535
|
-
result.push(item);
|
|
1536
|
-
}
|
|
1537
|
-
}
|
|
1538
|
-
return result;
|
|
1539
|
-
}
|
|
1540
|
-
function isUnsetApiKey(value) {
|
|
1541
|
-
return value === void 0 || value === null || value.trim().length === 0 || value === INSTALL_API_KEY_PLACEHOLDER;
|
|
1542
|
-
}
|
|
1543
|
-
function loadCliSettings(home) {
|
|
1544
|
-
try {
|
|
1545
|
-
const raw = JSON.parse(readFileSync(resolveCliSettingsPath(home), "utf-8"));
|
|
1546
|
-
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
|
|
1547
|
-
return { enabled: [], apiKey: "", autoUpdate: true };
|
|
1548
|
-
}
|
|
1549
|
-
const record = raw;
|
|
1550
|
-
return {
|
|
1551
|
-
enabled: uniqueStrings(record.enabled),
|
|
1552
|
-
apiKey: typeof record.apiKey === "string" ? record.apiKey : "",
|
|
1553
|
-
autoUpdate: typeof record.autoUpdate === "boolean" ? record.autoUpdate : true
|
|
1554
|
-
};
|
|
1555
|
-
} catch {
|
|
1556
|
-
return { enabled: [], apiKey: "", autoUpdate: true };
|
|
1557
|
-
}
|
|
1558
|
-
}
|
|
1559
|
-
function loadConfiguredApiKey(home) {
|
|
1560
|
-
const apiKey = loadCliSettings(home).apiKey;
|
|
1561
|
-
return isUnsetApiKey(apiKey) ? void 0 : apiKey;
|
|
1562
|
-
}
|
|
1563
|
-
function saveCliSettings(settings, home) {
|
|
1564
|
-
const path = resolveCliSettingsPath(home);
|
|
1565
|
-
const temporary = `${path}.tmp`;
|
|
1566
|
-
const payload = {
|
|
1567
|
-
enabled: uniqueStrings(settings.enabled),
|
|
1568
|
-
apiKey: typeof settings.apiKey === "string" ? settings.apiKey : "",
|
|
1569
|
-
autoUpdate: settings.autoUpdate === true
|
|
1570
|
-
};
|
|
1571
|
-
try {
|
|
1572
|
-
mkdirSync(join(path, ".."), { recursive: true });
|
|
1573
|
-
writeFileSync(temporary, JSON.stringify(payload, null, 2) + "\n", "utf-8");
|
|
1574
|
-
renameSync(temporary, path);
|
|
1575
|
-
} catch (error) {
|
|
1576
|
-
throw new Error(`settings write failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1577
|
-
}
|
|
1578
|
-
return path;
|
|
1579
|
-
}
|
|
1580
|
-
|
|
1581
1605
|
// src/utils/upgradeManager.ts
|
|
1582
1606
|
var ANSI_GREEN = "\x1B[92m";
|
|
1583
1607
|
var ANSI_RED = "\x1B[91m";
|
|
@@ -1732,46 +1756,28 @@ function runPackageManagerCommand(command, deps = {}) {
|
|
|
1732
1756
|
}
|
|
1733
1757
|
}
|
|
1734
1758
|
function parsePostUpgradeCliOutcomes(raw) {
|
|
1735
|
-
if (raw === void 0) {
|
|
1736
|
-
return void 0;
|
|
1737
|
-
}
|
|
1738
|
-
const start = raw.indexOf("{");
|
|
1739
|
-
if (start < 0) {
|
|
1740
|
-
return void 0;
|
|
1741
|
-
}
|
|
1742
|
-
let parsed;
|
|
1743
|
-
try {
|
|
1744
|
-
parsed = JSON.parse(raw.slice(start));
|
|
1745
|
-
} catch {
|
|
1759
|
+
if (raw === void 0 || !raw.includes("install report:")) {
|
|
1746
1760
|
return void 0;
|
|
1747
1761
|
}
|
|
1748
|
-
|
|
1749
|
-
|
|
1762
|
+
const lines = raw.split(/\r?\n/);
|
|
1763
|
+
const cliLine = /^\s{2,}([a-z][a-z0-9-]*):\s+/;
|
|
1764
|
+
const outcomes = [];
|
|
1765
|
+
for (const line of lines) {
|
|
1766
|
+
const match = cliLine.exec(line);
|
|
1767
|
+
if (match) {
|
|
1768
|
+
outcomes.push({ cli: match[1], ok: true });
|
|
1769
|
+
}
|
|
1750
1770
|
}
|
|
1751
|
-
|
|
1752
|
-
const errors = parsed.errors;
|
|
1753
|
-
if (!Array.isArray(summaries)) {
|
|
1771
|
+
if (outcomes.length === 0) {
|
|
1754
1772
|
return void 0;
|
|
1755
1773
|
}
|
|
1756
|
-
const
|
|
1757
|
-
const
|
|
1758
|
-
for (const
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
const record = entry;
|
|
1763
|
-
const cli = record.cli;
|
|
1764
|
-
if (typeof cli !== "string" || cli.length === 0) {
|
|
1765
|
-
continue;
|
|
1774
|
+
const errorBlockIndex = lines.findIndex((line) => line.trim() === "Errors:");
|
|
1775
|
+
const errorTexts = errorBlockIndex >= 0 ? lines.slice(errorBlockIndex + 1).filter((line) => line.trim().length > 0) : [];
|
|
1776
|
+
for (const outcome of outcomes) {
|
|
1777
|
+
const lowered = outcome.cli.toLowerCase();
|
|
1778
|
+
if (errorTexts.some((text) => text.toLowerCase().includes(lowered))) {
|
|
1779
|
+
outcome.ok = false;
|
|
1766
1780
|
}
|
|
1767
|
-
const lowered = cli.toLowerCase();
|
|
1768
|
-
const failed = errorTexts.some((text) => text.toLowerCase().includes(lowered));
|
|
1769
|
-
outcomes.push({
|
|
1770
|
-
cli,
|
|
1771
|
-
ok: !failed,
|
|
1772
|
-
server: typeof record.server === "number" ? record.server : void 0,
|
|
1773
|
-
skills: typeof record.skills === "number" ? record.skills : void 0
|
|
1774
|
-
});
|
|
1775
1781
|
}
|
|
1776
1782
|
return outcomes;
|
|
1777
1783
|
}
|
|
@@ -1976,7 +1982,6 @@ function describeToolsProfile(toolNames) {
|
|
|
1976
1982
|
}
|
|
1977
1983
|
|
|
1978
1984
|
// src/utils/clientSnippets.ts
|
|
1979
|
-
var API_KEY_PLACEHOLDER = "<your-google-maps-api-key>";
|
|
1980
1985
|
var SNIPPET_DEFAULT_HOST = "127.0.0.1";
|
|
1981
1986
|
var SNIPPET_DEFAULT_PORT = 3e3;
|
|
1982
1987
|
var SNIPPET_CLIENT_TARGETS = {
|
|
@@ -2007,15 +2012,8 @@ function renderClientSnippet(options = {}) {
|
|
|
2007
2012
|
if (launcher === "npx") {
|
|
2008
2013
|
notes.push("The npx launcher runs the published package; npm must be available on PATH.");
|
|
2009
2014
|
}
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
if (options.envKey) {
|
|
2013
|
-
notes.push(
|
|
2014
|
-
`Variable references (\${${options.envKey}}) are client-specific: confirm your client expands environment variables before relying on them.`
|
|
2015
|
-
);
|
|
2016
|
-
} else {
|
|
2017
|
-
notes.push(`Replace ${API_KEY_PLACEHOLDER} with your Google Maps API key.`);
|
|
2018
|
-
}
|
|
2015
|
+
entry = { command, args };
|
|
2016
|
+
notes.push("The registration carries no API key: configure it once with 'mcp-google-api api-key <key>'.");
|
|
2019
2017
|
}
|
|
2020
2018
|
const json = JSON.stringify({ mcpServers: { [serverName]: entry } }, null, 2);
|
|
2021
2019
|
return { json, notes };
|
|
@@ -2025,7 +2023,7 @@ function renderClientSnippet(options = {}) {
|
|
|
2025
2023
|
import {
|
|
2026
2024
|
appendFileSync,
|
|
2027
2025
|
cpSync,
|
|
2028
|
-
existsSync
|
|
2026
|
+
existsSync,
|
|
2029
2027
|
mkdirSync as mkdirSync3,
|
|
2030
2028
|
readFileSync as readFileSync3,
|
|
2031
2029
|
readdirSync,
|
|
@@ -2082,7 +2080,7 @@ function supportedCliNames() {
|
|
|
2082
2080
|
return Object.keys(CLI_TARGETS).join(", ");
|
|
2083
2081
|
}
|
|
2084
2082
|
function readJsonConfig(configPath) {
|
|
2085
|
-
if (!
|
|
2083
|
+
if (!existsSync(configPath)) return {};
|
|
2086
2084
|
const parsed = JSON.parse(readFileSync3(configPath, "utf-8"));
|
|
2087
2085
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
2088
2086
|
throw new Error(`configuration is not a JSON object: ${configPath}`);
|
|
@@ -2106,7 +2104,7 @@ function writeJsonMcpServerEntry(configPath, serverName, entry) {
|
|
|
2106
2104
|
writeJsonConfig(configPath, config);
|
|
2107
2105
|
}
|
|
2108
2106
|
function removeJsonMcpServerEntry(configPath, serverName) {
|
|
2109
|
-
if (!
|
|
2107
|
+
if (!existsSync(configPath)) return false;
|
|
2110
2108
|
const config = readJsonConfig(configPath);
|
|
2111
2109
|
const mcpServers = config.mcpServers;
|
|
2112
2110
|
if (mcpServers === void 0) return false;
|
|
@@ -2179,7 +2177,7 @@ function registrationToTomlLines(entry) {
|
|
|
2179
2177
|
return lines;
|
|
2180
2178
|
}
|
|
2181
2179
|
function writeTomlMcpServerSection(configPath, serverName, entry) {
|
|
2182
|
-
const previous =
|
|
2180
|
+
const previous = existsSync(configPath) ? readFileSync3(configPath, "utf-8") : "";
|
|
2183
2181
|
const base = removeTomlSection(previous, serverName);
|
|
2184
2182
|
const table = [tomlSectionHeader(serverName), ...registrationToTomlLines(entry), ""].join("\n");
|
|
2185
2183
|
const content = base.length > 0 ? base.replace(/\n*$/, "\n") + table : table;
|
|
@@ -2189,7 +2187,7 @@ function writeTomlMcpServerSection(configPath, serverName, entry) {
|
|
|
2189
2187
|
renameSync2(temporary, configPath);
|
|
2190
2188
|
}
|
|
2191
2189
|
function removeTomlMcpServerSection(configPath, serverName) {
|
|
2192
|
-
if (!
|
|
2190
|
+
if (!existsSync(configPath)) return false;
|
|
2193
2191
|
const previous = readFileSync3(configPath, "utf-8");
|
|
2194
2192
|
if (!hasTomlSection(previous, serverName)) return false;
|
|
2195
2193
|
const content = removeTomlSection(previous, serverName);
|
|
@@ -2199,11 +2197,11 @@ function removeTomlMcpServerSection(configPath, serverName) {
|
|
|
2199
2197
|
return true;
|
|
2200
2198
|
}
|
|
2201
2199
|
function hasTomlMcpServerSection(configPath, serverName) {
|
|
2202
|
-
if (!
|
|
2200
|
+
if (!existsSync(configPath)) return false;
|
|
2203
2201
|
return hasTomlSection(readFileSync3(configPath, "utf-8"), serverName);
|
|
2204
2202
|
}
|
|
2205
2203
|
function listFilesRecursive(root) {
|
|
2206
|
-
if (!
|
|
2204
|
+
if (!existsSync(root)) return [];
|
|
2207
2205
|
const files = [];
|
|
2208
2206
|
const walk = (dir) => {
|
|
2209
2207
|
for (const entry of readdirSync(dir)) {
|
|
@@ -2246,10 +2244,10 @@ function removeSkillDir(skillDir) {
|
|
|
2246
2244
|
function compareSkillTree(source, destination, layout = {}) {
|
|
2247
2245
|
const packaged = listFilesRecursive(source).filter((rel) => rel !== layout.debugPath);
|
|
2248
2246
|
if (packaged.length === 0) return "MISSING";
|
|
2249
|
-
if (!
|
|
2247
|
+
if (!existsSync(destination)) return "MISSING";
|
|
2250
2248
|
for (const rel of packaged) {
|
|
2251
2249
|
const target = join3(destination, ...rel.split("/"));
|
|
2252
|
-
if (!
|
|
2250
|
+
if (!existsSync(target)) return "MISSING";
|
|
2253
2251
|
const packagedText = readFileSync3(join3(source, ...rel.split("/"))).toString("utf-8");
|
|
2254
2252
|
const installedText = readFileSync3(target).toString("utf-8");
|
|
2255
2253
|
if (installedText === packagedText) continue;
|
|
@@ -2286,21 +2284,21 @@ function applyServerRegistration(target, serverName, entry, home) {
|
|
|
2286
2284
|
}
|
|
2287
2285
|
|
|
2288
2286
|
// src/utils/cliInstaller.ts
|
|
2289
|
-
import { existsSync as
|
|
2287
|
+
import { existsSync as existsSync2, readFileSync as readFileSync4 } from "fs";
|
|
2290
2288
|
import { dirname as dirname2, join as join4, resolve as resolve3 } from "path";
|
|
2291
2289
|
function buildServerRegistration() {
|
|
2292
2290
|
return { command: "npx", args: ["-y", MCP_SERVER_NAME, "start", "--stdio"] };
|
|
2293
2291
|
}
|
|
2294
2292
|
function resolveSkillsResourcesRoot(packageRoot) {
|
|
2295
2293
|
const candidate = resolve3(packageRoot, "src", "resources", "skills");
|
|
2296
|
-
if (!
|
|
2294
|
+
if (!existsSync2(candidate)) {
|
|
2297
2295
|
throw new Error(`skills resources root not found: ${candidate}`);
|
|
2298
2296
|
}
|
|
2299
2297
|
return candidate;
|
|
2300
2298
|
}
|
|
2301
2299
|
function loadSkillsManifest(resourcesRoot) {
|
|
2302
2300
|
const manifestPath = join4(resourcesRoot, "manifest.json");
|
|
2303
|
-
if (!
|
|
2301
|
+
if (!existsSync2(manifestPath)) {
|
|
2304
2302
|
throw new Error(`skills manifest not found: ${manifestPath}`);
|
|
2305
2303
|
}
|
|
2306
2304
|
let parsed;
|
|
@@ -2319,14 +2317,14 @@ function loadSkillsManifest(resourcesRoot) {
|
|
|
2319
2317
|
throw new Error(`skills manifest entry is missing id or path: ${manifestPath}`);
|
|
2320
2318
|
}
|
|
2321
2319
|
const skillFile = join4(resourcesRoot, skill.path);
|
|
2322
|
-
if (!
|
|
2320
|
+
if (!existsSync2(skillFile)) {
|
|
2323
2321
|
throw new Error(`manifest skill file not found: ${skillFile}`);
|
|
2324
2322
|
}
|
|
2325
2323
|
if (skill.debug !== void 0 && (typeof skill.debug !== "string" || skill.debug.trim() === "")) {
|
|
2326
2324
|
throw new Error(`manifest skill debug path is invalid: ${manifestPath}`);
|
|
2327
2325
|
}
|
|
2328
2326
|
const debugPath = typeof skill.debug === "string" ? skill.debug : void 0;
|
|
2329
|
-
if (debugPath !== void 0 && !
|
|
2327
|
+
if (debugPath !== void 0 && !existsSync2(join4(dirname2(resolve3(skillFile)), debugPath))) {
|
|
2330
2328
|
throw new Error(`manifest skill debug file not found: ${join4(dirname2(resolve3(skillFile)), debugPath)}`);
|
|
2331
2329
|
}
|
|
2332
2330
|
return {
|
|
@@ -2506,40 +2504,9 @@ function buildCliShowReport(options) {
|
|
|
2506
2504
|
// src/cli.ts
|
|
2507
2505
|
var __filename = fileURLToPath(import.meta.url);
|
|
2508
2506
|
var __dirname = dirname3(__filename);
|
|
2509
|
-
function
|
|
2510
|
-
for (let i = 0; i < argv.length; i++) {
|
|
2511
|
-
const arg = argv[i];
|
|
2512
|
-
if (arg === "--env-file" && i + 1 < argv.length) {
|
|
2513
|
-
return argv[i + 1];
|
|
2514
|
-
}
|
|
2515
|
-
if (arg.startsWith("--env-file=")) {
|
|
2516
|
-
return arg.slice("--env-file=".length);
|
|
2517
|
-
}
|
|
2518
|
-
}
|
|
2519
|
-
return void 0;
|
|
2520
|
-
}
|
|
2521
|
-
function loadEnvironment(envFile) {
|
|
2522
|
-
if (envFile !== void 0) {
|
|
2523
|
-
const result = applyExplicitEnvFile(envFile);
|
|
2524
|
-
if (!result.ok) {
|
|
2525
|
-
process.stderr.write(JSON.stringify({ error: result.error }, null, 2) + "\n");
|
|
2526
|
-
process.exit(1);
|
|
2527
|
-
}
|
|
2528
|
-
return;
|
|
2529
|
-
}
|
|
2530
|
-
dotenvConfig2({ path: resolve5(process.cwd(), ".env") });
|
|
2531
|
-
dotenvConfig2({ path: resolve5(__dirname, "../.env") });
|
|
2532
|
-
}
|
|
2533
|
-
async function startServer(port, apiKey, host) {
|
|
2534
|
-
if (port) {
|
|
2535
|
-
process.env.MCP_SERVER_PORT = port.toString();
|
|
2536
|
-
}
|
|
2507
|
+
async function startServer(port, apiKey, host = "127.0.0.1") {
|
|
2537
2508
|
ApiKeyManager.getInstance().setDefaultApiKey(apiKey);
|
|
2538
|
-
|
|
2539
|
-
process.env.MCP_SERVER_HOST = host;
|
|
2540
|
-
}
|
|
2541
|
-
Logger.log("\u{1F680} Starting Google Maps MCP Server...");
|
|
2542
|
-
Logger.log("\u{1F4CD} 18 tools registered (set GOOGLE_MAPS_ENABLED_TOOLS to limit)");
|
|
2509
|
+
Logger.log("\u{1F680} Starting MCP Google API server...");
|
|
2543
2510
|
Logger.log(
|
|
2544
2511
|
"\u2139\uFE0F Reminder: enable Places API (New) in https://console.cloud.google.com before using the new Place features."
|
|
2545
2512
|
);
|
|
@@ -2549,26 +2516,20 @@ async function startServer(port, apiKey, host) {
|
|
|
2549
2516
|
let failed = 0;
|
|
2550
2517
|
await Promise.all(
|
|
2551
2518
|
config_default.map(async (config) => {
|
|
2552
|
-
const
|
|
2553
|
-
if (!portString) {
|
|
2554
|
-
Logger.error(`\u26A0\uFE0F [${config.name}] Port environment variable ${config.portEnvVar} not set.`);
|
|
2555
|
-
Logger.log(`\u{1F4A1} Please set ${config.portEnvVar} in your .env file or use --port parameter.`);
|
|
2556
|
-
Logger.log(` Example: ${config.portEnvVar}=3000 or --port 3000`);
|
|
2557
|
-
failed++;
|
|
2558
|
-
return;
|
|
2559
|
-
}
|
|
2560
|
-
const serverPort = Number(portString);
|
|
2519
|
+
const serverPort = port;
|
|
2561
2520
|
if (!Number.isInteger(serverPort) || serverPort < 1 || serverPort > 65535) {
|
|
2562
2521
|
Logger.error(
|
|
2563
|
-
`\u274C [${config.name}] Invalid port number "${
|
|
2522
|
+
`\u274C [${config.name}] Invalid port number "${serverPort}" (expected integer 1-65535).`
|
|
2564
2523
|
);
|
|
2524
|
+
Logger.log("\u{1F4A1} Fix the persisted port with 'mcp-google-api port <number>' or pass --port.");
|
|
2565
2525
|
failed++;
|
|
2566
2526
|
return;
|
|
2567
2527
|
}
|
|
2568
2528
|
try {
|
|
2569
2529
|
const activeTools = filterTools(config.tools);
|
|
2530
|
+
Logger.log(`\u{1F4CD} ${activeTools.length} tools registered (configure with 'mcp-google-api tools')`);
|
|
2570
2531
|
const server = new BaseMcpServer(config.name, activeTools);
|
|
2571
|
-
const serverHost =
|
|
2532
|
+
const serverHost = host;
|
|
2572
2533
|
Logger.log(`\u{1F527} [${config.name}] Initializing MCP Server in HTTP mode on ${serverHost}:${serverPort}...`);
|
|
2573
2534
|
await server.startHttpServer(serverPort, serverHost);
|
|
2574
2535
|
servers.push(server);
|
|
@@ -2814,7 +2775,7 @@ function parseDoctorProbes(raw) {
|
|
|
2814
2775
|
async function runDoctor(packageVersion, apiKey, live = false, runner = execTool, options = {}) {
|
|
2815
2776
|
const nodeVersion = options.nodeVersion ?? process.versions.node;
|
|
2816
2777
|
const nodeMajor = Number.parseInt(nodeVersion.split(".")[0] ?? "0", 10);
|
|
2817
|
-
const config = options.config ?? resolveRuntimeConfig(
|
|
2778
|
+
const config = options.config ?? resolveRuntimeConfig(loadCliSettings());
|
|
2818
2779
|
const allToolNames = options.allToolNames ?? config_default[0].tools.map((tool) => tool.name);
|
|
2819
2780
|
const toolsResolution = resolveEnabledToolNames(allToolNames, config.enabledToolsRaw);
|
|
2820
2781
|
const diagnostics = describeRuntimeConfig(config);
|
|
@@ -2833,17 +2794,12 @@ async function runDoctor(packageVersion, apiKey, live = false, runner = execTool
|
|
|
2833
2794
|
{
|
|
2834
2795
|
name: "api-key",
|
|
2835
2796
|
status: apiKey ? "pass" : "fail",
|
|
2836
|
-
detail: apiKey ? "
|
|
2797
|
+
detail: apiKey ? "Google Maps API key is configured via 'mcp-google-api api-key' (value hidden)." : "Google Maps API key is not configured. Run 'mcp-google-api api-key <your-api-key>' to configure it."
|
|
2837
2798
|
},
|
|
2838
2799
|
{
|
|
2839
2800
|
name: "config",
|
|
2840
2801
|
status: toolsResolution.fallback ? "fail" : "pass",
|
|
2841
|
-
detail: toolsResolution.fallback ? `
|
|
2842
|
-
},
|
|
2843
|
-
{
|
|
2844
|
-
name: "env-file",
|
|
2845
|
-
status: "pass",
|
|
2846
|
-
detail: config.envFile ? `Explicit environment file loaded: ${config.envFile}` : "No explicit --env-file requested; implicit .env discovery (current directory, then package directory) applies."
|
|
2802
|
+
detail: toolsResolution.fallback ? `enabledTools matched 0 tools; the server falls back to all ${allToolNames.length} tools. Effective configuration: ${formatConfigDiagnostics(diagnostics)}` : `Effective configuration: ${formatConfigDiagnostics(diagnostics)}`
|
|
2847
2803
|
},
|
|
2848
2804
|
{
|
|
2849
2805
|
name: "tools",
|
|
@@ -2861,7 +2817,7 @@ async function runDoctor(packageVersion, apiKey, live = false, runner = execTool
|
|
|
2861
2817
|
checks.push({
|
|
2862
2818
|
name: "live-api",
|
|
2863
2819
|
status: "skip",
|
|
2864
|
-
detail: "Live API checks require
|
|
2820
|
+
detail: "Live API checks require a configured Google Maps API key."
|
|
2865
2821
|
});
|
|
2866
2822
|
} else {
|
|
2867
2823
|
const selected = options.probes ? DOCTOR_PROBES.filter((probe) => options.probes.includes(probe.id)) : DOCTOR_PROBES;
|
|
@@ -2897,78 +2853,6 @@ async function runDoctor(packageVersion, apiKey, live = false, runner = execTool
|
|
|
2897
2853
|
checks
|
|
2898
2854
|
};
|
|
2899
2855
|
}
|
|
2900
|
-
async function runSetup(options, deps = {}) {
|
|
2901
|
-
const fsImpl = deps.fs ?? {
|
|
2902
|
-
existsSync: (path) => existsSync4(path),
|
|
2903
|
-
readFileSync: (path, encoding) => readFileSync5(path, encoding),
|
|
2904
|
-
writeFileSync: (path, data, encoding) => writeFileSync4(path, data, encoding)
|
|
2905
|
-
};
|
|
2906
|
-
const nodeVersion = deps.nodeVersion ?? process.versions.node;
|
|
2907
|
-
const nodeMajor = Number.parseInt(nodeVersion.split(".")[0] ?? "0", 10);
|
|
2908
|
-
const config = deps.config ?? resolveRuntimeConfig(process.env);
|
|
2909
|
-
const examplePath = resolve5(options.packageDir, ".env.example");
|
|
2910
|
-
const envPath = resolve5(options.targetDir, ".env");
|
|
2911
|
-
const checks = [
|
|
2912
|
-
{
|
|
2913
|
-
name: "node",
|
|
2914
|
-
status: nodeMajor >= NODE_ENGINE_FLOOR ? "pass" : "fail",
|
|
2915
|
-
detail: `Node.js ${nodeVersion} detected; version ${NODE_ENGINE_FLOOR} or newer is required (package.json engines).`
|
|
2916
|
-
},
|
|
2917
|
-
{
|
|
2918
|
-
name: "env-template",
|
|
2919
|
-
status: fsImpl.existsSync(examplePath) ? "pass" : "fail",
|
|
2920
|
-
detail: fsImpl.existsSync(examplePath) ? `Template found: ${examplePath}` : `Template missing: ${examplePath} (expected next to the installed package).`
|
|
2921
|
-
}
|
|
2922
|
-
];
|
|
2923
|
-
let created = false;
|
|
2924
|
-
if (options.writeEnv) {
|
|
2925
|
-
if (fsImpl.existsSync(envPath)) {
|
|
2926
|
-
checks.push({
|
|
2927
|
-
name: "env-file",
|
|
2928
|
-
status: "pass",
|
|
2929
|
-
detail: `${envPath} already exists; left untouched (no overwrite).`
|
|
2930
|
-
});
|
|
2931
|
-
} else if (!fsImpl.existsSync(examplePath)) {
|
|
2932
|
-
checks.push({
|
|
2933
|
-
name: "env-file",
|
|
2934
|
-
status: "fail",
|
|
2935
|
-
detail: `Cannot create ${envPath}: template ${examplePath} is missing.`
|
|
2936
|
-
});
|
|
2937
|
-
} else {
|
|
2938
|
-
try {
|
|
2939
|
-
fsImpl.writeFileSync(envPath, fsImpl.readFileSync(examplePath, "utf-8"), "utf-8");
|
|
2940
|
-
created = true;
|
|
2941
|
-
checks.push({
|
|
2942
|
-
name: "env-file",
|
|
2943
|
-
status: "pass",
|
|
2944
|
-
detail: `Created ${envPath} from template; edit it and set GOOGLE_MAPS_API_KEY.`
|
|
2945
|
-
});
|
|
2946
|
-
} catch (error) {
|
|
2947
|
-
checks.push({
|
|
2948
|
-
name: "env-file",
|
|
2949
|
-
status: "fail",
|
|
2950
|
-
detail: `Failed to create ${envPath}: ${error instanceof Error ? error.message : String(error)}`
|
|
2951
|
-
});
|
|
2952
|
-
}
|
|
2953
|
-
}
|
|
2954
|
-
} else {
|
|
2955
|
-
checks.push({
|
|
2956
|
-
name: "env-file",
|
|
2957
|
-
status: fsImpl.existsSync(envPath) ? "pass" : "skip",
|
|
2958
|
-
detail: fsImpl.existsSync(envPath) ? `${envPath} found.` : `${envPath} not found; rerun with --write-env to create it from the template.`
|
|
2959
|
-
});
|
|
2960
|
-
}
|
|
2961
|
-
checks.push({
|
|
2962
|
-
name: "api-key",
|
|
2963
|
-
status: config.apiKey ? "pass" : "fail",
|
|
2964
|
-
detail: config.apiKey ? `GOOGLE_MAPS_API_KEY is configured (source: ${config.apiKeySource}, value hidden).` : "GOOGLE_MAPS_API_KEY is not configured. Set it in the environment or in .env after scaffolding."
|
|
2965
|
-
});
|
|
2966
|
-
return {
|
|
2967
|
-
success: checks.every((check) => check.status !== "fail"),
|
|
2968
|
-
created,
|
|
2969
|
-
checks
|
|
2970
|
-
};
|
|
2971
|
-
}
|
|
2972
2856
|
function runSnippetCommand(options, streams = { stdout: process.stdout, stderr: process.stderr }) {
|
|
2973
2857
|
try {
|
|
2974
2858
|
const snippet = renderClientSnippet(options);
|
|
@@ -3083,21 +2967,95 @@ function runAutoUpdateCommand(rawState, options = {}) {
|
|
|
3083
2967
|
streams.stdout.write(enabled ? "Auto-update check enabled.\n" : "Auto-update check disabled.\n");
|
|
3084
2968
|
return 0;
|
|
3085
2969
|
}
|
|
2970
|
+
function runToolsCommand(raw, options = {}) {
|
|
2971
|
+
const streams = options.streams ?? { stdout: process.stdout, stderr: process.stderr };
|
|
2972
|
+
const allToolNames = config_default[0].tools.map((tool) => tool.name);
|
|
2973
|
+
const trimmed = (raw ?? "").trim();
|
|
2974
|
+
if (trimmed.length === 0) {
|
|
2975
|
+
streams.stderr.write(
|
|
2976
|
+
`tools: missing or empty tool list. Use '*' for all tools or a comma-separated subset of: ${allToolNames.join(", ")}.
|
|
2977
|
+
`
|
|
2978
|
+
);
|
|
2979
|
+
return 1;
|
|
2980
|
+
}
|
|
2981
|
+
let value;
|
|
2982
|
+
if (trimmed === "*") {
|
|
2983
|
+
value = "*";
|
|
2984
|
+
} else {
|
|
2985
|
+
const requested = trimmed.split(",").map((item) => item.trim()).filter(Boolean);
|
|
2986
|
+
const unknown = requested.filter((item) => !allToolNames.includes(item));
|
|
2987
|
+
if (requested.length === 0 || unknown.length > 0) {
|
|
2988
|
+
streams.stderr.write(
|
|
2989
|
+
`tools: unknown tool name(s): ${unknown.join(", ") || "(none)"}. Accepted: ${allToolNames.join(", ")} (or '*').
|
|
2990
|
+
`
|
|
2991
|
+
);
|
|
2992
|
+
return 1;
|
|
2993
|
+
}
|
|
2994
|
+
value = requested.join(",");
|
|
2995
|
+
}
|
|
2996
|
+
try {
|
|
2997
|
+
const settings = loadCliSettings(options.home);
|
|
2998
|
+
settings.enabledTools = value;
|
|
2999
|
+
saveCliSettings(settings, options.home);
|
|
3000
|
+
} catch (error) {
|
|
3001
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3002
|
+
streams.stderr.write(`tools: failed to persist the setting: ${message}
|
|
3003
|
+
`);
|
|
3004
|
+
return 1;
|
|
3005
|
+
}
|
|
3006
|
+
const count = value === "*" ? allToolNames.length : value.split(",").length;
|
|
3007
|
+
streams.stdout.write(`Enabled tools set to ${value === "*" ? `* (all ${count})` : value}.
|
|
3008
|
+
`);
|
|
3009
|
+
return 0;
|
|
3010
|
+
}
|
|
3011
|
+
function runHostCommand(raw, options = {}) {
|
|
3012
|
+
const streams = options.streams ?? { stdout: process.stdout, stderr: process.stderr };
|
|
3013
|
+
const trimmed = (raw ?? "").trim();
|
|
3014
|
+
if (trimmed.length === 0) {
|
|
3015
|
+
streams.stderr.write("host: missing or empty hostname. Example: mcp-google-api host 127.0.0.1\n");
|
|
3016
|
+
return 1;
|
|
3017
|
+
}
|
|
3018
|
+
try {
|
|
3019
|
+
const settings = loadCliSettings(options.home);
|
|
3020
|
+
settings.host = trimmed;
|
|
3021
|
+
saveCliSettings(settings, options.home);
|
|
3022
|
+
} catch (error) {
|
|
3023
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3024
|
+
streams.stderr.write(`host: failed to persist the setting: ${message}
|
|
3025
|
+
`);
|
|
3026
|
+
return 1;
|
|
3027
|
+
}
|
|
3028
|
+
streams.stdout.write(`HTTP bind host set to ${trimmed}.
|
|
3029
|
+
`);
|
|
3030
|
+
return 0;
|
|
3031
|
+
}
|
|
3032
|
+
function runPortCommand(raw, options = {}) {
|
|
3033
|
+
const streams = options.streams ?? { stdout: process.stdout, stderr: process.stderr };
|
|
3034
|
+
const trimmed = (raw ?? "").trim();
|
|
3035
|
+
const parsed = Number(trimmed);
|
|
3036
|
+
if (trimmed.length === 0 || !Number.isInteger(parsed) || parsed < 1 || parsed > 65535) {
|
|
3037
|
+
streams.stderr.write(`port: invalid port "${trimmed || "(none)"}" (expected integer 1-65535).
|
|
3038
|
+
`);
|
|
3039
|
+
return 1;
|
|
3040
|
+
}
|
|
3041
|
+
try {
|
|
3042
|
+
const settings = loadCliSettings(options.home);
|
|
3043
|
+
settings.port = parsed;
|
|
3044
|
+
saveCliSettings(settings, options.home);
|
|
3045
|
+
} catch (error) {
|
|
3046
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3047
|
+
streams.stderr.write(`port: failed to persist the setting: ${message}
|
|
3048
|
+
`);
|
|
3049
|
+
return 1;
|
|
3050
|
+
}
|
|
3051
|
+
streams.stdout.write(`HTTP port set to ${parsed}.
|
|
3052
|
+
`);
|
|
3053
|
+
return 0;
|
|
3054
|
+
}
|
|
3086
3055
|
function runInstallUninstallCommand(operation, options) {
|
|
3087
3056
|
const streams = options.streams ?? { stdout: process.stdout, stderr: process.stderr };
|
|
3088
3057
|
if (loadCliSettings(options.home).enabled.length === 0) {
|
|
3089
|
-
streams.stdout.write(
|
|
3090
|
-
JSON.stringify(
|
|
3091
|
-
{
|
|
3092
|
-
success: true,
|
|
3093
|
-
message: "No CLI enabled. Use 'mcp-google-api enable <cli name>' first.",
|
|
3094
|
-
summaries: [],
|
|
3095
|
-
errors: []
|
|
3096
|
-
},
|
|
3097
|
-
null,
|
|
3098
|
-
2
|
|
3099
|
-
) + "\n"
|
|
3100
|
-
);
|
|
3058
|
+
streams.stdout.write("No CLI agent enabled. Run 'mcp-google-api enable <cli name>' first.\n");
|
|
3101
3059
|
return 0;
|
|
3102
3060
|
}
|
|
3103
3061
|
let result;
|
|
@@ -3109,13 +3067,26 @@ function runInstallUninstallCommand(operation, options) {
|
|
|
3109
3067
|
}) : uninstallEnabledCliResources({ home: options.home, resourcesRoot: options.resourcesRoot });
|
|
3110
3068
|
} catch (error) {
|
|
3111
3069
|
streams.stderr.write(
|
|
3112
|
-
|
|
3070
|
+
`${operation} failed: ${error instanceof Error ? error.message : String(error)}
|
|
3071
|
+
`
|
|
3113
3072
|
);
|
|
3114
3073
|
return 1;
|
|
3115
3074
|
}
|
|
3116
|
-
streams.stdout.write(
|
|
3117
|
-
|
|
3118
|
-
)
|
|
3075
|
+
streams.stdout.write(`mcp-google-api ${operation} report:
|
|
3076
|
+
`);
|
|
3077
|
+
for (const summary of result.summaries) {
|
|
3078
|
+
streams.stdout.write(
|
|
3079
|
+
` ${summary.cli}: MCP server ${operation === "install" ? "registered" : "removed"}, ${summary.skills} skill(s) ${operation === "install" ? "installed" : "removed"}
|
|
3080
|
+
`
|
|
3081
|
+
);
|
|
3082
|
+
}
|
|
3083
|
+
if (result.errors.length > 0) {
|
|
3084
|
+
streams.stdout.write("Errors:\n");
|
|
3085
|
+
for (const error of result.errors) {
|
|
3086
|
+
streams.stdout.write(` ${error}
|
|
3087
|
+
`);
|
|
3088
|
+
}
|
|
3089
|
+
}
|
|
3119
3090
|
return result.errors.length > 0 ? 1 : 0;
|
|
3120
3091
|
}
|
|
3121
3092
|
function runShowCommand(options) {
|
|
@@ -3130,10 +3101,46 @@ function runShowCommand(options) {
|
|
|
3130
3101
|
return 1;
|
|
3131
3102
|
}
|
|
3132
3103
|
}
|
|
3104
|
+
function isEntryMainModule() {
|
|
3105
|
+
const entryPath = process.argv[1];
|
|
3106
|
+
if (entryPath === void 0 || entryPath === "") {
|
|
3107
|
+
return false;
|
|
3108
|
+
}
|
|
3109
|
+
try {
|
|
3110
|
+
return realpathSync(entryPath) === realpathSync(fileURLToPath(import.meta.url));
|
|
3111
|
+
} catch {
|
|
3112
|
+
return false;
|
|
3113
|
+
}
|
|
3114
|
+
}
|
|
3115
|
+
function describeUndispatchedServerStart(args) {
|
|
3116
|
+
const isServerStart = (args.includes("start") || args.includes("run")) && args.includes("--stdio");
|
|
3117
|
+
if (!isServerStart) {
|
|
3118
|
+
return void 0;
|
|
3119
|
+
}
|
|
3120
|
+
return "CLI dispatch was skipped for this server-start launch: the running artifact is not the mcp-google-api entrypoint (stale build after a distribution rename). Rebuild the local artifact with 'npm run build' (or 'npm run verify'), or refresh the global installation with 'npm install -g mcp-google-api@latest', then reconnect the MCP client.";
|
|
3121
|
+
}
|
|
3122
|
+
function handleUndispatchedEntry(args, streams = process) {
|
|
3123
|
+
const error = describeUndispatchedServerStart(args);
|
|
3124
|
+
if (error === void 0) {
|
|
3125
|
+
return 0;
|
|
3126
|
+
}
|
|
3127
|
+
streams.stderr.write(
|
|
3128
|
+
JSON.stringify(
|
|
3129
|
+
{
|
|
3130
|
+
error,
|
|
3131
|
+
requestedEntry: process.argv[1] ?? null,
|
|
3132
|
+
modulePath: fileURLToPath(import.meta.url),
|
|
3133
|
+
remediation: ["npm run build", "npm install -g mcp-google-api@latest"]
|
|
3134
|
+
},
|
|
3135
|
+
null,
|
|
3136
|
+
2
|
|
3137
|
+
) + "\n"
|
|
3138
|
+
);
|
|
3139
|
+
return 1;
|
|
3140
|
+
}
|
|
3133
3141
|
var isRunDirectly = process.argv[1] && (process.argv[1].endsWith("cli.ts") || process.argv[1].endsWith("cli.js") || process.argv[1].endsWith("mcp-google-api") || process.argv[1].includes("mcp-google-api"));
|
|
3134
|
-
var isMainModule =
|
|
3142
|
+
var isMainModule = isEntryMainModule();
|
|
3135
3143
|
if (isRunDirectly || isMainModule) {
|
|
3136
|
-
loadEnvironment(extractEnvFileOption(process.argv));
|
|
3137
3144
|
let packageVersion = "0.0.0";
|
|
3138
3145
|
try {
|
|
3139
3146
|
const packageJsonPath = resolve5(__dirname, "../package.json");
|
|
@@ -3152,25 +3159,28 @@ if (isRunDirectly || isMainModule) {
|
|
|
3152
3159
|
process.exitCode = 1;
|
|
3153
3160
|
});
|
|
3154
3161
|
} else {
|
|
3155
|
-
const buildServerCommandOptions = (yargs2) =>
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
[
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3162
|
+
const buildServerCommandOptions = (yargs2) => {
|
|
3163
|
+
const settings = loadCliSettings();
|
|
3164
|
+
return yargs2.option("port", {
|
|
3165
|
+
alias: "p",
|
|
3166
|
+
type: "number",
|
|
3167
|
+
description: "Port to run the MCP server on (default: persisted 'port' setting)",
|
|
3168
|
+
default: settings.port
|
|
3169
|
+
}).option("host", {
|
|
3170
|
+
type: "string",
|
|
3171
|
+
description: "Hostname to bind the server to (default: persisted 'host' setting; 0.0.0.0 for all interfaces)",
|
|
3172
|
+
default: settings.host
|
|
3173
|
+
}).option("stdio", {
|
|
3174
|
+
type: "boolean",
|
|
3175
|
+
description: "Use stdio transport instead of HTTP",
|
|
3176
|
+
default: false
|
|
3177
|
+
}).example([
|
|
3178
|
+
["$0 start", "Start HTTP server with default settings"],
|
|
3179
|
+
["$0 start --host 0.0.0.0 --port 3000", "Start HTTP accessible from all interfaces"],
|
|
3180
|
+
["$0 start --stdio", "Start in stdio mode (for MCP clients)"],
|
|
3181
|
+
["$0 run --stdio", "Alias: run the server in stdio mode"]
|
|
3182
|
+
]);
|
|
3183
|
+
};
|
|
3174
3184
|
const runServerCommand = async (argv) => {
|
|
3175
3185
|
const effectiveApiKey = loadConfiguredApiKey();
|
|
3176
3186
|
ApiKeyManager.getInstance().setDefaultApiKey(effectiveApiKey);
|
|
@@ -3195,8 +3205,8 @@ if (isRunDirectly || isMainModule) {
|
|
|
3195
3205
|
const server = new BaseMcpServer(config_default[0].name, tools);
|
|
3196
3206
|
await server.startStdio();
|
|
3197
3207
|
} else {
|
|
3198
|
-
Logger.log("\u{1F5FA}\uFE0F Google
|
|
3199
|
-
Logger.log("
|
|
3208
|
+
Logger.log("\u{1F5FA}\uFE0F MCP Google API server");
|
|
3209
|
+
Logger.log(" Google APIs for AI agents over the Model Context Protocol");
|
|
3200
3210
|
Logger.log("");
|
|
3201
3211
|
if (effectiveApiKey === void 0) {
|
|
3202
3212
|
Logger.log("\u26A0\uFE0F Google Maps API Key not found!");
|
|
@@ -3231,8 +3241,7 @@ Usage: mcp-google-api <command> [options]
|
|
|
3231
3241
|
}).option("apikey", {
|
|
3232
3242
|
alias: "k",
|
|
3233
3243
|
type: "string",
|
|
3234
|
-
description: "Google Maps API key
|
|
3235
|
-
default: process.env.GOOGLE_MAPS_API_KEY
|
|
3244
|
+
description: "Google Maps API key override for live probes (default: persisted api-key setting)"
|
|
3236
3245
|
}).example([
|
|
3237
3246
|
["$0 doctor", "Check local runtime and API key configuration without network calls"],
|
|
3238
3247
|
["$0 doctor --live", "Also verify three Google Maps APIs with billable requests"],
|
|
@@ -3245,33 +3254,10 @@ Usage: mcp-google-api <command> [options]
|
|
|
3245
3254
|
process.exitCode = 1;
|
|
3246
3255
|
return;
|
|
3247
3256
|
}
|
|
3248
|
-
const
|
|
3257
|
+
const effectiveKey = (argv.apikey ?? "").trim() || loadConfiguredApiKey();
|
|
3258
|
+
const report = await runDoctor(packageVersion, effectiveKey, argv.live, void 0, {
|
|
3249
3259
|
probes: parsedProbes.probes,
|
|
3250
|
-
config: resolveRuntimeConfig(
|
|
3251
|
-
});
|
|
3252
|
-
process.stdout.write(JSON.stringify(report, null, 2) + "\n");
|
|
3253
|
-
process.exitCode = report.success ? 0 : 1;
|
|
3254
|
-
}
|
|
3255
|
-
).command(
|
|
3256
|
-
"setup",
|
|
3257
|
-
"Non-interactive onboarding: verify local readiness, optionally scaffold .env",
|
|
3258
|
-
(yargs2) => yargs2.option("write-env", {
|
|
3259
|
-
type: "boolean",
|
|
3260
|
-
default: false,
|
|
3261
|
-
description: "Create .env from .env.example in --env-dir when it does not exist (never overwrites)"
|
|
3262
|
-
}).option("env-dir", {
|
|
3263
|
-
type: "string",
|
|
3264
|
-
default: process.cwd(),
|
|
3265
|
-
description: "Directory where .env is looked up / created"
|
|
3266
|
-
}).example([
|
|
3267
|
-
["$0 setup", "Check readiness without writing anything"],
|
|
3268
|
-
["$0 setup --write-env", "Also scaffold .env from the package template"]
|
|
3269
|
-
]),
|
|
3270
|
-
async (argv) => {
|
|
3271
|
-
const report = await runSetup({
|
|
3272
|
-
packageDir: resolve5(__dirname, ".."),
|
|
3273
|
-
targetDir: argv.envDir,
|
|
3274
|
-
writeEnv: argv.writeEnv
|
|
3260
|
+
config: resolveRuntimeConfig(loadCliSettings())
|
|
3275
3261
|
});
|
|
3276
3262
|
process.stdout.write(JSON.stringify(report, null, 2) + "\n");
|
|
3277
3263
|
process.exitCode = report.success ? 0 : 1;
|
|
@@ -3305,9 +3291,6 @@ Usage: mcp-google-api <command> [options]
|
|
|
3305
3291
|
type: "number",
|
|
3306
3292
|
default: 3e3,
|
|
3307
3293
|
description: "Port used in the URL (transport http)"
|
|
3308
|
-
}).option("env-key", {
|
|
3309
|
-
type: "string",
|
|
3310
|
-
description: "Emit a ${NAME} variable reference instead of a literal placeholder (expansion is client-specific)"
|
|
3311
3294
|
}).option("name", {
|
|
3312
3295
|
type: "string",
|
|
3313
3296
|
default: "mcp-google-api",
|
|
@@ -3325,7 +3308,6 @@ Usage: mcp-google-api <command> [options]
|
|
|
3325
3308
|
commandPath: argv.command,
|
|
3326
3309
|
host: argv.host,
|
|
3327
3310
|
port: argv.port,
|
|
3328
|
-
envKey: argv.envKey,
|
|
3329
3311
|
serverName: argv.name
|
|
3330
3312
|
});
|
|
3331
3313
|
}
|
|
@@ -3377,6 +3359,54 @@ Usage: mcp-google-api <command> [options]
|
|
|
3377
3359
|
home: argv.home
|
|
3378
3360
|
});
|
|
3379
3361
|
}
|
|
3362
|
+
).command(
|
|
3363
|
+
"tools <tools>",
|
|
3364
|
+
"Persist the enabled-tool allowlist in the configuration file ('*' = all 18 tools)",
|
|
3365
|
+
(yargs2) => yargs2.positional("tools", {
|
|
3366
|
+
type: "string",
|
|
3367
|
+
describe: "Comma-separated tool names (e.g. maps_geocode,maps_search_places) or '*' for all"
|
|
3368
|
+
}).option("home", {
|
|
3369
|
+
type: "string",
|
|
3370
|
+
description: "Home-directory override for the settings file (default: user home)"
|
|
3371
|
+
}).example([
|
|
3372
|
+
["$0 tools '*'", "Expose every registered tool (default)"],
|
|
3373
|
+
["$0 tools maps_geocode,maps_search_places", "Expose only the named tools"]
|
|
3374
|
+
]),
|
|
3375
|
+
(argv) => {
|
|
3376
|
+
process.exitCode = runToolsCommand(argv.tools, {
|
|
3377
|
+
home: argv.home
|
|
3378
|
+
});
|
|
3379
|
+
}
|
|
3380
|
+
).command(
|
|
3381
|
+
"host <host>",
|
|
3382
|
+
"Persist the HTTP bind host in the configuration file (default 127.0.0.1)",
|
|
3383
|
+
(yargs2) => yargs2.positional("host", {
|
|
3384
|
+
type: "string",
|
|
3385
|
+
describe: "Bind hostname (e.g. 127.0.0.1 loopback; 0.0.0.0 for all interfaces)"
|
|
3386
|
+
}).option("home", {
|
|
3387
|
+
type: "string",
|
|
3388
|
+
description: "Home-directory override for the settings file (default: user home)"
|
|
3389
|
+
}).example([["$0 host 0.0.0.0", "Bind the HTTP server on all interfaces"]]),
|
|
3390
|
+
(argv) => {
|
|
3391
|
+
process.exitCode = runHostCommand(argv.host, {
|
|
3392
|
+
home: argv.home
|
|
3393
|
+
});
|
|
3394
|
+
}
|
|
3395
|
+
).command(
|
|
3396
|
+
"port <port>",
|
|
3397
|
+
"Persist the HTTP listen port in the configuration file (default 3000)",
|
|
3398
|
+
(yargs2) => yargs2.positional("port", {
|
|
3399
|
+
type: "number",
|
|
3400
|
+
describe: "Listen port (integer 1-65535)"
|
|
3401
|
+
}).option("home", {
|
|
3402
|
+
type: "string",
|
|
3403
|
+
description: "Home-directory override for the settings file (default: user home)"
|
|
3404
|
+
}).example([["$0 port 3100", "Serve Streamable HTTP on port 3100"]]),
|
|
3405
|
+
(argv) => {
|
|
3406
|
+
process.exitCode = runPortCommand(argv.port === void 0 ? void 0 : String(argv.port), {
|
|
3407
|
+
home: argv.home
|
|
3408
|
+
});
|
|
3409
|
+
}
|
|
3380
3410
|
).command(
|
|
3381
3411
|
"api-key <key>",
|
|
3382
3412
|
"Save the Google Maps API key in the user settings and refresh every enabled CLI registration",
|
|
@@ -3521,7 +3551,7 @@ Usage: mcp-google-api <command> [options]
|
|
|
3521
3551
|
if (trimmed) lines.push(trimmed);
|
|
3522
3552
|
}
|
|
3523
3553
|
} else {
|
|
3524
|
-
if (!
|
|
3554
|
+
if (!existsSync3(argv.input)) {
|
|
3525
3555
|
console.error(`Error: File not found: ${argv.input}`);
|
|
3526
3556
|
process.exit(1);
|
|
3527
3557
|
}
|
|
@@ -3592,11 +3622,7 @@ Usage: mcp-google-api <command> [options]
|
|
|
3592
3622
|
() => {
|
|
3593
3623
|
cli.showHelp("log");
|
|
3594
3624
|
}
|
|
3595
|
-
).version(packageVersion).alias("version", "v").
|
|
3596
|
-
type: "string",
|
|
3597
|
-
global: true,
|
|
3598
|
-
description: "Load environment variables from this exact file (error if missing; no implicit .env fallback)"
|
|
3599
|
-
}).epilog(
|
|
3625
|
+
).version(packageVersion).alias("version", "v").epilog(
|
|
3600
3626
|
`Management commands (single flags, dispatched before subcommands):
|
|
3601
3627
|
--ver Print the installed version and force one update check
|
|
3602
3628
|
--version Alias of --ver
|
|
@@ -3605,13 +3631,16 @@ Usage: mcp-google-api <command> [options]
|
|
|
3605
3631
|
).help();
|
|
3606
3632
|
cli.parse();
|
|
3607
3633
|
}
|
|
3634
|
+
} else {
|
|
3635
|
+
process.exitCode = handleUndispatchedEntry(process.argv.slice(2));
|
|
3608
3636
|
}
|
|
3609
3637
|
export {
|
|
3610
3638
|
DOCTOR_PROBES,
|
|
3611
3639
|
EXEC_TOOLS,
|
|
3612
3640
|
createShutdownHandler,
|
|
3641
|
+
describeUndispatchedServerStart,
|
|
3613
3642
|
execTool,
|
|
3614
|
-
|
|
3643
|
+
handleUndispatchedEntry,
|
|
3615
3644
|
installShutdownHandlers,
|
|
3616
3645
|
parseDoctorProbes,
|
|
3617
3646
|
runApiKeyCommand,
|
|
@@ -3619,10 +3648,12 @@ export {
|
|
|
3619
3648
|
runDoctor,
|
|
3620
3649
|
runEnableDisableCliCommand,
|
|
3621
3650
|
runExecCommand,
|
|
3651
|
+
runHostCommand,
|
|
3622
3652
|
runInstallUninstallCommand,
|
|
3623
|
-
|
|
3653
|
+
runPortCommand,
|
|
3624
3654
|
runShowCommand,
|
|
3625
3655
|
runSnippetCommand,
|
|
3656
|
+
runToolsCommand,
|
|
3626
3657
|
startServer
|
|
3627
3658
|
};
|
|
3628
3659
|
//# sourceMappingURL=cli.js.map
|