mcp-google-map 0.9.0 → 0.13.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/dist/cli.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  Logger,
4
4
  PlacesSearcher
5
- } from "./chunk-OAML6T2D.js";
5
+ } from "./chunk-VCFXCLJF.js";
6
6
 
7
7
  // src/cli.ts
8
8
  import { config as dotenvConfig2 } from "dotenv";
@@ -13,75 +13,6 @@ import { hideBin } from "yargs/helpers";
13
13
  // src/utils/configResolver.ts
14
14
  import { config as dotenvConfig } from "dotenv";
15
15
  import { existsSync } from "fs";
16
-
17
- // src/utils/autoUpdate.ts
18
- var AUTO_UPDATE_DISABLED_ENV = "MCP_AUTO_UPDATE_DISABLED";
19
- var DEFAULT_PACKAGE_NAME = "mcp-google-map";
20
- var DEFAULT_REGISTRY_BASE_URL = "https://registry.npmjs.org";
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
- }
62
- function isAutoUpdateDisabled(env = process.env) {
63
- const raw = env[AUTO_UPDATE_DISABLED_ENV];
64
- return typeof raw === "string" && /^(1|true|yes)$/i.test(raw.trim());
65
- }
66
- function isNewerVersion(candidate, current) {
67
- const toSegments = (version) => version.trim().split(".").map((segment) => /^\d+$/.test(segment) ? Number(segment) : Number.NaN);
68
- const candidateSegments = toSegments(candidate);
69
- const currentSegments = toSegments(current);
70
- if (candidateSegments.some((segment) => !Number.isInteger(segment)) || currentSegments.some((segment) => !Number.isInteger(segment))) {
71
- return false;
72
- }
73
- const length = Math.max(candidateSegments.length, currentSegments.length);
74
- for (let index = 0; index < length; index += 1) {
75
- const candidatePart = candidateSegments[index] ?? 0;
76
- const currentPart = currentSegments[index] ?? 0;
77
- if (candidatePart !== currentPart) {
78
- return candidatePart > currentPart;
79
- }
80
- }
81
- return false;
82
- }
83
-
84
- // src/utils/configResolver.ts
85
16
  var NODE_ENGINE_FLOOR = 22;
86
17
  var DEFAULT_HTTP_HOST = "127.0.0.1";
87
18
  var DEFAULT_HTTP_PORT = 3e3;
@@ -116,7 +47,7 @@ function resolveRuntimeConfig(env = process.env, overrides = {}) {
116
47
  portSource,
117
48
  enabledToolsRaw,
118
49
  enabledToolsSource: enabledToolsRaw ? "environment" : "default",
119
- autoUpdateDisabled: isAutoUpdateDisabled(env),
50
+ autoUpdateDisabled: overrides.autoUpdateDisabled === true,
120
51
  envFile: overrides.envFile
121
52
  };
122
53
  }
@@ -178,10 +109,92 @@ import { z } from "zod";
178
109
 
179
110
  // src/utils/requestContext.ts
180
111
  import { AsyncLocalStorage } from "async_hooks";
112
+
113
+ // src/utils/apiKeyManager.ts
114
+ var ApiKeyManager = class _ApiKeyManager {
115
+ constructor() {
116
+ this.defaultApiKey = void 0;
117
+ }
118
+ /**
119
+ * @brief Returns the process-wide singleton instance.
120
+ * @details Lazily constructs the manager; the default key starts unset and
121
+ * MUST be injected by the server-start path before serving requests.
122
+ * Complexity O(1).
123
+ * @return {ApiKeyManager} The singleton instance.
124
+ */
125
+ static getInstance() {
126
+ if (!_ApiKeyManager.instance) {
127
+ _ApiKeyManager.instance = new _ApiKeyManager();
128
+ }
129
+ return _ApiKeyManager.instance;
130
+ }
131
+ /**
132
+ * @brief Injects the startup default API key.
133
+ * @details Called by the server-start command with the key resolved from
134
+ * the persisted settings (REQ-142); undefined clears the default so
135
+ * requests without header/session keys resolve no key (REQ-018, DES-006).
136
+ * Pure in-memory mutation; no environment writes.
137
+ * @param key {string | undefined} Default key from the persisted settings; undefined clears it.
138
+ * @return {void}
139
+ * @satisfies DES-006, REQ-142
140
+ */
141
+ setDefaultApiKey(key) {
142
+ this.defaultApiKey = key;
143
+ }
144
+ /**
145
+ * @brief Resolves the effective API key for one request.
146
+ * @details Priority: `X-Google-Maps-API-Key` header, then `Authorization:
147
+ * Bearer` token, then the session key, then the injected default key
148
+ * (DES-005, REQ-016, REQ-017, REQ-018). Complexity O(1).
149
+ * @param req {Request | undefined} Incoming HTTP request; undefined skips header resolution.
150
+ * @param sessionApiKey {string | undefined} Session-scoped key; consulted before the default.
151
+ * @return {string | undefined} Effective key; undefined when nothing resolves.
152
+ * @satisfies DES-005, REQ-016, REQ-017, REQ-018
153
+ */
154
+ getApiKey(req, sessionApiKey) {
155
+ if (req) {
156
+ const headerApiKey = req.headers["x-google-maps-api-key"];
157
+ if (headerApiKey) {
158
+ return headerApiKey;
159
+ }
160
+ const authHeader = req.headers["authorization"];
161
+ if (authHeader && authHeader.startsWith("Bearer ")) {
162
+ return authHeader.substring(7);
163
+ }
164
+ }
165
+ if (sessionApiKey) {
166
+ return sessionApiKey;
167
+ }
168
+ return this.defaultApiKey;
169
+ }
170
+ /**
171
+ * @brief Reports whether any API key is resolvable for a request.
172
+ * @details Delegates to `getApiKey` truthiness. Complexity O(1).
173
+ * @param req {Request | undefined} Incoming HTTP request.
174
+ * @param sessionApiKey {string | undefined} Session-scoped key.
175
+ * @return {boolean} True when a key resolves.
176
+ */
177
+ hasApiKey(req, sessionApiKey) {
178
+ return !!this.getApiKey(req, sessionApiKey);
179
+ }
180
+ /**
181
+ * @brief Validates the API key format.
182
+ * @details Accepts 20-50 characters of `[A-Za-z0-9_-]` (REQ-020). Pure;
183
+ * complexity O(n) in key length.
184
+ * @param key {string} Candidate key.
185
+ * @return {boolean} True when the format is valid.
186
+ * @satisfies REQ-020
187
+ */
188
+ isValidApiKeyFormat(key) {
189
+ return /^[A-Za-z0-9_-]{20,50}$/.test(key);
190
+ }
191
+ };
192
+
193
+ // src/utils/requestContext.ts
181
194
  var requestContextStorage = new AsyncLocalStorage();
182
195
  function getCurrentApiKey() {
183
196
  const context = requestContextStorage.getStore();
184
- return context?.apiKey || process.env.GOOGLE_MAPS_API_KEY;
197
+ return context?.apiKey || ApiKeyManager.getInstance().getApiKey();
185
198
  }
186
199
  function runWithContext(context, fn) {
187
200
  return requestContextStorage.run(context, fn);
@@ -1210,62 +1223,6 @@ import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
1210
1223
  import express from "express";
1211
1224
  import { randomUUID } from "crypto";
1212
1225
  import { z as z19 } from "zod";
1213
-
1214
- // src/utils/apiKeyManager.ts
1215
- var ApiKeyManager = class _ApiKeyManager {
1216
- constructor() {
1217
- this.defaultApiKey = process.env.GOOGLE_MAPS_API_KEY;
1218
- }
1219
- static getInstance() {
1220
- if (!_ApiKeyManager.instance) {
1221
- _ApiKeyManager.instance = new _ApiKeyManager();
1222
- }
1223
- return _ApiKeyManager.instance;
1224
- }
1225
- /**
1226
- * Set the default API key (from command line or environment)
1227
- */
1228
- setDefaultApiKey(key) {
1229
- this.defaultApiKey = key;
1230
- process.env.GOOGLE_MAPS_API_KEY = key;
1231
- }
1232
- /**
1233
- * Get API key with priority:
1234
- * 1. HTTP Header (X-Google-Maps-API-Key or Authorization: Bearer)
1235
- * 2. Session-specific API key
1236
- * 3. Default API key (command line or environment)
1237
- */
1238
- getApiKey(req, sessionApiKey) {
1239
- if (req) {
1240
- const headerApiKey = req.headers["x-google-maps-api-key"];
1241
- if (headerApiKey) {
1242
- return headerApiKey;
1243
- }
1244
- const authHeader = req.headers["authorization"];
1245
- if (authHeader && authHeader.startsWith("Bearer ")) {
1246
- return authHeader.substring(7);
1247
- }
1248
- }
1249
- if (sessionApiKey) {
1250
- return sessionApiKey;
1251
- }
1252
- return this.defaultApiKey;
1253
- }
1254
- /**
1255
- * Check if any API key is available
1256
- */
1257
- hasApiKey(req, sessionApiKey) {
1258
- return !!this.getApiKey(req, sessionApiKey);
1259
- }
1260
- /**
1261
- * Validate API key format (basic validation)
1262
- */
1263
- isValidApiKeyFormat(key) {
1264
- return /^[A-Za-z0-9_-]{20,50}$/.test(key);
1265
- }
1266
- };
1267
-
1268
- // src/core/BaseMcpServer.ts
1269
1226
  var VERSION = "0.0.1";
1270
1227
  var BaseMcpServer = class {
1271
1228
  constructor(name, tools) {
@@ -1443,7 +1400,7 @@ var BaseMcpServer = class {
1443
1400
 
1444
1401
  // src/cli.ts
1445
1402
  import { fileURLToPath } from "url";
1446
- import { dirname as dirname2 } from "path";
1403
+ import { dirname as dirname3 } from "path";
1447
1404
  import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync4 } from "fs";
1448
1405
  import { createInterface } from "readline";
1449
1406
 
@@ -1453,12 +1410,80 @@ import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync a
1453
1410
  import { homedir } from "os";
1454
1411
  import { dirname, join as join2 } from "path";
1455
1412
 
1413
+ // src/utils/autoUpdate.ts
1414
+ var AUTO_UPDATE_DISABLED_ENV = "MCP_AUTO_UPDATE_DISABLED";
1415
+ var DEFAULT_PACKAGE_NAME = "mcp-google-map";
1416
+ var DEFAULT_REGISTRY_BASE_URL = "https://registry.npmjs.org";
1417
+ var DEFAULT_UPDATE_CHECK_TIMEOUT_MS = 2e3;
1418
+ async function probeLatestVersion(deps = {}) {
1419
+ const env = deps.env;
1420
+ if (env !== void 0 && isAutoUpdateDisabled(env)) {
1421
+ return { status: "disabled" };
1422
+ }
1423
+ const currentVersion = deps.currentVersion;
1424
+ if (!currentVersion) {
1425
+ return { status: "unknown-version" };
1426
+ }
1427
+ const packageName = deps.packageName ?? DEFAULT_PACKAGE_NAME;
1428
+ const registryBaseUrl = deps.registryBaseUrl ?? DEFAULT_REGISTRY_BASE_URL;
1429
+ const timeoutMs = deps.timeoutMs ?? DEFAULT_UPDATE_CHECK_TIMEOUT_MS;
1430
+ const fetchImpl = deps.fetchImpl ?? fetch;
1431
+ try {
1432
+ const response = await fetchImpl(`${registryBaseUrl}/${packageName}/latest`, {
1433
+ headers: { accept: "application/json" },
1434
+ signal: AbortSignal.timeout(timeoutMs)
1435
+ });
1436
+ if (!response.ok) {
1437
+ return {
1438
+ status: "error",
1439
+ message: `registry responded with HTTP ${response.status}.`
1440
+ };
1441
+ }
1442
+ const payload = await response.json();
1443
+ if (typeof payload?.version !== "string") {
1444
+ return {
1445
+ status: "error",
1446
+ message: "registry response has no version field."
1447
+ };
1448
+ }
1449
+ if (!isNewerVersion(payload.version, currentVersion)) {
1450
+ return { status: "current", currentVersion };
1451
+ }
1452
+ return { status: "newer", currentVersion, latestVersion: payload.version };
1453
+ } catch (error) {
1454
+ const message = error instanceof Error ? error.message : String(error);
1455
+ return { status: "error", message };
1456
+ }
1457
+ }
1458
+ function isAutoUpdateDisabled(env = process.env) {
1459
+ const raw = env[AUTO_UPDATE_DISABLED_ENV];
1460
+ return typeof raw === "string" && /^(1|true|yes)$/i.test(raw.trim());
1461
+ }
1462
+ function isNewerVersion(candidate, current) {
1463
+ const toSegments = (version) => version.trim().split(".").map((segment) => /^\d+$/.test(segment) ? Number(segment) : Number.NaN);
1464
+ const candidateSegments = toSegments(candidate);
1465
+ const currentSegments = toSegments(current);
1466
+ if (candidateSegments.some((segment) => !Number.isInteger(segment)) || currentSegments.some((segment) => !Number.isInteger(segment))) {
1467
+ return false;
1468
+ }
1469
+ const length = Math.max(candidateSegments.length, currentSegments.length);
1470
+ for (let index = 0; index < length; index += 1) {
1471
+ const candidatePart = candidateSegments[index] ?? 0;
1472
+ const currentPart = currentSegments[index] ?? 0;
1473
+ if (candidatePart !== currentPart) {
1474
+ return candidatePart > currentPart;
1475
+ }
1476
+ }
1477
+ return false;
1478
+ }
1479
+
1456
1480
  // src/utils/cliSettings.ts
1457
1481
  import { mkdirSync, readFileSync, renameSync, writeFileSync } from "fs";
1458
1482
  import { join, resolve } from "path";
1459
1483
  var SETTINGS_PROGRAM_NAME = "mcp-google-map";
1460
1484
  var CLI_SETTINGS_RELATIVE = [".config", SETTINGS_PROGRAM_NAME, "config.json"];
1461
1485
  var MCP_SERVER_NAME = "mcp-google-map";
1486
+ var INSTALL_API_KEY_PLACEHOLDER = "__RUN_mcp-google-map_api-key_TO_CONFIGURE_YOUR_API_KEY__";
1462
1487
  function resolveCliSettingsPath(home) {
1463
1488
  const root = home ?? (process.env.HOME ?? process.cwd());
1464
1489
  return resolve(root, ...CLI_SETTINGS_RELATIVE);
@@ -1473,23 +1498,40 @@ function uniqueStrings(value) {
1473
1498
  }
1474
1499
  return result;
1475
1500
  }
1501
+ function isUnsetApiKey(value) {
1502
+ return value === void 0 || value === null || value.trim().length === 0 || value === INSTALL_API_KEY_PLACEHOLDER;
1503
+ }
1476
1504
  function loadCliSettings(home) {
1477
1505
  try {
1478
1506
  const raw = JSON.parse(readFileSync(resolveCliSettingsPath(home), "utf-8"));
1479
1507
  if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
1480
- return { enabled: [] };
1508
+ return { enabled: [], apiKey: "", autoUpdate: true };
1481
1509
  }
1482
- return { enabled: uniqueStrings(raw.enabled) };
1510
+ const record = raw;
1511
+ return {
1512
+ enabled: uniqueStrings(record.enabled),
1513
+ apiKey: typeof record.apiKey === "string" ? record.apiKey : "",
1514
+ autoUpdate: typeof record.autoUpdate === "boolean" ? record.autoUpdate : true
1515
+ };
1483
1516
  } catch {
1484
- return { enabled: [] };
1517
+ return { enabled: [], apiKey: "", autoUpdate: true };
1485
1518
  }
1486
1519
  }
1520
+ function loadConfiguredApiKey(home) {
1521
+ const apiKey = loadCliSettings(home).apiKey;
1522
+ return isUnsetApiKey(apiKey) ? void 0 : apiKey;
1523
+ }
1487
1524
  function saveCliSettings(settings, home) {
1488
1525
  const path = resolveCliSettingsPath(home);
1489
1526
  const temporary = `${path}.tmp`;
1527
+ const payload = {
1528
+ enabled: uniqueStrings(settings.enabled),
1529
+ apiKey: typeof settings.apiKey === "string" ? settings.apiKey : "",
1530
+ autoUpdate: settings.autoUpdate === true
1531
+ };
1490
1532
  try {
1491
1533
  mkdirSync(join(path, ".."), { recursive: true });
1492
- writeFileSync(temporary, JSON.stringify({ enabled: uniqueStrings(settings.enabled) }, null, 2) + "\n", "utf-8");
1534
+ writeFileSync(temporary, JSON.stringify(payload, null, 2) + "\n", "utf-8");
1493
1535
  renameSync(temporary, path);
1494
1536
  } catch (error) {
1495
1537
  throw new Error(`settings write failed: ${error instanceof Error ? error.message : String(error)}`);
@@ -1772,7 +1814,22 @@ async function runVersionCommand(options, deps = {}) {
1772
1814
  `);
1773
1815
  return 0;
1774
1816
  }
1817
+ function isUpgradeNecessary(status) {
1818
+ return status !== "current";
1819
+ }
1775
1820
  async function runUpgradeCommand(options, deps = {}) {
1821
+ const precheck = await probeLatestVersion({
1822
+ ...deps.idleCheck,
1823
+ currentVersion: options.currentVersion
1824
+ });
1825
+ if (!isUpgradeNecessary(precheck.status)) {
1826
+ const stdout2 = deps.stdout ?? process.stdout;
1827
+ stdout2.write(
1828
+ `${DEFAULT_PACKAGE_NAME} ${options.currentVersion} is already the latest available version.
1829
+ `
1830
+ );
1831
+ return 0;
1832
+ }
1776
1833
  const upgraded = runPackageManagerCommand(PACKAGE_UPGRADE_COMMAND, deps);
1777
1834
  if (upgraded !== 0) {
1778
1835
  return upgraded;
@@ -1920,6 +1977,7 @@ function renderClientSnippet(options = {}) {
1920
1977
 
1921
1978
  // src/utils/cliTargets.ts
1922
1979
  import {
1980
+ appendFileSync,
1923
1981
  cpSync,
1924
1982
  existsSync as existsSync2,
1925
1983
  mkdirSync as mkdirSync3,
@@ -2114,30 +2172,46 @@ function listFilesRecursive(root) {
2114
2172
  walk(root);
2115
2173
  return files;
2116
2174
  }
2117
- function syncSkillTree(source, destination) {
2175
+ var SKILL_DOCUMENT_FILE = "SKILL.md";
2176
+ function syncSkillTree(source, destination, layout = {}) {
2118
2177
  mkdirSync3(destination, { recursive: true });
2119
- cpSync(source, destination, { recursive: true, force: true });
2120
- const expected = new Set(listFilesRecursive(source));
2178
+ cpSync(source, destination, {
2179
+ recursive: true,
2180
+ force: true,
2181
+ filter: (entry) => layout.debugPath === void 0 || relative(source, entry).split("\\").join("/") !== layout.debugPath
2182
+ });
2183
+ const expected = new Set(listFilesRecursive(source).filter((rel) => rel !== layout.debugPath));
2121
2184
  const existing = listFilesRecursive(destination);
2122
2185
  existing.sort((a, b) => b.length - a.length);
2123
2186
  for (const rel of existing) {
2124
2187
  if (expected.has(rel)) continue;
2125
2188
  rmSync(join3(destination, rel), { recursive: true, force: true });
2126
2189
  }
2190
+ if (layout.includeDebug === true && layout.debugPath !== void 0) {
2191
+ appendFileSync(
2192
+ join3(destination, ...SKILL_DOCUMENT_FILE.split("/")),
2193
+ readFileSync3(join3(source, ...layout.debugPath.split("/")))
2194
+ );
2195
+ }
2127
2196
  }
2128
2197
  function removeSkillDir(skillDir) {
2129
2198
  rmSync(skillDir, { recursive: true, force: true });
2130
2199
  }
2131
- function compareSkillTree(source, destination) {
2132
- const packaged = listFilesRecursive(source);
2200
+ function compareSkillTree(source, destination, layout = {}) {
2201
+ const packaged = listFilesRecursive(source).filter((rel) => rel !== layout.debugPath);
2133
2202
  if (packaged.length === 0) return "MISSING";
2134
2203
  if (!existsSync2(destination)) return "MISSING";
2135
2204
  for (const rel of packaged) {
2136
2205
  const target = join3(destination, ...rel.split("/"));
2137
2206
  if (!existsSync2(target)) return "MISSING";
2138
- if (readFileSync3(target).toString("utf-8") !== readFileSync3(join3(source, ...rel.split("/"))).toString("utf-8")) {
2139
- return "STALE";
2207
+ const packagedText = readFileSync3(join3(source, ...rel.split("/"))).toString("utf-8");
2208
+ const installedText = readFileSync3(target).toString("utf-8");
2209
+ if (installedText === packagedText) continue;
2210
+ if (rel === SKILL_DOCUMENT_FILE && layout.debugPath !== void 0) {
2211
+ const overlayText = readFileSync3(join3(source, ...layout.debugPath.split("/"))).toString("utf-8");
2212
+ if (installedText === packagedText + overlayText) continue;
2140
2213
  }
2214
+ return "STALE";
2141
2215
  }
2142
2216
  return "OK";
2143
2217
  }
@@ -2167,13 +2241,9 @@ function applyServerRegistration(target, serverName, entry, home) {
2167
2241
 
2168
2242
  // src/utils/cliInstaller.ts
2169
2243
  import { existsSync as existsSync3, readFileSync as readFileSync4 } from "fs";
2170
- import { join as join4, resolve as resolve3 } from "path";
2171
- function buildServerRegistration(apiKey) {
2172
- const env = {};
2173
- if (apiKey !== void 0 && apiKey.length > 0) {
2174
- env.GOOGLE_MAPS_API_KEY = apiKey;
2175
- }
2176
- return { command: "npx", args: ["-y", MCP_SERVER_NAME, "start", "--stdio"], env };
2244
+ import { dirname as dirname2, join as join4, resolve as resolve3 } from "path";
2245
+ function buildServerRegistration() {
2246
+ return { command: "npx", args: ["-y", MCP_SERVER_NAME, "start", "--stdio"] };
2177
2247
  }
2178
2248
  function resolveSkillsResourcesRoot(packageRoot) {
2179
2249
  const candidate = resolve3(packageRoot, "src", "resources", "skills");
@@ -2202,21 +2272,36 @@ function loadSkillsManifest(resourcesRoot) {
2202
2272
  if (typeof skill.id !== "string" || typeof skill.path !== "string") {
2203
2273
  throw new Error(`skills manifest entry is missing id or path: ${manifestPath}`);
2204
2274
  }
2205
- if (!existsSync3(join4(resourcesRoot, skill.path))) {
2206
- throw new Error(`manifest skill file not found: ${join4(resourcesRoot, skill.path)}`);
2275
+ const skillFile = join4(resourcesRoot, skill.path);
2276
+ if (!existsSync3(skillFile)) {
2277
+ throw new Error(`manifest skill file not found: ${skillFile}`);
2278
+ }
2279
+ if (skill.debug !== void 0 && (typeof skill.debug !== "string" || skill.debug.trim() === "")) {
2280
+ throw new Error(`manifest skill debug path is invalid: ${manifestPath}`);
2281
+ }
2282
+ const debugPath = typeof skill.debug === "string" ? skill.debug : void 0;
2283
+ if (debugPath !== void 0 && !existsSync3(join4(dirname2(resolve3(skillFile)), debugPath))) {
2284
+ throw new Error(`manifest skill debug file not found: ${join4(dirname2(resolve3(skillFile)), debugPath)}`);
2207
2285
  }
2208
2286
  return {
2209
2287
  id: skill.id,
2210
2288
  name: skill.name ?? skill.id,
2211
2289
  version: skill.version ?? "",
2212
2290
  path: skill.path,
2213
- description: skill.description ?? ""
2291
+ description: skill.description ?? "",
2292
+ ...debugPath === void 0 ? {} : { debugPath }
2214
2293
  };
2215
2294
  });
2216
2295
  }
2296
+ function resolveSkillContentLayout(skill, includeDebug) {
2297
+ return {
2298
+ ...skill.debugPath === void 0 ? {} : { debugPath: skill.debugPath },
2299
+ includeDebug: includeDebug && skill.debugPath !== void 0
2300
+ };
2301
+ }
2217
2302
  function installEnabledCliResources(options) {
2218
2303
  const manifest = loadSkillsManifest(options.resourcesRoot);
2219
- const entry = buildServerRegistration(options.apiKey);
2304
+ const entry = buildServerRegistration();
2220
2305
  const result = { summaries: [], errors: [] };
2221
2306
  for (const cli of loadCliSettings(options.home).enabled) {
2222
2307
  const target = CLI_TARGETS[cli];
@@ -2228,7 +2313,11 @@ function installEnabledCliResources(options) {
2228
2313
  applyServerRegistration(target, MCP_SERVER_NAME, entry, options.home);
2229
2314
  let skills = 0;
2230
2315
  for (const skill of manifest) {
2231
- syncSkillTree(join4(options.resourcesRoot, skill.id), join4(target.skillsDir(options.home), skill.id));
2316
+ syncSkillTree(
2317
+ join4(options.resourcesRoot, skill.id),
2318
+ join4(target.skillsDir(options.home), skill.id),
2319
+ resolveSkillContentLayout(skill, options.includeDebugSkills === true)
2320
+ );
2232
2321
  skills += 1;
2233
2322
  }
2234
2323
  result.summaries.push({ cli: target.id, server: 1, skills });
@@ -2350,7 +2439,9 @@ function buildInstallRows(home, resourcesRoot) {
2350
2439
  skill.id,
2351
2440
  target.id,
2352
2441
  "yes",
2353
- compareSkillTree(join5(resourcesRoot, skill.id), join5(target.skillsDir(home), skill.id))
2442
+ compareSkillTree(join5(resourcesRoot, skill.id), join5(target.skillsDir(home), skill.id), {
2443
+ ...skill.debugPath === void 0 ? {} : { debugPath: skill.debugPath }
2444
+ })
2354
2445
  ]);
2355
2446
  }
2356
2447
  }
@@ -2368,7 +2459,7 @@ function buildCliShowReport(options) {
2368
2459
 
2369
2460
  // src/cli.ts
2370
2461
  var __filename = fileURLToPath(import.meta.url);
2371
- var __dirname = dirname2(__filename);
2462
+ var __dirname = dirname3(__filename);
2372
2463
  function extractEnvFileOption(argv) {
2373
2464
  for (let i = 0; i < argv.length; i++) {
2374
2465
  const arg = argv[i];
@@ -2397,9 +2488,7 @@ async function startServer(port, apiKey, host) {
2397
2488
  if (port) {
2398
2489
  process.env.MCP_SERVER_PORT = port.toString();
2399
2490
  }
2400
- if (apiKey) {
2401
- process.env.GOOGLE_MAPS_API_KEY = apiKey;
2402
- }
2491
+ ApiKeyManager.getInstance().setDefaultApiKey(apiKey);
2403
2492
  if (host) {
2404
2493
  process.env.MCP_SERVER_HOST = host;
2405
2494
  }
@@ -2874,6 +2963,80 @@ function runEnableDisableCliCommand(name, enable, options = {}) {
2874
2963
  );
2875
2964
  return 0;
2876
2965
  }
2966
+ function runApiKeyCommand(key, options) {
2967
+ const streams = options.streams ?? { stdout: process.stdout, stderr: process.stderr };
2968
+ const trimmed = (key ?? "").trim();
2969
+ if (!ApiKeyManager.getInstance().isValidApiKeyFormat(trimmed)) {
2970
+ streams.stderr.write(
2971
+ "api-key: invalid Google Maps API key. Expected 20-50 characters of letters, digits, hyphens, or underscores.\nUsage: mcp-google-map api-key <your-api-key>\n"
2972
+ );
2973
+ return 1;
2974
+ }
2975
+ try {
2976
+ const settings = loadCliSettings(options.home);
2977
+ settings.apiKey = trimmed;
2978
+ saveCliSettings(settings, options.home);
2979
+ } catch (error) {
2980
+ const message = error instanceof Error ? error.message : String(error);
2981
+ streams.stderr.write(`api-key: failed to persist the API key: ${message}
2982
+ `);
2983
+ return 1;
2984
+ }
2985
+ streams.stdout.write(`API key configured (${maskSecret(trimmed)}).
2986
+ `);
2987
+ let install;
2988
+ try {
2989
+ install = installEnabledCliResources({ home: options.home, resourcesRoot: options.resourcesRoot });
2990
+ } catch (error) {
2991
+ const message = error instanceof Error ? error.message : String(error);
2992
+ streams.stderr.write(`api-key: install failed: ${message}
2993
+ `);
2994
+ return 1;
2995
+ }
2996
+ if (install.summaries.length === 0 && install.errors.length === 0) {
2997
+ streams.stdout.write("No CLI agent enabled. Run 'mcp-google-map enable <cli name>' first.\n");
2998
+ return 0;
2999
+ }
3000
+ for (const summary of install.summaries) {
3001
+ streams.stdout.write(
3002
+ `${summary.cli}: MCP connector installed (server entries: ${summary.server}, skills: ${summary.skills}).
3003
+ `
3004
+ );
3005
+ }
3006
+ for (const error of install.errors) {
3007
+ streams.stdout.write(`${error}
3008
+ `);
3009
+ }
3010
+ return install.errors.length > 0 ? 1 : 0;
3011
+ }
3012
+ function runAutoUpdateCommand(rawState, options = {}) {
3013
+ const streams = options.streams ?? { stdout: process.stdout, stderr: process.stderr };
3014
+ const normalized = (rawState ?? "").trim().toLowerCase();
3015
+ let enabled;
3016
+ if (normalized === "enable" || normalized === "1") {
3017
+ enabled = true;
3018
+ } else if (normalized === "disable" || normalized === "0") {
3019
+ enabled = false;
3020
+ } else {
3021
+ streams.stderr.write(
3022
+ `auto-update: invalid value "${rawState ?? ""}". Accepted values: enable, disable, 1, 0.
3023
+ `
3024
+ );
3025
+ return 1;
3026
+ }
3027
+ try {
3028
+ const settings = loadCliSettings(options.home);
3029
+ settings.autoUpdate = enabled;
3030
+ saveCliSettings(settings, options.home);
3031
+ } catch (error) {
3032
+ const message = error instanceof Error ? error.message : String(error);
3033
+ streams.stderr.write(`auto-update: failed to persist the setting: ${message}
3034
+ `);
3035
+ return 1;
3036
+ }
3037
+ streams.stdout.write(enabled ? "Auto-update check enabled.\n" : "Auto-update check disabled.\n");
3038
+ return 0;
3039
+ }
2877
3040
  function runInstallUninstallCommand(operation, options) {
2878
3041
  const streams = options.streams ?? { stdout: process.stdout, stderr: process.stderr };
2879
3042
  if (loadCliSettings(options.home).enabled.length === 0) {
@@ -2893,7 +3056,11 @@ function runInstallUninstallCommand(operation, options) {
2893
3056
  }
2894
3057
  let result;
2895
3058
  try {
2896
- result = operation === "install" ? installEnabledCliResources({ home: options.home, apiKey: options.apiKey, resourcesRoot: options.resourcesRoot }) : uninstallEnabledCliResources({ home: options.home, resourcesRoot: options.resourcesRoot });
3059
+ result = operation === "install" ? installEnabledCliResources({
3060
+ home: options.home,
3061
+ resourcesRoot: options.resourcesRoot,
3062
+ includeDebugSkills: options.includeDebugSkills === true
3063
+ }) : uninstallEnabledCliResources({ home: options.home, resourcesRoot: options.resourcesRoot });
2897
3064
  } catch (error) {
2898
3065
  streams.stderr.write(
2899
3066
  JSON.stringify({ success: false, errors: [error instanceof Error ? error.message : String(error)] }, null, 2) + "\n"
@@ -2948,41 +3115,49 @@ if (isRunDirectly || isMainModule) {
2948
3115
  type: "string",
2949
3116
  description: "Hostname to bind the server to (default 127.0.0.1 loopback; 0.0.0.0 for all interfaces)",
2950
3117
  default: process.env.MCP_SERVER_HOST || "127.0.0.1"
2951
- }).option("apikey", {
2952
- alias: "k",
2953
- type: "string",
2954
- description: "Google Maps API key",
2955
- default: process.env.GOOGLE_MAPS_API_KEY
2956
3118
  }).option("stdio", {
2957
3119
  type: "boolean",
2958
3120
  description: "Use stdio transport instead of HTTP",
2959
3121
  default: false
2960
3122
  }).example([
2961
3123
  ["$0 start", "Start HTTP server with default settings"],
2962
- ['$0 start --port 3000 --apikey "your_api_key"', "Start HTTP with custom port and API key"],
2963
3124
  ["$0 start --host 0.0.0.0 --port 3000", "Start HTTP accessible from all interfaces"],
2964
3125
  ["$0 start --stdio", "Start in stdio mode (for MCP clients)"],
2965
3126
  ["$0 run --stdio", "Alias: run the server in stdio mode"]
2966
3127
  ]);
2967
3128
  const runServerCommand = async (argv) => {
2968
- if (argv.apikey) {
2969
- process.env.GOOGLE_MAPS_API_KEY = argv.apikey;
3129
+ const effectiveApiKey = loadConfiguredApiKey();
3130
+ ApiKeyManager.getInstance().setDefaultApiKey(effectiveApiKey);
3131
+ if (loadCliSettings().autoUpdate) {
3132
+ void checkVersionIdle({ currentVersion: packageVersion }).catch(() => void 0);
2970
3133
  }
2971
- void checkVersionIdle({ currentVersion: packageVersion }).catch(() => void 0);
2972
3134
  const tools = filterTools(config_default[0].tools);
2973
3135
  if (argv.stdio) {
3136
+ if (effectiveApiKey === void 0) {
3137
+ process.stderr.write(
3138
+ JSON.stringify(
3139
+ {
3140
+ error: "Google Maps API key is not configured. Run 'mcp-google-map api-key <your-api-key>' to configure it."
3141
+ },
3142
+ null,
3143
+ 2
3144
+ ) + "\n"
3145
+ );
3146
+ process.exitCode = 1;
3147
+ return;
3148
+ }
2974
3149
  const server = new BaseMcpServer(config_default[0].name, tools);
2975
3150
  await server.startStdio();
2976
3151
  } else {
2977
3152
  Logger.log("\u{1F5FA}\uFE0F Google Maps MCP Server");
2978
3153
  Logger.log(" A Model Context Protocol server for Google Maps services");
2979
3154
  Logger.log("");
2980
- if (!argv.apikey) {
3155
+ if (effectiveApiKey === void 0) {
2981
3156
  Logger.log("\u26A0\uFE0F Google Maps API Key not found!");
2982
- Logger.log(" Please provide --apikey parameter or set GOOGLE_MAPS_API_KEY in your .env file");
3157
+ Logger.log(" Run 'mcp-google-map api-key <your-api-key>' to configure it.");
2983
3158
  Logger.log("");
2984
3159
  }
2985
- startServer(argv.port, argv.apikey, argv.host).then((outcome) => {
3160
+ startServer(argv.port, effectiveApiKey, argv.host).then((outcome) => {
2986
3161
  if (outcome.started === 0) {
2987
3162
  process.exit(1);
2988
3163
  }
@@ -3138,23 +3313,59 @@ Usage: mcp-google-map <command> [options]
3138
3313
  home: argv.home
3139
3314
  });
3140
3315
  }
3316
+ ).command(
3317
+ "auto-update <state>",
3318
+ "Enable or disable the automatic update check in the persisted configuration",
3319
+ (yargs2) => yargs2.positional("state", {
3320
+ type: "string",
3321
+ describe: "New state: enable or disable (1 and 0 accepted)"
3322
+ }).option("home", {
3323
+ type: "string",
3324
+ description: "Home-directory override for the settings file (default: user home)"
3325
+ }).example([[
3326
+ "$0 auto-update disable",
3327
+ "Disable the automatic update check in the persisted configuration"
3328
+ ]]),
3329
+ (argv) => {
3330
+ process.exitCode = runAutoUpdateCommand(argv.state, {
3331
+ home: argv.home
3332
+ });
3333
+ }
3334
+ ).command(
3335
+ "api-key <key>",
3336
+ "Save the Google Maps API key in the user settings and refresh every enabled CLI registration",
3337
+ (yargs2) => yargs2.positional("key", {
3338
+ type: "string",
3339
+ describe: "Google Maps API key (20-50 characters: letters, digits, hyphens, underscores)"
3340
+ }).option("home", {
3341
+ type: "string",
3342
+ description: "Home-directory override for CLI configuration paths (default: user home)"
3343
+ }).example([["$0 api-key AIzaSy...", "Save the key and reinstall every enabled CLI"]]),
3344
+ (argv) => {
3345
+ process.exitCode = runApiKeyCommand(argv.key, {
3346
+ home: argv.home,
3347
+ resourcesRoot: resolveSkillsResourcesRoot(resolve5(__dirname, ".."))
3348
+ });
3349
+ }
3141
3350
  ).command(
3142
3351
  "install",
3143
3352
  "Register the mcp-google-map stdio server and install packaged skills for every enabled CLI",
3144
3353
  (yargs2) => yargs2.option("home", {
3145
3354
  type: "string",
3146
3355
  description: "Home-directory override for CLI configuration paths (default: user home)"
3147
- }).option("apikey", {
3148
- alias: "k",
3149
- type: "string",
3150
- description: "Google Maps API key embedded in the registered entry (prefer GOOGLE_MAPS_API_KEY)",
3151
- default: resolveRuntimeConfig(process.env, { envFile: extractEnvFileOption(process.argv) }).apiKey
3152
- }).example([["$0 install", "Register the server and skills in every enabled CLI"]]),
3356
+ }).option("debug-skills", {
3357
+ type: "boolean",
3358
+ description: "Append each skill's packaged debug overlay to the installed SKILL.md",
3359
+ default: false
3360
+ }).example([
3361
+ ["$0 install", "Register the server and skills in every enabled CLI"],
3362
+ ["$0 install --debug-skills", "Also append the packaged debug overlay to every installed skill"]
3363
+ ]),
3153
3364
  (argv) => {
3154
3365
  process.exitCode = runInstallUninstallCommand("install", {
3155
3366
  home: argv.home,
3156
- apiKey: argv.apikey,
3157
- resourcesRoot: resolveSkillsResourcesRoot(resolve5(__dirname, ".."))
3367
+ resourcesRoot: resolveSkillsResourcesRoot(resolve5(__dirname, "..")),
3368
+ includeDebugSkills: argv.debugSkills === true
3158
3369
  });
3159
3370
  }
3160
3371
  ).command(
@@ -3193,11 +3404,6 @@ Usage: mcp-google-map <command> [options]
3193
3404
  }).positional("params", {
3194
3405
  type: "string",
3195
3406
  describe: "JSON parameters string"
3196
- }).option("apikey", {
3197
- alias: "k",
3198
- type: "string",
3199
- description: "Google Maps API key",
3200
- default: process.env.GOOGLE_MAPS_API_KEY
3201
3407
  }).example([
3202
3408
  [`$0 exec geocode '{"address":"Tokyo Tower"}'`, "Geocode an address"],
3203
3409
  [
@@ -3208,11 +3414,12 @@ Usage: mcp-google-map <command> [options]
3208
3414
  ]);
3209
3415
  },
3210
3416
  async (argv) => {
3211
- if (!argv.apikey) {
3417
+ const apiKey = loadConfiguredApiKey();
3418
+ if (!apiKey) {
3212
3419
  process.stderr.write(
3213
3420
  JSON.stringify(
3214
3421
  {
3215
- error: "GOOGLE_MAPS_API_KEY not set. Use --apikey or set GOOGLE_MAPS_API_KEY environment variable."
3422
+ error: "Google Maps API key is not configured. Run 'mcp-google-map api-key <your-api-key>' to configure it."
3216
3423
  },
3217
3424
  null,
3218
3425
  2
@@ -3223,7 +3430,7 @@ Usage: mcp-google-map <command> [options]
3223
3430
  }
3224
3431
  try {
3225
3432
  const params = argv.params ? JSON.parse(argv.params) : {};
3226
- process.exitCode = await runExecCommand(argv.tool, params, argv.apikey);
3433
+ process.exitCode = await runExecCommand(argv.tool, params, apiKey);
3227
3434
  } catch (error) {
3228
3435
  process.stderr.write(JSON.stringify({ error: error.message }, null, 2) + "\n");
3229
3436
  process.exitCode = 1;
@@ -3247,11 +3454,6 @@ Usage: mcp-google-map <command> [options]
3247
3454
  type: "number",
3248
3455
  describe: "Max parallel requests",
3249
3456
  default: 20
3250
- }).option("apikey", {
3251
- alias: "k",
3252
- type: "string",
3253
- description: "Google Maps API key",
3254
- default: process.env.GOOGLE_MAPS_API_KEY
3255
3457
  }).example([
3256
3458
  ["$0 batch-geocode -i addresses.txt", "Geocode to stdout"],
3257
3459
  ["$0 batch-geocode -i addresses.txt -o results.json", "Geocode to file"],
@@ -3259,8 +3461,9 @@ Usage: mcp-google-map <command> [options]
3259
3461
  ]);
3260
3462
  },
3261
3463
  async (argv) => {
3262
- if (!argv.apikey) {
3263
- console.error("Error: GOOGLE_MAPS_API_KEY not set. Use --apikey or set env var.");
3464
+ const apiKey = loadConfiguredApiKey();
3465
+ if (!apiKey) {
3466
+ console.error("Error: Google Maps API key is not configured. Run 'mcp-google-map api-key <your-api-key>'.");
3264
3467
  process.exit(1);
3265
3468
  }
3266
3469
  let lines;
@@ -3282,7 +3485,7 @@ Usage: mcp-google-map <command> [options]
3282
3485
  console.error("Error: No addresses found in input.");
3283
3486
  process.exit(1);
3284
3487
  }
3285
- const searcher = new PlacesSearcher(argv.apikey);
3488
+ const searcher = new PlacesSearcher(apiKey);
3286
3489
  const concurrency = Math.min(Math.max(argv.concurrency, 1), 50);
3287
3490
  const results = [];
3288
3491
  let completed = 0;
@@ -3365,6 +3568,8 @@ export {
3365
3568
  extractEnvFileOption,
3366
3569
  installShutdownHandlers,
3367
3570
  parseDoctorProbes,
3571
+ runApiKeyCommand,
3572
+ runAutoUpdateCommand,
3368
3573
  runDoctor,
3369
3574
  runEnableDisableCliCommand,
3370
3575
  runExecCommand,