@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 +11 -0
- package/dist/SKILL.md +11 -0
- package/dist/daemon.js +18 -5
- package/dist/index.js +179 -84
- 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
|
@@ -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
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
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.
|
|
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
|
|
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";
|
|
@@ -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
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
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.
|
|
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/
|
|
1910
|
-
import { existsSync as existsSync4,
|
|
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 {
|
|
1913
|
-
var
|
|
1914
|
-
|
|
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 =
|
|
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 =
|
|
2022
|
+
var HISTORY_PATH = join4(homedir3(), ".config", "mcp-s-cli", "history.jsonl");
|
|
1937
2023
|
function clearHistoryCommand() {
|
|
1938
|
-
if (!
|
|
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
|
|
1948
|
-
import { homedir as
|
|
1949
|
-
import { dirname as dirname3, join as
|
|
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
|
|
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 (!
|
|
1982
|
-
const content =
|
|
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 (!
|
|
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 (!
|
|
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
|
|
2292
|
-
import { homedir as
|
|
2293
|
-
import { dirname as dirname5, join as
|
|
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
|
|
2384
|
+
existsSync as existsSync7,
|
|
2299
2385
|
lstatSync,
|
|
2300
2386
|
mkdirSync as mkdirSync4,
|
|
2301
|
-
readFileSync as
|
|
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
|
|
2308
|
-
import { dirname as dirname4, join as
|
|
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 =
|
|
2396
|
+
var home = homedir5();
|
|
2311
2397
|
var AGENTS = {
|
|
2312
2398
|
cursor: {
|
|
2313
2399
|
displayName: "Cursor",
|
|
2314
|
-
globalSkillsDir:
|
|
2315
|
-
detect: () =>
|
|
2400
|
+
globalSkillsDir: join6(home, ".cursor", "skills"),
|
|
2401
|
+
detect: () => existsSync7(join6(home, ".cursor"))
|
|
2316
2402
|
},
|
|
2317
2403
|
codex: {
|
|
2318
2404
|
displayName: "Codex",
|
|
2319
|
-
globalSkillsDir:
|
|
2320
|
-
process.env.CODEX_HOME?.trim() ||
|
|
2405
|
+
globalSkillsDir: join6(
|
|
2406
|
+
process.env.CODEX_HOME?.trim() || join6(home, ".codex"),
|
|
2321
2407
|
"skills"
|
|
2322
2408
|
),
|
|
2323
|
-
detect: () =>
|
|
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:
|
|
2328
|
-
process.env.CLAUDE_CONFIG_DIR?.trim() ||
|
|
2413
|
+
globalSkillsDir: join6(
|
|
2414
|
+
process.env.CLAUDE_CONFIG_DIR?.trim() || join6(home, ".claude"),
|
|
2329
2415
|
"skills"
|
|
2330
2416
|
),
|
|
2331
|
-
detect: () =>
|
|
2332
|
-
process.env.CLAUDE_CONFIG_DIR?.trim() ||
|
|
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:
|
|
2338
|
-
detect: () =>
|
|
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:
|
|
2343
|
-
detect: () =>
|
|
2428
|
+
globalSkillsDir: join6(home, ".copilot", "skills"),
|
|
2429
|
+
detect: () => existsSync7(join6(home, ".copilot"))
|
|
2344
2430
|
},
|
|
2345
2431
|
cline: {
|
|
2346
2432
|
displayName: "Cline",
|
|
2347
|
-
globalSkillsDir:
|
|
2348
|
-
detect: () =>
|
|
2433
|
+
globalSkillsDir: join6(home, ".cline", "skills"),
|
|
2434
|
+
detect: () => existsSync7(join6(home, ".cline"))
|
|
2349
2435
|
},
|
|
2350
2436
|
roo: {
|
|
2351
2437
|
displayName: "Roo Code",
|
|
2352
|
-
globalSkillsDir:
|
|
2353
|
-
detect: () =>
|
|
2438
|
+
globalSkillsDir: join6(home, ".roo", "skills"),
|
|
2439
|
+
detect: () => existsSync7(join6(home, ".roo"))
|
|
2354
2440
|
},
|
|
2355
2441
|
continue: {
|
|
2356
2442
|
displayName: "Continue",
|
|
2357
|
-
globalSkillsDir:
|
|
2358
|
-
detect: () =>
|
|
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
|
|
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
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
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 (
|
|
2419
|
-
return
|
|
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(
|
|
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(
|
|
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 =
|
|
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(
|
|
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 =
|
|
2480
|
-
if (!
|
|
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 (
|
|
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 =
|
|
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:
|
|
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 =
|
|
2712
|
-
|
|
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 (
|
|
2720
|
-
return JSON.parse(
|
|
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
|
|
2935
|
+
import { existsSync as existsSync9, readdirSync as readdirSync2 } from "fs";
|
|
2850
2936
|
function killDaemonCommand() {
|
|
2851
2937
|
const socketDir = getSocketDir();
|
|
2852
|
-
if (!
|
|
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
|
|
2952
|
-
import { homedir as
|
|
2953
|
-
import { join as
|
|
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 =
|
|
2962
|
-
if (
|
|
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 =
|
|
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 (!
|
|
3080
|
+
if (!existsSync10(authFilePath)) {
|
|
2995
3081
|
console.log(" (no tokens stored)");
|
|
2996
3082
|
} else {
|
|
2997
3083
|
try {
|
|
2998
|
-
const raw2 =
|
|
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 =
|
|
3009
|
-
if (
|
|
3010
|
-
const lines =
|
|
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 =
|
|
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(
|
|
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;
|