@mcp-s/cli 0.0.15 → 0.0.17

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/SKILL.md CHANGED
@@ -17,6 +17,17 @@ Access a single MCP server through the command line. MCP enables interaction wit
17
17
  | `mcp-s-cli call <tool>` | Call tool (reads JSON from stdin if no args) |
18
18
  | `mcp-s-cli call <tool> '<json>'` | Call tool with arguments |
19
19
 
20
+ ## Auth Preflight
21
+
22
+ Before the first use of mcp-s-cli in a session, check auth state — this is instant (local file read, no network):
23
+
24
+ ```bash
25
+ mcp-s-cli check-auth
26
+ ```
27
+
28
+ - **Exit 0**: logged in and token is valid → you may proceed
29
+ - **Exit 4**: not logged in or token expired → run `mcp-s-cli login` before any other use of mcp-s-cli
30
+
20
31
  ## Workflow
21
32
 
22
33
  1. **Discover**: `mcp-s-cli grep <query>` → find tools by keyword; fall back to `mcp-s-cli` only when you need a full inventory
package/dist/SKILL.md CHANGED
@@ -17,6 +17,17 @@ Access a single MCP server through the command line. MCP enables interaction wit
17
17
  | `mcp-s-cli call <tool>` | Call tool (reads JSON from stdin if no args) |
18
18
  | `mcp-s-cli call <tool> '<json>'` | Call tool with arguments |
19
19
 
20
+ ## Auth Preflight
21
+
22
+ Before the first use of mcp-s-cli in a session, check auth state — this is instant (local file read, no network):
23
+
24
+ ```bash
25
+ mcp-s-cli check-auth
26
+ ```
27
+
28
+ - **Exit 0**: logged in and token is valid → you may proceed
29
+ - **Exit 4**: not logged in or token expired → run `mcp-s-cli login` before any other use of mcp-s-cli
30
+
20
31
  ## Workflow
21
32
 
22
33
  1. **Discover**: `mcp-s-cli grep <query>` → find tools by keyword; fall back to `mcp-s-cli` only when you need a full inventory
package/dist/daemon.js CHANGED
@@ -303,10 +303,23 @@ async function performOAuthFlow(authServerUrl, resourceUrl) {
303
303
  if (open) {
304
304
  try {
305
305
  const { spawn: spawn2 } = await import("child_process");
306
- spawn2(open, [authUrl.toString()], {
307
- detached: true,
308
- stdio: "ignore"
309
- }).unref();
306
+ const urlStr = authUrl.toString();
307
+ let child;
308
+ if (process.platform === "win32") {
309
+ const escapedUrl = urlStr.replace(/&/g, "^&");
310
+ child = spawn2("cmd", ["/c", "start", '""', escapedUrl], {
311
+ detached: true,
312
+ stdio: "ignore"
313
+ });
314
+ } else {
315
+ child = spawn2(open, [urlStr], {
316
+ detached: true,
317
+ stdio: "ignore"
318
+ });
319
+ }
320
+ child.on("error", () => {
321
+ });
322
+ child.unref();
310
323
  } catch {
311
324
  console.error("Could not open browser automatically.");
312
325
  }
@@ -414,7 +427,7 @@ import { join as join2 } from "path";
414
427
  import { fileURLToPath } from "url";
415
428
 
416
429
  // src/version.ts
417
- var VERSION = "0.0.15";
430
+ var VERSION = "0.0.17";
418
431
 
419
432
  // src/client.ts
420
433
  function getRetryConfig(settings) {
package/dist/index.js CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  // src/index.ts
4
4
  import { appendFileSync, mkdirSync as mkdirSync6 } from "fs";
5
- import { homedir as homedir7 } from "os";
6
- import { join as join8 } from "path";
5
+ import { homedir as homedir8 } from "os";
6
+ import { join as join9 } from "path";
7
7
 
8
8
  // src/client.ts
9
9
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
@@ -299,10 +299,23 @@ async function performOAuthFlow(authServerUrl, resourceUrl) {
299
299
  if (open) {
300
300
  try {
301
301
  const { spawn: spawn2 } = await import("child_process");
302
- spawn2(open, [authUrl.toString()], {
303
- detached: true,
304
- stdio: "ignore"
305
- }).unref();
302
+ const urlStr = authUrl.toString();
303
+ let child;
304
+ if (process.platform === "win32") {
305
+ const escapedUrl = urlStr.replace(/&/g, "^&");
306
+ child = spawn2("cmd", ["/c", "start", '""', escapedUrl], {
307
+ detached: true,
308
+ stdio: "ignore"
309
+ });
310
+ } else {
311
+ child = spawn2(open, [urlStr], {
312
+ detached: true,
313
+ stdio: "ignore"
314
+ });
315
+ }
316
+ child.on("error", () => {
317
+ });
318
+ child.unref();
306
319
  } catch {
307
320
  console.error("Could not open browser automatically.");
308
321
  }
@@ -1300,7 +1313,7 @@ async function cleanupOrphanedDaemons() {
1300
1313
  }
1301
1314
 
1302
1315
  // src/version.ts
1303
- var VERSION = "0.0.15";
1316
+ var VERSION = "0.0.17";
1304
1317
 
1305
1318
  // src/client.ts
1306
1319
  function getRetryConfig(settings) {
@@ -1906,17 +1919,90 @@ async function callCommand(options) {
1906
1919
  }
1907
1920
  }
1908
1921
 
1909
- // src/commands/clear.ts
1910
- import { existsSync as existsSync4, mkdirSync as mkdirSync2, rmSync, writeFileSync as writeFileSync2 } from "fs";
1922
+ // src/commands/check-auth.ts
1923
+ import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
1911
1924
  import { homedir as homedir2 } from "os";
1912
- import { dirname as dirname2, join as join3 } from "path";
1913
- var GLOBAL_CONFIG_PATH = join3(
1914
- homedir2(),
1925
+ import { join as join3 } from "path";
1926
+ var EXPIRY_MARGIN_MS = 15 * 60 * 1e3;
1927
+ async function checkAuthCommand(options) {
1928
+ const { configPath } = options;
1929
+ let serverConfig;
1930
+ let raw;
1931
+ try {
1932
+ const loaded = await loadConfig(configPath);
1933
+ serverConfig = loaded.serverConfig;
1934
+ raw = loaded.raw;
1935
+ } catch (err) {
1936
+ console.error(`Not configured: ${err.message}`);
1937
+ process.exit(4 /* AUTH_ERROR */);
1938
+ }
1939
+ if (!isHttpServer(serverConfig)) {
1940
+ console.log("stdio server \u2014 no auth required");
1941
+ process.exit(0);
1942
+ }
1943
+ if (raw.token) {
1944
+ console.log("Authenticated (static token)");
1945
+ process.exit(0);
1946
+ }
1947
+ const authFilePath = join3(homedir2(), ".config", "mcp-s-cli", "auth.json");
1948
+ if (!existsSync4(authFilePath)) {
1949
+ console.error("Not logged in (no auth file found). Run: mcp-s-cli login");
1950
+ process.exit(4 /* AUTH_ERROR */);
1951
+ }
1952
+ let data;
1953
+ try {
1954
+ data = JSON.parse(readFileSync3(authFilePath, "utf-8"));
1955
+ } catch {
1956
+ console.error("Not logged in (auth file unreadable). Run: mcp-s-cli login");
1957
+ process.exit(4 /* AUTH_ERROR */);
1958
+ }
1959
+ const tokens = data.tokens ?? {};
1960
+ const serverOrigin = new URL(serverConfig.url).origin;
1961
+ const matchingKey = Object.keys(tokens).find((key) => {
1962
+ try {
1963
+ return new URL(key).origin === serverOrigin;
1964
+ } catch {
1965
+ return key === serverOrigin || key.startsWith(serverOrigin);
1966
+ }
1967
+ });
1968
+ if (!matchingKey || !tokens[matchingKey]?.access_token) {
1969
+ console.error(`Not logged in to ${serverOrigin}. Run: mcp-s-cli login`);
1970
+ process.exit(4 /* AUTH_ERROR */);
1971
+ }
1972
+ const token = tokens[matchingKey];
1973
+ const expiredAt = token.access_token_expired_at;
1974
+ if (expiredAt) {
1975
+ const remaining = expiredAt - Date.now();
1976
+ if (remaining <= EXPIRY_MARGIN_MS) {
1977
+ const reason = remaining <= 0 ? "token expired" : "token expires in < 15 min";
1978
+ console.error(
1979
+ `Not logged in to ${serverOrigin} (${reason}). Run: mcp-s-cli login`
1980
+ );
1981
+ process.exit(4 /* AUTH_ERROR */);
1982
+ }
1983
+ const hours = Math.floor(remaining / (60 * 60 * 1e3));
1984
+ const minutes = Math.floor(remaining % (60 * 60 * 1e3) / 6e4);
1985
+ const display = hours > 0 ? `${hours}h ${minutes}m` : `${minutes}m`;
1986
+ console.log(
1987
+ `Logged in to ${serverOrigin} (token valid, expires in ${display})`
1988
+ );
1989
+ } else {
1990
+ console.log(`Logged in to ${serverOrigin} (token present, no expiry info)`);
1991
+ }
1992
+ process.exit(0);
1993
+ }
1994
+
1995
+ // src/commands/clear.ts
1996
+ import { existsSync as existsSync5, mkdirSync as mkdirSync2, rmSync, writeFileSync as writeFileSync2 } from "fs";
1997
+ import { homedir as homedir3 } from "os";
1998
+ import { dirname as dirname2, join as join4 } from "path";
1999
+ var GLOBAL_CONFIG_PATH = join4(
2000
+ homedir3(),
1915
2001
  ".config",
1916
2002
  "mcp-s-cli",
1917
2003
  "config.json"
1918
2004
  );
1919
- var AUTH_PATH = join3(homedir2(), ".config", "mcp-s-cli", "auth.json");
2005
+ var AUTH_PATH = join4(homedir3(), ".config", "mcp-s-cli", "auth.json");
1920
2006
  async function clearCommand() {
1921
2007
  mkdirSync2(dirname2(GLOBAL_CONFIG_PATH), { recursive: true });
1922
2008
  writeFileSync2(
@@ -1933,9 +2019,9 @@ async function clearAuthCommand() {
1933
2019
  `, "utf-8");
1934
2020
  console.log(`Cleared auth data from ${AUTH_PATH}`);
1935
2021
  }
1936
- var HISTORY_PATH = join3(homedir2(), ".config", "mcp-s-cli", "history.jsonl");
2022
+ var HISTORY_PATH = join4(homedir3(), ".config", "mcp-s-cli", "history.jsonl");
1937
2023
  function clearHistoryCommand() {
1938
- if (!existsSync4(HISTORY_PATH)) {
2024
+ if (!existsSync5(HISTORY_PATH)) {
1939
2025
  console.log("No history file found.");
1940
2026
  return;
1941
2027
  }
@@ -1944,9 +2030,9 @@ function clearHistoryCommand() {
1944
2030
  }
1945
2031
 
1946
2032
  // src/commands/config.ts
1947
- import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
1948
- import { homedir as homedir3 } from "os";
1949
- import { dirname as dirname3, join as join4, resolve as resolve2 } from "path";
2033
+ import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
2034
+ import { homedir as homedir4 } from "os";
2035
+ import { dirname as dirname3, join as join5, resolve as resolve2 } from "path";
1950
2036
  var TOP_LEVEL_KEYS = [
1951
2037
  "enabled",
1952
2038
  "org",
@@ -1969,7 +2055,7 @@ var SETTINGS_KEYS = [
1969
2055
  ];
1970
2056
  var ALL_KNOWN_KEYS = [...TOP_LEVEL_KEYS, ...SETTINGS_KEYS];
1971
2057
  function getDefaultConfigPath() {
1972
- return join4(homedir3(), ".config", "mcp-s-cli", "config.json");
2058
+ return join5(homedir4(), ".config", "mcp-s-cli", "config.json");
1973
2059
  }
1974
2060
  function resolveConfigPath(explicitPath) {
1975
2061
  if (explicitPath) return resolve2(explicitPath);
@@ -1978,8 +2064,8 @@ function resolveConfigPath(explicitPath) {
1978
2064
  return getDefaultConfigPath();
1979
2065
  }
1980
2066
  function readConfigFile(configPath) {
1981
- if (!existsSync5(configPath)) return {};
1982
- const content = readFileSync3(configPath, "utf-8").trim();
2067
+ if (!existsSync6(configPath)) return {};
2068
+ const content = readFileSync4(configPath, "utf-8").trim();
1983
2069
  if (!content) return {};
1984
2070
  try {
1985
2071
  const parsed = JSON.parse(content);
@@ -2060,7 +2146,7 @@ function configGetCommand(opts) {
2060
2146
  const { key, configPath: explicitPath } = opts;
2061
2147
  validateKey(key);
2062
2148
  const configPath = resolveConfigPath(explicitPath);
2063
- if (!existsSync5(configPath)) {
2149
+ if (!existsSync6(configPath)) {
2064
2150
  console.error(
2065
2151
  formatCliError({
2066
2152
  code: 1 /* CLIENT_ERROR */,
@@ -2089,7 +2175,7 @@ function configGetCommand(opts) {
2089
2175
  }
2090
2176
  function configShowCommand(opts) {
2091
2177
  const configPath = resolveConfigPath(opts.configPath);
2092
- if (!existsSync5(configPath)) {
2178
+ if (!existsSync6(configPath)) {
2093
2179
  console.error(
2094
2180
  formatCliError({
2095
2181
  code: 1 /* CLIENT_ERROR */,
@@ -2288,78 +2374,78 @@ async function infoCommand(options) {
2288
2374
  }
2289
2375
 
2290
2376
  // src/commands/init.ts
2291
- import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "fs";
2292
- import { homedir as homedir5 } from "os";
2293
- import { dirname as dirname5, join as join6 } from "path";
2377
+ import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
2378
+ import { homedir as homedir6 } from "os";
2379
+ import { dirname as dirname5, join as join7 } from "path";
2294
2380
  import * as readline from "readline";
2295
2381
 
2296
2382
  // src/commands/install-skill.ts
2297
2383
  import {
2298
- existsSync as existsSync6,
2384
+ existsSync as existsSync7,
2299
2385
  lstatSync,
2300
2386
  mkdirSync as mkdirSync4,
2301
- readFileSync as readFileSync4,
2387
+ readFileSync as readFileSync5,
2302
2388
  readlinkSync,
2303
2389
  rmSync as rmSync2,
2304
2390
  symlinkSync,
2305
2391
  writeFileSync as writeFileSync4
2306
2392
  } from "fs";
2307
- import { homedir as homedir4, platform } from "os";
2308
- import { dirname as dirname4, join as join5, normalize, relative, resolve as resolve3, sep } from "path";
2393
+ import { homedir as homedir5, platform } from "os";
2394
+ import { dirname as dirname4, join as join6, normalize, relative, resolve as resolve3, sep } from "path";
2309
2395
  import { fileURLToPath as fileURLToPath2 } from "url";
2310
- var home = homedir4();
2396
+ var home = homedir5();
2311
2397
  var AGENTS = {
2312
2398
  cursor: {
2313
2399
  displayName: "Cursor",
2314
- globalSkillsDir: join5(home, ".cursor", "skills"),
2315
- detect: () => existsSync6(join5(home, ".cursor"))
2400
+ globalSkillsDir: join6(home, ".cursor", "skills"),
2401
+ detect: () => existsSync7(join6(home, ".cursor"))
2316
2402
  },
2317
2403
  codex: {
2318
2404
  displayName: "Codex",
2319
- globalSkillsDir: join5(
2320
- process.env.CODEX_HOME?.trim() || join5(home, ".codex"),
2405
+ globalSkillsDir: join6(
2406
+ process.env.CODEX_HOME?.trim() || join6(home, ".codex"),
2321
2407
  "skills"
2322
2408
  ),
2323
- detect: () => existsSync6(process.env.CODEX_HOME?.trim() || join5(home, ".codex")) || existsSync6("/etc/codex")
2409
+ detect: () => existsSync7(process.env.CODEX_HOME?.trim() || join6(home, ".codex")) || existsSync7("/etc/codex")
2324
2410
  },
2325
2411
  "claude-code": {
2326
2412
  displayName: "Claude Code",
2327
- globalSkillsDir: join5(
2328
- process.env.CLAUDE_CONFIG_DIR?.trim() || join5(home, ".claude"),
2413
+ globalSkillsDir: join6(
2414
+ process.env.CLAUDE_CONFIG_DIR?.trim() || join6(home, ".claude"),
2329
2415
  "skills"
2330
2416
  ),
2331
- detect: () => existsSync6(
2332
- process.env.CLAUDE_CONFIG_DIR?.trim() || join5(home, ".claude")
2417
+ detect: () => existsSync7(
2418
+ process.env.CLAUDE_CONFIG_DIR?.trim() || join6(home, ".claude")
2333
2419
  )
2334
2420
  },
2335
2421
  windsurf: {
2336
2422
  displayName: "Windsurf",
2337
- globalSkillsDir: join5(home, ".codeium", "windsurf", "skills"),
2338
- detect: () => existsSync6(join5(home, ".codeium", "windsurf"))
2423
+ globalSkillsDir: join6(home, ".codeium", "windsurf", "skills"),
2424
+ detect: () => existsSync7(join6(home, ".codeium", "windsurf"))
2339
2425
  },
2340
2426
  "github-copilot": {
2341
2427
  displayName: "GitHub Copilot",
2342
- globalSkillsDir: join5(home, ".copilot", "skills"),
2343
- detect: () => existsSync6(join5(home, ".copilot"))
2428
+ globalSkillsDir: join6(home, ".copilot", "skills"),
2429
+ detect: () => existsSync7(join6(home, ".copilot"))
2344
2430
  },
2345
2431
  cline: {
2346
2432
  displayName: "Cline",
2347
- globalSkillsDir: join5(home, ".cline", "skills"),
2348
- detect: () => existsSync6(join5(home, ".cline"))
2433
+ globalSkillsDir: join6(home, ".cline", "skills"),
2434
+ detect: () => existsSync7(join6(home, ".cline"))
2349
2435
  },
2350
2436
  roo: {
2351
2437
  displayName: "Roo Code",
2352
- globalSkillsDir: join5(home, ".roo", "skills"),
2353
- detect: () => existsSync6(join5(home, ".roo"))
2438
+ globalSkillsDir: join6(home, ".roo", "skills"),
2439
+ detect: () => existsSync7(join6(home, ".roo"))
2354
2440
  },
2355
2441
  continue: {
2356
2442
  displayName: "Continue",
2357
- globalSkillsDir: join5(home, ".continue", "skills"),
2358
- detect: () => existsSync6(join5(home, ".continue")) || existsSync6(join5(process.cwd(), ".continue"))
2443
+ globalSkillsDir: join6(home, ".continue", "skills"),
2444
+ detect: () => existsSync7(join6(home, ".continue")) || existsSync7(join6(process.cwd(), ".continue"))
2359
2445
  }
2360
2446
  };
2361
2447
  function getCanonicalSkillDir(skillName) {
2362
- return join5(home, ".agents", "skills", skillName);
2448
+ return join6(home, ".agents", "skills", skillName);
2363
2449
  }
2364
2450
  function getCanonicalSkillPath() {
2365
2451
  return getCanonicalSkillDir(SKILL_NAME);
@@ -2410,13 +2496,13 @@ function getSkillMdContent() {
2410
2496
  const __filename = fileURLToPath2(import.meta.url);
2411
2497
  const __dirname = dirname4(__filename);
2412
2498
  const candidates = [
2413
- join5(__dirname, "SKILL.md"),
2414
- join5(__dirname, "..", "SKILL.md"),
2415
- join5(__dirname, "..", "..", "SKILL.md")
2499
+ join6(__dirname, "SKILL.md"),
2500
+ join6(__dirname, "..", "SKILL.md"),
2501
+ join6(__dirname, "..", "..", "SKILL.md")
2416
2502
  ];
2417
2503
  for (const candidate of candidates) {
2418
- if (existsSync6(candidate)) {
2419
- return readFileSync4(candidate, "utf-8");
2504
+ if (existsSync7(candidate)) {
2505
+ return readFileSync5(candidate, "utf-8");
2420
2506
  }
2421
2507
  }
2422
2508
  throw new Error(
@@ -2426,12 +2512,12 @@ function getSkillMdContent() {
2426
2512
  function installSkill() {
2427
2513
  const content = getSkillMdContent();
2428
2514
  const canonicalDir = getCanonicalSkillDir(SKILL_NAME);
2429
- if (!isPathSafe(join5(home, ".agents", "skills"), canonicalDir)) {
2515
+ if (!isPathSafe(join6(home, ".agents", "skills"), canonicalDir)) {
2430
2516
  throw new Error("Invalid skill name: potential path traversal detected");
2431
2517
  }
2432
2518
  rmSync2(canonicalDir, { recursive: true, force: true });
2433
2519
  mkdirSync4(canonicalDir, { recursive: true });
2434
- writeFileSync4(join5(canonicalDir, "SKILL.md"), content, "utf-8");
2520
+ writeFileSync4(join6(canonicalDir, "SKILL.md"), content, "utf-8");
2435
2521
  const result = {
2436
2522
  installed: [],
2437
2523
  skipped: [],
@@ -2443,7 +2529,7 @@ function installSkill() {
2443
2529
  result.skipped.push(agentName);
2444
2530
  continue;
2445
2531
  }
2446
- const agentSkillDir = join5(agent.globalSkillsDir, SKILL_NAME);
2532
+ const agentSkillDir = join6(agent.globalSkillsDir, SKILL_NAME);
2447
2533
  if (!isPathSafe(agent.globalSkillsDir, agentSkillDir)) {
2448
2534
  result.failed.push({
2449
2535
  agent: agentName,
@@ -2456,7 +2542,7 @@ function installSkill() {
2456
2542
  if (!symlinkOk) {
2457
2543
  rmSync2(agentSkillDir, { recursive: true, force: true });
2458
2544
  mkdirSync4(agentSkillDir, { recursive: true });
2459
- writeFileSync4(join5(agentSkillDir, "SKILL.md"), content, "utf-8");
2545
+ writeFileSync4(join6(agentSkillDir, "SKILL.md"), content, "utf-8");
2460
2546
  }
2461
2547
  result.installed.push(agentName);
2462
2548
  } catch (err) {
@@ -2476,8 +2562,8 @@ function clearSkill() {
2476
2562
  canonicalRemoved: false
2477
2563
  };
2478
2564
  for (const [agentName, agent] of Object.entries(AGENTS)) {
2479
- const agentSkillDir = join5(agent.globalSkillsDir, SKILL_NAME);
2480
- if (!existsSync6(agentSkillDir)) {
2565
+ const agentSkillDir = join6(agent.globalSkillsDir, SKILL_NAME);
2566
+ if (!existsSync7(agentSkillDir)) {
2481
2567
  result.skipped.push(agentName);
2482
2568
  continue;
2483
2569
  }
@@ -2492,7 +2578,7 @@ function clearSkill() {
2492
2578
  }
2493
2579
  }
2494
2580
  const canonicalDir = getCanonicalSkillDir(SKILL_NAME);
2495
- if (existsSync6(canonicalDir)) {
2581
+ if (existsSync7(canonicalDir)) {
2496
2582
  try {
2497
2583
  rmSync2(canonicalDir, { recursive: true, force: true });
2498
2584
  result.canonicalRemoved = true;
@@ -2503,12 +2589,12 @@ function clearSkill() {
2503
2589
  }
2504
2590
  function getSkillInstallInfo() {
2505
2591
  return Object.entries(AGENTS).filter(([, agent]) => agent.detect()).map(([agentName, agent]) => {
2506
- const agentSkillDir = join5(agent.globalSkillsDir, SKILL_NAME);
2592
+ const agentSkillDir = join6(agent.globalSkillsDir, SKILL_NAME);
2507
2593
  return {
2508
2594
  agent: agentName,
2509
2595
  displayName: agent.displayName,
2510
2596
  path: agentSkillDir,
2511
- installed: existsSync6(join5(agentSkillDir, "SKILL.md"))
2597
+ installed: existsSync7(join6(agentSkillDir, "SKILL.md"))
2512
2598
  };
2513
2599
  });
2514
2600
  }
@@ -2708,16 +2794,16 @@ ${BAR} ${c(A.cyan, "\u25C6")} `
2708
2794
  }
2709
2795
  }
2710
2796
  var p = { intro, outro, cancel, isCancel, select, text };
2711
- var GLOBAL_CONFIG_PATH2 = join6(
2712
- homedir5(),
2797
+ var GLOBAL_CONFIG_PATH2 = join7(
2798
+ homedir6(),
2713
2799
  ".config",
2714
2800
  "mcp-s-cli",
2715
2801
  "config.json"
2716
2802
  );
2717
2803
  function readExistingConfig() {
2718
2804
  try {
2719
- if (existsSync7(GLOBAL_CONFIG_PATH2)) {
2720
- return JSON.parse(readFileSync5(GLOBAL_CONFIG_PATH2, "utf-8"));
2805
+ if (existsSync8(GLOBAL_CONFIG_PATH2)) {
2806
+ return JSON.parse(readFileSync6(GLOBAL_CONFIG_PATH2, "utf-8"));
2721
2807
  }
2722
2808
  } catch {
2723
2809
  }
@@ -2846,10 +2932,10 @@ async function initInteractive() {
2846
2932
  }
2847
2933
 
2848
2934
  // src/commands/kill-daemon.ts
2849
- import { existsSync as existsSync8, readdirSync as readdirSync2 } from "fs";
2935
+ import { existsSync as existsSync9, readdirSync as readdirSync2 } from "fs";
2850
2936
  function killDaemonCommand() {
2851
2937
  const socketDir = getSocketDir();
2852
- if (!existsSync8(socketDir)) {
2938
+ if (!existsSync9(socketDir)) {
2853
2939
  console.log("No daemons running.");
2854
2940
  return;
2855
2941
  }
@@ -2948,9 +3034,9 @@ async function logoutCommand(options) {
2948
3034
  }
2949
3035
 
2950
3036
  // src/commands/whoami.ts
2951
- import { existsSync as existsSync9, readFileSync as readFileSync6, statSync } from "fs";
2952
- import { homedir as homedir6 } from "os";
2953
- import { join as join7, resolve as resolve4 } from "path";
3037
+ import { existsSync as existsSync10, readFileSync as readFileSync7, statSync } from "fs";
3038
+ import { homedir as homedir7 } from "os";
3039
+ import { join as join8, resolve as resolve4 } from "path";
2954
3040
  function getResolvedConfigPath(explicitPath) {
2955
3041
  if (explicitPath) {
2956
3042
  return resolve4(explicitPath);
@@ -2958,8 +3044,8 @@ function getResolvedConfigPath(explicitPath) {
2958
3044
  if (process.env.MCP_S_CLI_CONFIG_PATH) {
2959
3045
  return resolve4(process.env.MCP_S_CLI_CONFIG_PATH);
2960
3046
  }
2961
- const defaultPath = join7(homedir6(), ".config", "mcp-s-cli", "config.json");
2962
- if (existsSync9(defaultPath)) {
3047
+ const defaultPath = join8(homedir7(), ".config", "mcp-s-cli", "config.json");
3048
+ if (existsSync10(defaultPath)) {
2963
3049
  return defaultPath;
2964
3050
  }
2965
3051
  return null;
@@ -2987,15 +3073,15 @@ async function whoamiCommand(options) {
2987
3073
  console.log(` Error loading config: ${err.message}`);
2988
3074
  return;
2989
3075
  }
2990
- const authFilePath = join7(homedir6(), ".config", "mcp-s-cli", "auth.json");
3076
+ const authFilePath = join8(homedir7(), ".config", "mcp-s-cli", "auth.json");
2991
3077
  console.log("");
2992
3078
  console.log("OAuth Tokens");
2993
3079
  console.log(` Path: ${authFilePath}`);
2994
- if (!existsSync9(authFilePath)) {
3080
+ if (!existsSync10(authFilePath)) {
2995
3081
  console.log(" (no tokens stored)");
2996
3082
  } else {
2997
3083
  try {
2998
- const raw2 = readFileSync6(authFilePath, "utf-8");
3084
+ const raw2 = readFileSync7(authFilePath, "utf-8");
2999
3085
  const data = JSON.parse(raw2);
3000
3086
  const tokenEntries = Object.entries(data.tokens ?? {});
3001
3087
  if (tokenEntries.length === 0) {
@@ -3005,9 +3091,9 @@ async function whoamiCommand(options) {
3005
3091
  console.log(" (could not read auth file)");
3006
3092
  }
3007
3093
  }
3008
- const historyPath = join7(homedir6(), ".config", "mcp-s-cli", "history.jsonl");
3009
- if (existsSync9(historyPath)) {
3010
- const lines = readFileSync6(historyPath, "utf-8").split("\n").filter(Boolean);
3094
+ const historyPath = join8(homedir7(), ".config", "mcp-s-cli", "history.jsonl");
3095
+ if (existsSync10(historyPath)) {
3096
+ const lines = readFileSync7(historyPath, "utf-8").split("\n").filter(Boolean);
3011
3097
  const size = statSync(historyPath).size;
3012
3098
  const sizeStr = size >= 1024 * 1024 ? `${(size / (1024 * 1024)).toFixed(1)} MB` : size >= 1024 ? `${(size / 1024).toFixed(1)} KB` : `${size} B`;
3013
3099
  console.log("");
@@ -3216,6 +3302,10 @@ function parseArgs2(args) {
3216
3302
  result.command = "logout";
3217
3303
  return result;
3218
3304
  }
3305
+ if (firstArg === "check-auth") {
3306
+ result.command = "check-auth";
3307
+ return result;
3308
+ }
3219
3309
  if (firstArg === "clear") {
3220
3310
  result.command = "clear";
3221
3311
  return result;
@@ -3307,6 +3397,7 @@ Usage:
3307
3397
  mcp-s-cli whoami Show config location and auth state
3308
3398
  mcp-s-cli login Log in to the configured server via OAuth
3309
3399
  mcp-s-cli logout Log out (remove stored OAuth tokens)
3400
+ mcp-s-cli check-auth Check auth state offline (no network); exits 0=ok, 4=needs login
3310
3401
  mcp-s-cli config set <key> <value> Set a value in config.json
3311
3402
  mcp-s-cli config get <key> Get a value from config.json
3312
3403
  mcp-s-cli config show Print full config.json
@@ -3348,6 +3439,7 @@ Examples:
3348
3439
  mcp-s-cli whoami # Show config and auth state
3349
3440
  mcp-s-cli login # Authenticate via OAuth
3350
3441
  mcp-s-cli logout # Remove stored OAuth tokens
3442
+ mcp-s-cli check-auth # Check auth state offline (fast preflight)
3351
3443
  mcp-s-cli config set settings.timeout 30 # Set request timeout to 30s
3352
3444
  mcp-s-cli clear # Reset server config
3353
3445
  mcp-s-cli clear-auth # Clear stored auth data
@@ -3371,10 +3463,10 @@ Config File:
3371
3463
  function appendHistory(argv, history) {
3372
3464
  if (!history) return;
3373
3465
  try {
3374
- const dir = join8(homedir7(), ".config", "mcp-s-cli");
3466
+ const dir = join9(homedir8(), ".config", "mcp-s-cli");
3375
3467
  mkdirSync6(dir, { recursive: true });
3376
3468
  const entry = JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), args: argv });
3377
- appendFileSync(join8(dir, "history.jsonl"), `${entry}
3469
+ appendFileSync(join9(dir, "history.jsonl"), `${entry}
3378
3470
  `, "utf8");
3379
3471
  } catch (err) {
3380
3472
  const msg = err instanceof Error ? err.message : String(err);
@@ -3473,6 +3565,9 @@ async function main() {
3473
3565
  case "logout":
3474
3566
  await logoutCommand({ configPath: args.configPath });
3475
3567
  break;
3568
+ case "check-auth":
3569
+ await checkAuthCommand({ configPath: args.configPath });
3570
+ break;
3476
3571
  case "clear":
3477
3572
  await clearCommand();
3478
3573
  break;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcp-s/cli",
3
- "version": "0.0.15",
3
+ "version": "0.0.17",
4
4
  "description": "A lightweight CLI for connecting AI agents to the Webrix MCP Gateway",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",