@mcp-s/cli 0.0.14 → 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.14";
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";
@@ -1153,6 +1153,7 @@ function isDaemonValid(serverName, config) {
1153
1153
  debug(`[daemon-client] Process ${pidInfo.pid} not running, cleaning up`);
1154
1154
  removePidFile(serverName);
1155
1155
  removeSocketFile(serverName);
1156
+ removeReadyFile(serverName);
1156
1157
  return false;
1157
1158
  }
1158
1159
  const currentHash = getConfigHash(config);
@@ -1163,6 +1164,7 @@ function isDaemonValid(serverName, config) {
1163
1164
  killProcess(pidInfo.pid);
1164
1165
  removePidFile(serverName);
1165
1166
  removeSocketFile(serverName);
1167
+ removeReadyFile(serverName);
1166
1168
  return false;
1167
1169
  }
1168
1170
  if (!existsSync3(socketPath)) {
@@ -1298,7 +1300,7 @@ async function cleanupOrphanedDaemons() {
1298
1300
  }
1299
1301
 
1300
1302
  // src/version.ts
1301
- var VERSION = "0.0.14";
1303
+ var VERSION = "0.0.16";
1302
1304
 
1303
1305
  // src/client.ts
1304
1306
  function getRetryConfig(settings) {
@@ -1904,17 +1906,90 @@ async function callCommand(options) {
1904
1906
  }
1905
1907
  }
1906
1908
 
1907
- // src/commands/clear.ts
1908
- 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";
1909
1911
  import { homedir as homedir2 } from "os";
1910
- import { dirname as dirname2, join as join3 } from "path";
1911
- var GLOBAL_CONFIG_PATH = join3(
1912
- 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(),
1913
1988
  ".config",
1914
1989
  "mcp-s-cli",
1915
1990
  "config.json"
1916
1991
  );
1917
- var AUTH_PATH = join3(homedir2(), ".config", "mcp-s-cli", "auth.json");
1992
+ var AUTH_PATH = join4(homedir3(), ".config", "mcp-s-cli", "auth.json");
1918
1993
  async function clearCommand() {
1919
1994
  mkdirSync2(dirname2(GLOBAL_CONFIG_PATH), { recursive: true });
1920
1995
  writeFileSync2(
@@ -1931,9 +2006,9 @@ async function clearAuthCommand() {
1931
2006
  `, "utf-8");
1932
2007
  console.log(`Cleared auth data from ${AUTH_PATH}`);
1933
2008
  }
1934
- var HISTORY_PATH = join3(homedir2(), ".config", "mcp-s-cli", "history.jsonl");
2009
+ var HISTORY_PATH = join4(homedir3(), ".config", "mcp-s-cli", "history.jsonl");
1935
2010
  function clearHistoryCommand() {
1936
- if (!existsSync4(HISTORY_PATH)) {
2011
+ if (!existsSync5(HISTORY_PATH)) {
1937
2012
  console.log("No history file found.");
1938
2013
  return;
1939
2014
  }
@@ -1942,9 +2017,9 @@ function clearHistoryCommand() {
1942
2017
  }
1943
2018
 
1944
2019
  // src/commands/config.ts
1945
- import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
1946
- import { homedir as homedir3 } from "os";
1947
- 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";
1948
2023
  var TOP_LEVEL_KEYS = [
1949
2024
  "enabled",
1950
2025
  "org",
@@ -1967,7 +2042,7 @@ var SETTINGS_KEYS = [
1967
2042
  ];
1968
2043
  var ALL_KNOWN_KEYS = [...TOP_LEVEL_KEYS, ...SETTINGS_KEYS];
1969
2044
  function getDefaultConfigPath() {
1970
- return join4(homedir3(), ".config", "mcp-s-cli", "config.json");
2045
+ return join5(homedir4(), ".config", "mcp-s-cli", "config.json");
1971
2046
  }
1972
2047
  function resolveConfigPath(explicitPath) {
1973
2048
  if (explicitPath) return resolve2(explicitPath);
@@ -1976,8 +2051,8 @@ function resolveConfigPath(explicitPath) {
1976
2051
  return getDefaultConfigPath();
1977
2052
  }
1978
2053
  function readConfigFile(configPath) {
1979
- if (!existsSync5(configPath)) return {};
1980
- const content = readFileSync3(configPath, "utf-8").trim();
2054
+ if (!existsSync6(configPath)) return {};
2055
+ const content = readFileSync4(configPath, "utf-8").trim();
1981
2056
  if (!content) return {};
1982
2057
  try {
1983
2058
  const parsed = JSON.parse(content);
@@ -2058,7 +2133,7 @@ function configGetCommand(opts) {
2058
2133
  const { key, configPath: explicitPath } = opts;
2059
2134
  validateKey(key);
2060
2135
  const configPath = resolveConfigPath(explicitPath);
2061
- if (!existsSync5(configPath)) {
2136
+ if (!existsSync6(configPath)) {
2062
2137
  console.error(
2063
2138
  formatCliError({
2064
2139
  code: 1 /* CLIENT_ERROR */,
@@ -2087,7 +2162,7 @@ function configGetCommand(opts) {
2087
2162
  }
2088
2163
  function configShowCommand(opts) {
2089
2164
  const configPath = resolveConfigPath(opts.configPath);
2090
- if (!existsSync5(configPath)) {
2165
+ if (!existsSync6(configPath)) {
2091
2166
  console.error(
2092
2167
  formatCliError({
2093
2168
  code: 1 /* CLIENT_ERROR */,
@@ -2286,78 +2361,78 @@ async function infoCommand(options) {
2286
2361
  }
2287
2362
 
2288
2363
  // src/commands/init.ts
2289
- import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "fs";
2290
- import { homedir as homedir5 } from "os";
2291
- 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";
2292
2367
  import * as readline from "readline";
2293
2368
 
2294
2369
  // src/commands/install-skill.ts
2295
2370
  import {
2296
- existsSync as existsSync6,
2371
+ existsSync as existsSync7,
2297
2372
  lstatSync,
2298
2373
  mkdirSync as mkdirSync4,
2299
- readFileSync as readFileSync4,
2374
+ readFileSync as readFileSync5,
2300
2375
  readlinkSync,
2301
2376
  rmSync as rmSync2,
2302
2377
  symlinkSync,
2303
2378
  writeFileSync as writeFileSync4
2304
2379
  } from "fs";
2305
- import { homedir as homedir4, platform } from "os";
2306
- 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";
2307
2382
  import { fileURLToPath as fileURLToPath2 } from "url";
2308
- var home = homedir4();
2383
+ var home = homedir5();
2309
2384
  var AGENTS = {
2310
2385
  cursor: {
2311
2386
  displayName: "Cursor",
2312
- globalSkillsDir: join5(home, ".cursor", "skills"),
2313
- detect: () => existsSync6(join5(home, ".cursor"))
2387
+ globalSkillsDir: join6(home, ".cursor", "skills"),
2388
+ detect: () => existsSync7(join6(home, ".cursor"))
2314
2389
  },
2315
2390
  codex: {
2316
2391
  displayName: "Codex",
2317
- globalSkillsDir: join5(
2318
- process.env.CODEX_HOME?.trim() || join5(home, ".codex"),
2392
+ globalSkillsDir: join6(
2393
+ process.env.CODEX_HOME?.trim() || join6(home, ".codex"),
2319
2394
  "skills"
2320
2395
  ),
2321
- 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")
2322
2397
  },
2323
2398
  "claude-code": {
2324
2399
  displayName: "Claude Code",
2325
- globalSkillsDir: join5(
2326
- process.env.CLAUDE_CONFIG_DIR?.trim() || join5(home, ".claude"),
2400
+ globalSkillsDir: join6(
2401
+ process.env.CLAUDE_CONFIG_DIR?.trim() || join6(home, ".claude"),
2327
2402
  "skills"
2328
2403
  ),
2329
- detect: () => existsSync6(
2330
- process.env.CLAUDE_CONFIG_DIR?.trim() || join5(home, ".claude")
2404
+ detect: () => existsSync7(
2405
+ process.env.CLAUDE_CONFIG_DIR?.trim() || join6(home, ".claude")
2331
2406
  )
2332
2407
  },
2333
2408
  windsurf: {
2334
2409
  displayName: "Windsurf",
2335
- globalSkillsDir: join5(home, ".codeium", "windsurf", "skills"),
2336
- detect: () => existsSync6(join5(home, ".codeium", "windsurf"))
2410
+ globalSkillsDir: join6(home, ".codeium", "windsurf", "skills"),
2411
+ detect: () => existsSync7(join6(home, ".codeium", "windsurf"))
2337
2412
  },
2338
2413
  "github-copilot": {
2339
2414
  displayName: "GitHub Copilot",
2340
- globalSkillsDir: join5(home, ".copilot", "skills"),
2341
- detect: () => existsSync6(join5(home, ".copilot"))
2415
+ globalSkillsDir: join6(home, ".copilot", "skills"),
2416
+ detect: () => existsSync7(join6(home, ".copilot"))
2342
2417
  },
2343
2418
  cline: {
2344
2419
  displayName: "Cline",
2345
- globalSkillsDir: join5(home, ".cline", "skills"),
2346
- detect: () => existsSync6(join5(home, ".cline"))
2420
+ globalSkillsDir: join6(home, ".cline", "skills"),
2421
+ detect: () => existsSync7(join6(home, ".cline"))
2347
2422
  },
2348
2423
  roo: {
2349
2424
  displayName: "Roo Code",
2350
- globalSkillsDir: join5(home, ".roo", "skills"),
2351
- detect: () => existsSync6(join5(home, ".roo"))
2425
+ globalSkillsDir: join6(home, ".roo", "skills"),
2426
+ detect: () => existsSync7(join6(home, ".roo"))
2352
2427
  },
2353
2428
  continue: {
2354
2429
  displayName: "Continue",
2355
- globalSkillsDir: join5(home, ".continue", "skills"),
2356
- 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"))
2357
2432
  }
2358
2433
  };
2359
2434
  function getCanonicalSkillDir(skillName) {
2360
- return join5(home, ".agents", "skills", skillName);
2435
+ return join6(home, ".agents", "skills", skillName);
2361
2436
  }
2362
2437
  function getCanonicalSkillPath() {
2363
2438
  return getCanonicalSkillDir(SKILL_NAME);
@@ -2408,13 +2483,13 @@ function getSkillMdContent() {
2408
2483
  const __filename = fileURLToPath2(import.meta.url);
2409
2484
  const __dirname = dirname4(__filename);
2410
2485
  const candidates = [
2411
- join5(__dirname, "SKILL.md"),
2412
- join5(__dirname, "..", "SKILL.md"),
2413
- join5(__dirname, "..", "..", "SKILL.md")
2486
+ join6(__dirname, "SKILL.md"),
2487
+ join6(__dirname, "..", "SKILL.md"),
2488
+ join6(__dirname, "..", "..", "SKILL.md")
2414
2489
  ];
2415
2490
  for (const candidate of candidates) {
2416
- if (existsSync6(candidate)) {
2417
- return readFileSync4(candidate, "utf-8");
2491
+ if (existsSync7(candidate)) {
2492
+ return readFileSync5(candidate, "utf-8");
2418
2493
  }
2419
2494
  }
2420
2495
  throw new Error(
@@ -2424,12 +2499,12 @@ function getSkillMdContent() {
2424
2499
  function installSkill() {
2425
2500
  const content = getSkillMdContent();
2426
2501
  const canonicalDir = getCanonicalSkillDir(SKILL_NAME);
2427
- if (!isPathSafe(join5(home, ".agents", "skills"), canonicalDir)) {
2502
+ if (!isPathSafe(join6(home, ".agents", "skills"), canonicalDir)) {
2428
2503
  throw new Error("Invalid skill name: potential path traversal detected");
2429
2504
  }
2430
2505
  rmSync2(canonicalDir, { recursive: true, force: true });
2431
2506
  mkdirSync4(canonicalDir, { recursive: true });
2432
- writeFileSync4(join5(canonicalDir, "SKILL.md"), content, "utf-8");
2507
+ writeFileSync4(join6(canonicalDir, "SKILL.md"), content, "utf-8");
2433
2508
  const result = {
2434
2509
  installed: [],
2435
2510
  skipped: [],
@@ -2441,7 +2516,7 @@ function installSkill() {
2441
2516
  result.skipped.push(agentName);
2442
2517
  continue;
2443
2518
  }
2444
- const agentSkillDir = join5(agent.globalSkillsDir, SKILL_NAME);
2519
+ const agentSkillDir = join6(agent.globalSkillsDir, SKILL_NAME);
2445
2520
  if (!isPathSafe(agent.globalSkillsDir, agentSkillDir)) {
2446
2521
  result.failed.push({
2447
2522
  agent: agentName,
@@ -2454,7 +2529,7 @@ function installSkill() {
2454
2529
  if (!symlinkOk) {
2455
2530
  rmSync2(agentSkillDir, { recursive: true, force: true });
2456
2531
  mkdirSync4(agentSkillDir, { recursive: true });
2457
- writeFileSync4(join5(agentSkillDir, "SKILL.md"), content, "utf-8");
2532
+ writeFileSync4(join6(agentSkillDir, "SKILL.md"), content, "utf-8");
2458
2533
  }
2459
2534
  result.installed.push(agentName);
2460
2535
  } catch (err) {
@@ -2474,8 +2549,8 @@ function clearSkill() {
2474
2549
  canonicalRemoved: false
2475
2550
  };
2476
2551
  for (const [agentName, agent] of Object.entries(AGENTS)) {
2477
- const agentSkillDir = join5(agent.globalSkillsDir, SKILL_NAME);
2478
- if (!existsSync6(agentSkillDir)) {
2552
+ const agentSkillDir = join6(agent.globalSkillsDir, SKILL_NAME);
2553
+ if (!existsSync7(agentSkillDir)) {
2479
2554
  result.skipped.push(agentName);
2480
2555
  continue;
2481
2556
  }
@@ -2490,7 +2565,7 @@ function clearSkill() {
2490
2565
  }
2491
2566
  }
2492
2567
  const canonicalDir = getCanonicalSkillDir(SKILL_NAME);
2493
- if (existsSync6(canonicalDir)) {
2568
+ if (existsSync7(canonicalDir)) {
2494
2569
  try {
2495
2570
  rmSync2(canonicalDir, { recursive: true, force: true });
2496
2571
  result.canonicalRemoved = true;
@@ -2501,12 +2576,12 @@ function clearSkill() {
2501
2576
  }
2502
2577
  function getSkillInstallInfo() {
2503
2578
  return Object.entries(AGENTS).filter(([, agent]) => agent.detect()).map(([agentName, agent]) => {
2504
- const agentSkillDir = join5(agent.globalSkillsDir, SKILL_NAME);
2579
+ const agentSkillDir = join6(agent.globalSkillsDir, SKILL_NAME);
2505
2580
  return {
2506
2581
  agent: agentName,
2507
2582
  displayName: agent.displayName,
2508
2583
  path: agentSkillDir,
2509
- installed: existsSync6(join5(agentSkillDir, "SKILL.md"))
2584
+ installed: existsSync7(join6(agentSkillDir, "SKILL.md"))
2510
2585
  };
2511
2586
  });
2512
2587
  }
@@ -2706,16 +2781,16 @@ ${BAR} ${c(A.cyan, "\u25C6")} `
2706
2781
  }
2707
2782
  }
2708
2783
  var p = { intro, outro, cancel, isCancel, select, text };
2709
- var GLOBAL_CONFIG_PATH2 = join6(
2710
- homedir5(),
2784
+ var GLOBAL_CONFIG_PATH2 = join7(
2785
+ homedir6(),
2711
2786
  ".config",
2712
2787
  "mcp-s-cli",
2713
2788
  "config.json"
2714
2789
  );
2715
2790
  function readExistingConfig() {
2716
2791
  try {
2717
- if (existsSync7(GLOBAL_CONFIG_PATH2)) {
2718
- 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"));
2719
2794
  }
2720
2795
  } catch {
2721
2796
  }
@@ -2844,10 +2919,10 @@ async function initInteractive() {
2844
2919
  }
2845
2920
 
2846
2921
  // src/commands/kill-daemon.ts
2847
- import { existsSync as existsSync8, readdirSync as readdirSync2 } from "fs";
2922
+ import { existsSync as existsSync9, readdirSync as readdirSync2 } from "fs";
2848
2923
  function killDaemonCommand() {
2849
2924
  const socketDir = getSocketDir();
2850
- if (!existsSync8(socketDir)) {
2925
+ if (!existsSync9(socketDir)) {
2851
2926
  console.log("No daemons running.");
2852
2927
  return;
2853
2928
  }
@@ -2946,9 +3021,9 @@ async function logoutCommand(options) {
2946
3021
  }
2947
3022
 
2948
3023
  // src/commands/whoami.ts
2949
- import { existsSync as existsSync9, readFileSync as readFileSync6, statSync } from "fs";
2950
- import { homedir as homedir6 } from "os";
2951
- 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";
2952
3027
  function getResolvedConfigPath(explicitPath) {
2953
3028
  if (explicitPath) {
2954
3029
  return resolve4(explicitPath);
@@ -2956,8 +3031,8 @@ function getResolvedConfigPath(explicitPath) {
2956
3031
  if (process.env.MCP_S_CLI_CONFIG_PATH) {
2957
3032
  return resolve4(process.env.MCP_S_CLI_CONFIG_PATH);
2958
3033
  }
2959
- const defaultPath = join7(homedir6(), ".config", "mcp-s-cli", "config.json");
2960
- if (existsSync9(defaultPath)) {
3034
+ const defaultPath = join8(homedir7(), ".config", "mcp-s-cli", "config.json");
3035
+ if (existsSync10(defaultPath)) {
2961
3036
  return defaultPath;
2962
3037
  }
2963
3038
  return null;
@@ -2985,15 +3060,15 @@ async function whoamiCommand(options) {
2985
3060
  console.log(` Error loading config: ${err.message}`);
2986
3061
  return;
2987
3062
  }
2988
- const authFilePath = join7(homedir6(), ".config", "mcp-s-cli", "auth.json");
3063
+ const authFilePath = join8(homedir7(), ".config", "mcp-s-cli", "auth.json");
2989
3064
  console.log("");
2990
3065
  console.log("OAuth Tokens");
2991
3066
  console.log(` Path: ${authFilePath}`);
2992
- if (!existsSync9(authFilePath)) {
3067
+ if (!existsSync10(authFilePath)) {
2993
3068
  console.log(" (no tokens stored)");
2994
3069
  } else {
2995
3070
  try {
2996
- const raw2 = readFileSync6(authFilePath, "utf-8");
3071
+ const raw2 = readFileSync7(authFilePath, "utf-8");
2997
3072
  const data = JSON.parse(raw2);
2998
3073
  const tokenEntries = Object.entries(data.tokens ?? {});
2999
3074
  if (tokenEntries.length === 0) {
@@ -3003,9 +3078,9 @@ async function whoamiCommand(options) {
3003
3078
  console.log(" (could not read auth file)");
3004
3079
  }
3005
3080
  }
3006
- const historyPath = join7(homedir6(), ".config", "mcp-s-cli", "history.jsonl");
3007
- if (existsSync9(historyPath)) {
3008
- 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);
3009
3084
  const size = statSync(historyPath).size;
3010
3085
  const sizeStr = size >= 1024 * 1024 ? `${(size / (1024 * 1024)).toFixed(1)} MB` : size >= 1024 ? `${(size / 1024).toFixed(1)} KB` : `${size} B`;
3011
3086
  console.log("");
@@ -3214,6 +3289,10 @@ function parseArgs2(args) {
3214
3289
  result.command = "logout";
3215
3290
  return result;
3216
3291
  }
3292
+ if (firstArg === "check-auth") {
3293
+ result.command = "check-auth";
3294
+ return result;
3295
+ }
3217
3296
  if (firstArg === "clear") {
3218
3297
  result.command = "clear";
3219
3298
  return result;
@@ -3305,6 +3384,7 @@ Usage:
3305
3384
  mcp-s-cli whoami Show config location and auth state
3306
3385
  mcp-s-cli login Log in to the configured server via OAuth
3307
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
3308
3388
  mcp-s-cli config set <key> <value> Set a value in config.json
3309
3389
  mcp-s-cli config get <key> Get a value from config.json
3310
3390
  mcp-s-cli config show Print full config.json
@@ -3346,6 +3426,7 @@ Examples:
3346
3426
  mcp-s-cli whoami # Show config and auth state
3347
3427
  mcp-s-cli login # Authenticate via OAuth
3348
3428
  mcp-s-cli logout # Remove stored OAuth tokens
3429
+ mcp-s-cli check-auth # Check auth state offline (fast preflight)
3349
3430
  mcp-s-cli config set settings.timeout 30 # Set request timeout to 30s
3350
3431
  mcp-s-cli clear # Reset server config
3351
3432
  mcp-s-cli clear-auth # Clear stored auth data
@@ -3369,10 +3450,10 @@ Config File:
3369
3450
  function appendHistory(argv, history) {
3370
3451
  if (!history) return;
3371
3452
  try {
3372
- const dir = join8(homedir7(), ".config", "mcp-s-cli");
3453
+ const dir = join9(homedir8(), ".config", "mcp-s-cli");
3373
3454
  mkdirSync6(dir, { recursive: true });
3374
3455
  const entry = JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), args: argv });
3375
- appendFileSync(join8(dir, "history.jsonl"), `${entry}
3456
+ appendFileSync(join9(dir, "history.jsonl"), `${entry}
3376
3457
  `, "utf8");
3377
3458
  } catch (err) {
3378
3459
  const msg = err instanceof Error ? err.message : String(err);
@@ -3471,6 +3552,9 @@ async function main() {
3471
3552
  case "logout":
3472
3553
  await logoutCommand({ configPath: args.configPath });
3473
3554
  break;
3555
+ case "check-auth":
3556
+ await checkAuthCommand({ configPath: args.configPath });
3557
+ break;
3474
3558
  case "clear":
3475
3559
  await clearCommand();
3476
3560
  break;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcp-s/cli",
3
- "version": "0.0.14",
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",