@mcp-s/cli 0.0.15 → 0.0.16

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
@@ -414,7 +414,7 @@ import { join as join2 } from "path";
414
414
  import { fileURLToPath } from "url";
415
415
 
416
416
  // src/version.ts
417
- var VERSION = "0.0.15";
417
+ var VERSION = "0.0.16";
418
418
 
419
419
  // src/client.ts
420
420
  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";
@@ -1300,7 +1300,7 @@ async function cleanupOrphanedDaemons() {
1300
1300
  }
1301
1301
 
1302
1302
  // src/version.ts
1303
- var VERSION = "0.0.15";
1303
+ var VERSION = "0.0.16";
1304
1304
 
1305
1305
  // src/client.ts
1306
1306
  function getRetryConfig(settings) {
@@ -1906,17 +1906,90 @@ async function callCommand(options) {
1906
1906
  }
1907
1907
  }
1908
1908
 
1909
- // src/commands/clear.ts
1910
- import { existsSync as existsSync4, mkdirSync as mkdirSync2, rmSync, writeFileSync as writeFileSync2 } from "fs";
1909
+ // src/commands/check-auth.ts
1910
+ import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
1911
1911
  import { homedir as homedir2 } from "os";
1912
- import { dirname as dirname2, join as join3 } from "path";
1913
- var GLOBAL_CONFIG_PATH = join3(
1914
- homedir2(),
1912
+ import { join as join3 } from "path";
1913
+ var EXPIRY_MARGIN_MS = 15 * 60 * 1e3;
1914
+ async function checkAuthCommand(options) {
1915
+ const { configPath } = options;
1916
+ let serverConfig;
1917
+ let raw;
1918
+ try {
1919
+ const loaded = await loadConfig(configPath);
1920
+ serverConfig = loaded.serverConfig;
1921
+ raw = loaded.raw;
1922
+ } catch (err) {
1923
+ console.error(`Not configured: ${err.message}`);
1924
+ process.exit(4 /* AUTH_ERROR */);
1925
+ }
1926
+ if (!isHttpServer(serverConfig)) {
1927
+ console.log("stdio server \u2014 no auth required");
1928
+ process.exit(0);
1929
+ }
1930
+ if (raw.token) {
1931
+ console.log("Authenticated (static token)");
1932
+ process.exit(0);
1933
+ }
1934
+ const authFilePath = join3(homedir2(), ".config", "mcp-s-cli", "auth.json");
1935
+ if (!existsSync4(authFilePath)) {
1936
+ console.error("Not logged in (no auth file found). Run: mcp-s-cli login");
1937
+ process.exit(4 /* AUTH_ERROR */);
1938
+ }
1939
+ let data;
1940
+ try {
1941
+ data = JSON.parse(readFileSync3(authFilePath, "utf-8"));
1942
+ } catch {
1943
+ console.error("Not logged in (auth file unreadable). Run: mcp-s-cli login");
1944
+ process.exit(4 /* AUTH_ERROR */);
1945
+ }
1946
+ const tokens = data.tokens ?? {};
1947
+ const serverOrigin = new URL(serverConfig.url).origin;
1948
+ const matchingKey = Object.keys(tokens).find((key) => {
1949
+ try {
1950
+ return new URL(key).origin === serverOrigin;
1951
+ } catch {
1952
+ return key === serverOrigin || key.startsWith(serverOrigin);
1953
+ }
1954
+ });
1955
+ if (!matchingKey || !tokens[matchingKey]?.access_token) {
1956
+ console.error(`Not logged in to ${serverOrigin}. Run: mcp-s-cli login`);
1957
+ process.exit(4 /* AUTH_ERROR */);
1958
+ }
1959
+ const token = tokens[matchingKey];
1960
+ const expiredAt = token.access_token_expired_at;
1961
+ if (expiredAt) {
1962
+ const remaining = expiredAt - Date.now();
1963
+ if (remaining <= EXPIRY_MARGIN_MS) {
1964
+ const reason = remaining <= 0 ? "token expired" : "token expires in < 15 min";
1965
+ console.error(
1966
+ `Not logged in to ${serverOrigin} (${reason}). Run: mcp-s-cli login`
1967
+ );
1968
+ process.exit(4 /* AUTH_ERROR */);
1969
+ }
1970
+ const hours = Math.floor(remaining / (60 * 60 * 1e3));
1971
+ const minutes = Math.floor(remaining % (60 * 60 * 1e3) / 6e4);
1972
+ const display = hours > 0 ? `${hours}h ${minutes}m` : `${minutes}m`;
1973
+ console.log(
1974
+ `Logged in to ${serverOrigin} (token valid, expires in ${display})`
1975
+ );
1976
+ } else {
1977
+ console.log(`Logged in to ${serverOrigin} (token present, no expiry info)`);
1978
+ }
1979
+ process.exit(0);
1980
+ }
1981
+
1982
+ // src/commands/clear.ts
1983
+ import { existsSync as existsSync5, mkdirSync as mkdirSync2, rmSync, writeFileSync as writeFileSync2 } from "fs";
1984
+ import { homedir as homedir3 } from "os";
1985
+ import { dirname as dirname2, join as join4 } from "path";
1986
+ var GLOBAL_CONFIG_PATH = join4(
1987
+ homedir3(),
1915
1988
  ".config",
1916
1989
  "mcp-s-cli",
1917
1990
  "config.json"
1918
1991
  );
1919
- var AUTH_PATH = join3(homedir2(), ".config", "mcp-s-cli", "auth.json");
1992
+ var AUTH_PATH = join4(homedir3(), ".config", "mcp-s-cli", "auth.json");
1920
1993
  async function clearCommand() {
1921
1994
  mkdirSync2(dirname2(GLOBAL_CONFIG_PATH), { recursive: true });
1922
1995
  writeFileSync2(
@@ -1933,9 +2006,9 @@ async function clearAuthCommand() {
1933
2006
  `, "utf-8");
1934
2007
  console.log(`Cleared auth data from ${AUTH_PATH}`);
1935
2008
  }
1936
- var HISTORY_PATH = join3(homedir2(), ".config", "mcp-s-cli", "history.jsonl");
2009
+ var HISTORY_PATH = join4(homedir3(), ".config", "mcp-s-cli", "history.jsonl");
1937
2010
  function clearHistoryCommand() {
1938
- if (!existsSync4(HISTORY_PATH)) {
2011
+ if (!existsSync5(HISTORY_PATH)) {
1939
2012
  console.log("No history file found.");
1940
2013
  return;
1941
2014
  }
@@ -1944,9 +2017,9 @@ function clearHistoryCommand() {
1944
2017
  }
1945
2018
 
1946
2019
  // 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";
2020
+ import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
2021
+ import { homedir as homedir4 } from "os";
2022
+ import { dirname as dirname3, join as join5, resolve as resolve2 } from "path";
1950
2023
  var TOP_LEVEL_KEYS = [
1951
2024
  "enabled",
1952
2025
  "org",
@@ -1969,7 +2042,7 @@ var SETTINGS_KEYS = [
1969
2042
  ];
1970
2043
  var ALL_KNOWN_KEYS = [...TOP_LEVEL_KEYS, ...SETTINGS_KEYS];
1971
2044
  function getDefaultConfigPath() {
1972
- return join4(homedir3(), ".config", "mcp-s-cli", "config.json");
2045
+ return join5(homedir4(), ".config", "mcp-s-cli", "config.json");
1973
2046
  }
1974
2047
  function resolveConfigPath(explicitPath) {
1975
2048
  if (explicitPath) return resolve2(explicitPath);
@@ -1978,8 +2051,8 @@ function resolveConfigPath(explicitPath) {
1978
2051
  return getDefaultConfigPath();
1979
2052
  }
1980
2053
  function readConfigFile(configPath) {
1981
- if (!existsSync5(configPath)) return {};
1982
- const content = readFileSync3(configPath, "utf-8").trim();
2054
+ if (!existsSync6(configPath)) return {};
2055
+ const content = readFileSync4(configPath, "utf-8").trim();
1983
2056
  if (!content) return {};
1984
2057
  try {
1985
2058
  const parsed = JSON.parse(content);
@@ -2060,7 +2133,7 @@ function configGetCommand(opts) {
2060
2133
  const { key, configPath: explicitPath } = opts;
2061
2134
  validateKey(key);
2062
2135
  const configPath = resolveConfigPath(explicitPath);
2063
- if (!existsSync5(configPath)) {
2136
+ if (!existsSync6(configPath)) {
2064
2137
  console.error(
2065
2138
  formatCliError({
2066
2139
  code: 1 /* CLIENT_ERROR */,
@@ -2089,7 +2162,7 @@ function configGetCommand(opts) {
2089
2162
  }
2090
2163
  function configShowCommand(opts) {
2091
2164
  const configPath = resolveConfigPath(opts.configPath);
2092
- if (!existsSync5(configPath)) {
2165
+ if (!existsSync6(configPath)) {
2093
2166
  console.error(
2094
2167
  formatCliError({
2095
2168
  code: 1 /* CLIENT_ERROR */,
@@ -2288,78 +2361,78 @@ async function infoCommand(options) {
2288
2361
  }
2289
2362
 
2290
2363
  // 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";
2364
+ import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
2365
+ import { homedir as homedir6 } from "os";
2366
+ import { dirname as dirname5, join as join7 } from "path";
2294
2367
  import * as readline from "readline";
2295
2368
 
2296
2369
  // src/commands/install-skill.ts
2297
2370
  import {
2298
- existsSync as existsSync6,
2371
+ existsSync as existsSync7,
2299
2372
  lstatSync,
2300
2373
  mkdirSync as mkdirSync4,
2301
- readFileSync as readFileSync4,
2374
+ readFileSync as readFileSync5,
2302
2375
  readlinkSync,
2303
2376
  rmSync as rmSync2,
2304
2377
  symlinkSync,
2305
2378
  writeFileSync as writeFileSync4
2306
2379
  } 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";
2380
+ import { homedir as homedir5, platform } from "os";
2381
+ import { dirname as dirname4, join as join6, normalize, relative, resolve as resolve3, sep } from "path";
2309
2382
  import { fileURLToPath as fileURLToPath2 } from "url";
2310
- var home = homedir4();
2383
+ var home = homedir5();
2311
2384
  var AGENTS = {
2312
2385
  cursor: {
2313
2386
  displayName: "Cursor",
2314
- globalSkillsDir: join5(home, ".cursor", "skills"),
2315
- detect: () => existsSync6(join5(home, ".cursor"))
2387
+ globalSkillsDir: join6(home, ".cursor", "skills"),
2388
+ detect: () => existsSync7(join6(home, ".cursor"))
2316
2389
  },
2317
2390
  codex: {
2318
2391
  displayName: "Codex",
2319
- globalSkillsDir: join5(
2320
- process.env.CODEX_HOME?.trim() || join5(home, ".codex"),
2392
+ globalSkillsDir: join6(
2393
+ process.env.CODEX_HOME?.trim() || join6(home, ".codex"),
2321
2394
  "skills"
2322
2395
  ),
2323
- detect: () => existsSync6(process.env.CODEX_HOME?.trim() || join5(home, ".codex")) || existsSync6("/etc/codex")
2396
+ detect: () => existsSync7(process.env.CODEX_HOME?.trim() || join6(home, ".codex")) || existsSync7("/etc/codex")
2324
2397
  },
2325
2398
  "claude-code": {
2326
2399
  displayName: "Claude Code",
2327
- globalSkillsDir: join5(
2328
- process.env.CLAUDE_CONFIG_DIR?.trim() || join5(home, ".claude"),
2400
+ globalSkillsDir: join6(
2401
+ process.env.CLAUDE_CONFIG_DIR?.trim() || join6(home, ".claude"),
2329
2402
  "skills"
2330
2403
  ),
2331
- detect: () => existsSync6(
2332
- process.env.CLAUDE_CONFIG_DIR?.trim() || join5(home, ".claude")
2404
+ detect: () => existsSync7(
2405
+ process.env.CLAUDE_CONFIG_DIR?.trim() || join6(home, ".claude")
2333
2406
  )
2334
2407
  },
2335
2408
  windsurf: {
2336
2409
  displayName: "Windsurf",
2337
- globalSkillsDir: join5(home, ".codeium", "windsurf", "skills"),
2338
- detect: () => existsSync6(join5(home, ".codeium", "windsurf"))
2410
+ globalSkillsDir: join6(home, ".codeium", "windsurf", "skills"),
2411
+ detect: () => existsSync7(join6(home, ".codeium", "windsurf"))
2339
2412
  },
2340
2413
  "github-copilot": {
2341
2414
  displayName: "GitHub Copilot",
2342
- globalSkillsDir: join5(home, ".copilot", "skills"),
2343
- detect: () => existsSync6(join5(home, ".copilot"))
2415
+ globalSkillsDir: join6(home, ".copilot", "skills"),
2416
+ detect: () => existsSync7(join6(home, ".copilot"))
2344
2417
  },
2345
2418
  cline: {
2346
2419
  displayName: "Cline",
2347
- globalSkillsDir: join5(home, ".cline", "skills"),
2348
- detect: () => existsSync6(join5(home, ".cline"))
2420
+ globalSkillsDir: join6(home, ".cline", "skills"),
2421
+ detect: () => existsSync7(join6(home, ".cline"))
2349
2422
  },
2350
2423
  roo: {
2351
2424
  displayName: "Roo Code",
2352
- globalSkillsDir: join5(home, ".roo", "skills"),
2353
- detect: () => existsSync6(join5(home, ".roo"))
2425
+ globalSkillsDir: join6(home, ".roo", "skills"),
2426
+ detect: () => existsSync7(join6(home, ".roo"))
2354
2427
  },
2355
2428
  continue: {
2356
2429
  displayName: "Continue",
2357
- globalSkillsDir: join5(home, ".continue", "skills"),
2358
- detect: () => existsSync6(join5(home, ".continue")) || existsSync6(join5(process.cwd(), ".continue"))
2430
+ globalSkillsDir: join6(home, ".continue", "skills"),
2431
+ detect: () => existsSync7(join6(home, ".continue")) || existsSync7(join6(process.cwd(), ".continue"))
2359
2432
  }
2360
2433
  };
2361
2434
  function getCanonicalSkillDir(skillName) {
2362
- return join5(home, ".agents", "skills", skillName);
2435
+ return join6(home, ".agents", "skills", skillName);
2363
2436
  }
2364
2437
  function getCanonicalSkillPath() {
2365
2438
  return getCanonicalSkillDir(SKILL_NAME);
@@ -2410,13 +2483,13 @@ function getSkillMdContent() {
2410
2483
  const __filename = fileURLToPath2(import.meta.url);
2411
2484
  const __dirname = dirname4(__filename);
2412
2485
  const candidates = [
2413
- join5(__dirname, "SKILL.md"),
2414
- join5(__dirname, "..", "SKILL.md"),
2415
- join5(__dirname, "..", "..", "SKILL.md")
2486
+ join6(__dirname, "SKILL.md"),
2487
+ join6(__dirname, "..", "SKILL.md"),
2488
+ join6(__dirname, "..", "..", "SKILL.md")
2416
2489
  ];
2417
2490
  for (const candidate of candidates) {
2418
- if (existsSync6(candidate)) {
2419
- return readFileSync4(candidate, "utf-8");
2491
+ if (existsSync7(candidate)) {
2492
+ return readFileSync5(candidate, "utf-8");
2420
2493
  }
2421
2494
  }
2422
2495
  throw new Error(
@@ -2426,12 +2499,12 @@ function getSkillMdContent() {
2426
2499
  function installSkill() {
2427
2500
  const content = getSkillMdContent();
2428
2501
  const canonicalDir = getCanonicalSkillDir(SKILL_NAME);
2429
- if (!isPathSafe(join5(home, ".agents", "skills"), canonicalDir)) {
2502
+ if (!isPathSafe(join6(home, ".agents", "skills"), canonicalDir)) {
2430
2503
  throw new Error("Invalid skill name: potential path traversal detected");
2431
2504
  }
2432
2505
  rmSync2(canonicalDir, { recursive: true, force: true });
2433
2506
  mkdirSync4(canonicalDir, { recursive: true });
2434
- writeFileSync4(join5(canonicalDir, "SKILL.md"), content, "utf-8");
2507
+ writeFileSync4(join6(canonicalDir, "SKILL.md"), content, "utf-8");
2435
2508
  const result = {
2436
2509
  installed: [],
2437
2510
  skipped: [],
@@ -2443,7 +2516,7 @@ function installSkill() {
2443
2516
  result.skipped.push(agentName);
2444
2517
  continue;
2445
2518
  }
2446
- const agentSkillDir = join5(agent.globalSkillsDir, SKILL_NAME);
2519
+ const agentSkillDir = join6(agent.globalSkillsDir, SKILL_NAME);
2447
2520
  if (!isPathSafe(agent.globalSkillsDir, agentSkillDir)) {
2448
2521
  result.failed.push({
2449
2522
  agent: agentName,
@@ -2456,7 +2529,7 @@ function installSkill() {
2456
2529
  if (!symlinkOk) {
2457
2530
  rmSync2(agentSkillDir, { recursive: true, force: true });
2458
2531
  mkdirSync4(agentSkillDir, { recursive: true });
2459
- writeFileSync4(join5(agentSkillDir, "SKILL.md"), content, "utf-8");
2532
+ writeFileSync4(join6(agentSkillDir, "SKILL.md"), content, "utf-8");
2460
2533
  }
2461
2534
  result.installed.push(agentName);
2462
2535
  } catch (err) {
@@ -2476,8 +2549,8 @@ function clearSkill() {
2476
2549
  canonicalRemoved: false
2477
2550
  };
2478
2551
  for (const [agentName, agent] of Object.entries(AGENTS)) {
2479
- const agentSkillDir = join5(agent.globalSkillsDir, SKILL_NAME);
2480
- if (!existsSync6(agentSkillDir)) {
2552
+ const agentSkillDir = join6(agent.globalSkillsDir, SKILL_NAME);
2553
+ if (!existsSync7(agentSkillDir)) {
2481
2554
  result.skipped.push(agentName);
2482
2555
  continue;
2483
2556
  }
@@ -2492,7 +2565,7 @@ function clearSkill() {
2492
2565
  }
2493
2566
  }
2494
2567
  const canonicalDir = getCanonicalSkillDir(SKILL_NAME);
2495
- if (existsSync6(canonicalDir)) {
2568
+ if (existsSync7(canonicalDir)) {
2496
2569
  try {
2497
2570
  rmSync2(canonicalDir, { recursive: true, force: true });
2498
2571
  result.canonicalRemoved = true;
@@ -2503,12 +2576,12 @@ function clearSkill() {
2503
2576
  }
2504
2577
  function getSkillInstallInfo() {
2505
2578
  return Object.entries(AGENTS).filter(([, agent]) => agent.detect()).map(([agentName, agent]) => {
2506
- const agentSkillDir = join5(agent.globalSkillsDir, SKILL_NAME);
2579
+ const agentSkillDir = join6(agent.globalSkillsDir, SKILL_NAME);
2507
2580
  return {
2508
2581
  agent: agentName,
2509
2582
  displayName: agent.displayName,
2510
2583
  path: agentSkillDir,
2511
- installed: existsSync6(join5(agentSkillDir, "SKILL.md"))
2584
+ installed: existsSync7(join6(agentSkillDir, "SKILL.md"))
2512
2585
  };
2513
2586
  });
2514
2587
  }
@@ -2708,16 +2781,16 @@ ${BAR} ${c(A.cyan, "\u25C6")} `
2708
2781
  }
2709
2782
  }
2710
2783
  var p = { intro, outro, cancel, isCancel, select, text };
2711
- var GLOBAL_CONFIG_PATH2 = join6(
2712
- homedir5(),
2784
+ var GLOBAL_CONFIG_PATH2 = join7(
2785
+ homedir6(),
2713
2786
  ".config",
2714
2787
  "mcp-s-cli",
2715
2788
  "config.json"
2716
2789
  );
2717
2790
  function readExistingConfig() {
2718
2791
  try {
2719
- if (existsSync7(GLOBAL_CONFIG_PATH2)) {
2720
- return JSON.parse(readFileSync5(GLOBAL_CONFIG_PATH2, "utf-8"));
2792
+ if (existsSync8(GLOBAL_CONFIG_PATH2)) {
2793
+ return JSON.parse(readFileSync6(GLOBAL_CONFIG_PATH2, "utf-8"));
2721
2794
  }
2722
2795
  } catch {
2723
2796
  }
@@ -2846,10 +2919,10 @@ async function initInteractive() {
2846
2919
  }
2847
2920
 
2848
2921
  // src/commands/kill-daemon.ts
2849
- import { existsSync as existsSync8, readdirSync as readdirSync2 } from "fs";
2922
+ import { existsSync as existsSync9, readdirSync as readdirSync2 } from "fs";
2850
2923
  function killDaemonCommand() {
2851
2924
  const socketDir = getSocketDir();
2852
- if (!existsSync8(socketDir)) {
2925
+ if (!existsSync9(socketDir)) {
2853
2926
  console.log("No daemons running.");
2854
2927
  return;
2855
2928
  }
@@ -2948,9 +3021,9 @@ async function logoutCommand(options) {
2948
3021
  }
2949
3022
 
2950
3023
  // 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";
3024
+ import { existsSync as existsSync10, readFileSync as readFileSync7, statSync } from "fs";
3025
+ import { homedir as homedir7 } from "os";
3026
+ import { join as join8, resolve as resolve4 } from "path";
2954
3027
  function getResolvedConfigPath(explicitPath) {
2955
3028
  if (explicitPath) {
2956
3029
  return resolve4(explicitPath);
@@ -2958,8 +3031,8 @@ function getResolvedConfigPath(explicitPath) {
2958
3031
  if (process.env.MCP_S_CLI_CONFIG_PATH) {
2959
3032
  return resolve4(process.env.MCP_S_CLI_CONFIG_PATH);
2960
3033
  }
2961
- const defaultPath = join7(homedir6(), ".config", "mcp-s-cli", "config.json");
2962
- if (existsSync9(defaultPath)) {
3034
+ const defaultPath = join8(homedir7(), ".config", "mcp-s-cli", "config.json");
3035
+ if (existsSync10(defaultPath)) {
2963
3036
  return defaultPath;
2964
3037
  }
2965
3038
  return null;
@@ -2987,15 +3060,15 @@ async function whoamiCommand(options) {
2987
3060
  console.log(` Error loading config: ${err.message}`);
2988
3061
  return;
2989
3062
  }
2990
- const authFilePath = join7(homedir6(), ".config", "mcp-s-cli", "auth.json");
3063
+ const authFilePath = join8(homedir7(), ".config", "mcp-s-cli", "auth.json");
2991
3064
  console.log("");
2992
3065
  console.log("OAuth Tokens");
2993
3066
  console.log(` Path: ${authFilePath}`);
2994
- if (!existsSync9(authFilePath)) {
3067
+ if (!existsSync10(authFilePath)) {
2995
3068
  console.log(" (no tokens stored)");
2996
3069
  } else {
2997
3070
  try {
2998
- const raw2 = readFileSync6(authFilePath, "utf-8");
3071
+ const raw2 = readFileSync7(authFilePath, "utf-8");
2999
3072
  const data = JSON.parse(raw2);
3000
3073
  const tokenEntries = Object.entries(data.tokens ?? {});
3001
3074
  if (tokenEntries.length === 0) {
@@ -3005,9 +3078,9 @@ async function whoamiCommand(options) {
3005
3078
  console.log(" (could not read auth file)");
3006
3079
  }
3007
3080
  }
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);
3081
+ const historyPath = join8(homedir7(), ".config", "mcp-s-cli", "history.jsonl");
3082
+ if (existsSync10(historyPath)) {
3083
+ const lines = readFileSync7(historyPath, "utf-8").split("\n").filter(Boolean);
3011
3084
  const size = statSync(historyPath).size;
3012
3085
  const sizeStr = size >= 1024 * 1024 ? `${(size / (1024 * 1024)).toFixed(1)} MB` : size >= 1024 ? `${(size / 1024).toFixed(1)} KB` : `${size} B`;
3013
3086
  console.log("");
@@ -3216,6 +3289,10 @@ function parseArgs2(args) {
3216
3289
  result.command = "logout";
3217
3290
  return result;
3218
3291
  }
3292
+ if (firstArg === "check-auth") {
3293
+ result.command = "check-auth";
3294
+ return result;
3295
+ }
3219
3296
  if (firstArg === "clear") {
3220
3297
  result.command = "clear";
3221
3298
  return result;
@@ -3307,6 +3384,7 @@ Usage:
3307
3384
  mcp-s-cli whoami Show config location and auth state
3308
3385
  mcp-s-cli login Log in to the configured server via OAuth
3309
3386
  mcp-s-cli logout Log out (remove stored OAuth tokens)
3387
+ mcp-s-cli check-auth Check auth state offline (no network); exits 0=ok, 4=needs login
3310
3388
  mcp-s-cli config set <key> <value> Set a value in config.json
3311
3389
  mcp-s-cli config get <key> Get a value from config.json
3312
3390
  mcp-s-cli config show Print full config.json
@@ -3348,6 +3426,7 @@ Examples:
3348
3426
  mcp-s-cli whoami # Show config and auth state
3349
3427
  mcp-s-cli login # Authenticate via OAuth
3350
3428
  mcp-s-cli logout # Remove stored OAuth tokens
3429
+ mcp-s-cli check-auth # Check auth state offline (fast preflight)
3351
3430
  mcp-s-cli config set settings.timeout 30 # Set request timeout to 30s
3352
3431
  mcp-s-cli clear # Reset server config
3353
3432
  mcp-s-cli clear-auth # Clear stored auth data
@@ -3371,10 +3450,10 @@ Config File:
3371
3450
  function appendHistory(argv, history) {
3372
3451
  if (!history) return;
3373
3452
  try {
3374
- const dir = join8(homedir7(), ".config", "mcp-s-cli");
3453
+ const dir = join9(homedir8(), ".config", "mcp-s-cli");
3375
3454
  mkdirSync6(dir, { recursive: true });
3376
3455
  const entry = JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), args: argv });
3377
- appendFileSync(join8(dir, "history.jsonl"), `${entry}
3456
+ appendFileSync(join9(dir, "history.jsonl"), `${entry}
3378
3457
  `, "utf8");
3379
3458
  } catch (err) {
3380
3459
  const msg = err instanceof Error ? err.message : String(err);
@@ -3473,6 +3552,9 @@ async function main() {
3473
3552
  case "logout":
3474
3553
  await logoutCommand({ configPath: args.configPath });
3475
3554
  break;
3555
+ case "check-auth":
3556
+ await checkAuthCommand({ configPath: args.configPath });
3557
+ break;
3476
3558
  case "clear":
3477
3559
  await clearCommand();
3478
3560
  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.16",
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",