@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 +11 -0
- package/dist/SKILL.md +11 -0
- package/dist/daemon.js +1 -1
- package/dist/index.js +164 -80
- package/package.json +1 -1
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
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
|
|
6
|
-
import { join as
|
|
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.
|
|
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/
|
|
1908
|
-
import { existsSync as existsSync4,
|
|
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 {
|
|
1911
|
-
var
|
|
1912
|
-
|
|
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 =
|
|
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 =
|
|
2009
|
+
var HISTORY_PATH = join4(homedir3(), ".config", "mcp-s-cli", "history.jsonl");
|
|
1935
2010
|
function clearHistoryCommand() {
|
|
1936
|
-
if (!
|
|
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
|
|
1946
|
-
import { homedir as
|
|
1947
|
-
import { dirname as dirname3, join as
|
|
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
|
|
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 (!
|
|
1980
|
-
const content =
|
|
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 (!
|
|
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 (!
|
|
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
|
|
2290
|
-
import { homedir as
|
|
2291
|
-
import { dirname as dirname5, join as
|
|
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
|
|
2371
|
+
existsSync as existsSync7,
|
|
2297
2372
|
lstatSync,
|
|
2298
2373
|
mkdirSync as mkdirSync4,
|
|
2299
|
-
readFileSync as
|
|
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
|
|
2306
|
-
import { dirname as dirname4, join as
|
|
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 =
|
|
2383
|
+
var home = homedir5();
|
|
2309
2384
|
var AGENTS = {
|
|
2310
2385
|
cursor: {
|
|
2311
2386
|
displayName: "Cursor",
|
|
2312
|
-
globalSkillsDir:
|
|
2313
|
-
detect: () =>
|
|
2387
|
+
globalSkillsDir: join6(home, ".cursor", "skills"),
|
|
2388
|
+
detect: () => existsSync7(join6(home, ".cursor"))
|
|
2314
2389
|
},
|
|
2315
2390
|
codex: {
|
|
2316
2391
|
displayName: "Codex",
|
|
2317
|
-
globalSkillsDir:
|
|
2318
|
-
process.env.CODEX_HOME?.trim() ||
|
|
2392
|
+
globalSkillsDir: join6(
|
|
2393
|
+
process.env.CODEX_HOME?.trim() || join6(home, ".codex"),
|
|
2319
2394
|
"skills"
|
|
2320
2395
|
),
|
|
2321
|
-
detect: () =>
|
|
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:
|
|
2326
|
-
process.env.CLAUDE_CONFIG_DIR?.trim() ||
|
|
2400
|
+
globalSkillsDir: join6(
|
|
2401
|
+
process.env.CLAUDE_CONFIG_DIR?.trim() || join6(home, ".claude"),
|
|
2327
2402
|
"skills"
|
|
2328
2403
|
),
|
|
2329
|
-
detect: () =>
|
|
2330
|
-
process.env.CLAUDE_CONFIG_DIR?.trim() ||
|
|
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:
|
|
2336
|
-
detect: () =>
|
|
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:
|
|
2341
|
-
detect: () =>
|
|
2415
|
+
globalSkillsDir: join6(home, ".copilot", "skills"),
|
|
2416
|
+
detect: () => existsSync7(join6(home, ".copilot"))
|
|
2342
2417
|
},
|
|
2343
2418
|
cline: {
|
|
2344
2419
|
displayName: "Cline",
|
|
2345
|
-
globalSkillsDir:
|
|
2346
|
-
detect: () =>
|
|
2420
|
+
globalSkillsDir: join6(home, ".cline", "skills"),
|
|
2421
|
+
detect: () => existsSync7(join6(home, ".cline"))
|
|
2347
2422
|
},
|
|
2348
2423
|
roo: {
|
|
2349
2424
|
displayName: "Roo Code",
|
|
2350
|
-
globalSkillsDir:
|
|
2351
|
-
detect: () =>
|
|
2425
|
+
globalSkillsDir: join6(home, ".roo", "skills"),
|
|
2426
|
+
detect: () => existsSync7(join6(home, ".roo"))
|
|
2352
2427
|
},
|
|
2353
2428
|
continue: {
|
|
2354
2429
|
displayName: "Continue",
|
|
2355
|
-
globalSkillsDir:
|
|
2356
|
-
detect: () =>
|
|
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
|
|
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
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
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 (
|
|
2417
|
-
return
|
|
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(
|
|
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(
|
|
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 =
|
|
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(
|
|
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 =
|
|
2478
|
-
if (!
|
|
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 (
|
|
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 =
|
|
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:
|
|
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 =
|
|
2710
|
-
|
|
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 (
|
|
2718
|
-
return JSON.parse(
|
|
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
|
|
2922
|
+
import { existsSync as existsSync9, readdirSync as readdirSync2 } from "fs";
|
|
2848
2923
|
function killDaemonCommand() {
|
|
2849
2924
|
const socketDir = getSocketDir();
|
|
2850
|
-
if (!
|
|
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
|
|
2950
|
-
import { homedir as
|
|
2951
|
-
import { join as
|
|
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 =
|
|
2960
|
-
if (
|
|
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 =
|
|
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 (!
|
|
3067
|
+
if (!existsSync10(authFilePath)) {
|
|
2993
3068
|
console.log(" (no tokens stored)");
|
|
2994
3069
|
} else {
|
|
2995
3070
|
try {
|
|
2996
|
-
const raw2 =
|
|
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 =
|
|
3007
|
-
if (
|
|
3008
|
-
const lines =
|
|
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 =
|
|
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(
|
|
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;
|