@mcp-s/cli 0.0.10 → 0.0.12
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 +10 -0
- package/dist/SKILL.md +10 -0
- package/dist/daemon.js +1 -1
- package/dist/index.js +421 -96
- package/package.json +1 -1
package/SKILL.md
CHANGED
|
@@ -41,11 +41,21 @@ Every command consumes tokens. Pick the cheapest operation that gets the job don
|
|
|
41
41
|
- **Pipe output** through shell tools (`head`, `grep`, `jq`) to trim large responses before they enter context.
|
|
42
42
|
- Never fetch a full list when the user only needs a few fields (names, links, IDs).
|
|
43
43
|
|
|
44
|
+
### When a filter produces no output
|
|
45
|
+
|
|
46
|
+
When you pipe through a filter and get empty output, **do not** fall back to the raw unfiltered call. Use the **probe-then-filter** pattern:
|
|
47
|
+
|
|
48
|
+
1. **Probe**: fetch a *single* item (e.g., `limit: 1`, or a dedicated get-one tool) without any pipe.
|
|
49
|
+
2. **Learn**: read the response to understand the actual field names, nesting, and types.
|
|
50
|
+
3. **Re-run**: call the bulk tool again with a corrected filter based on what you learned.
|
|
51
|
+
|
|
44
52
|
### Decision heuristic
|
|
45
53
|
|
|
46
54
|
- User mentions a specific tool or domain keyword → `grep` for it.
|
|
47
55
|
- User asks a broad question across all tools → list tools first, then act.
|
|
56
|
+
- Task involves multiple items of the same type → use one bulk/list call, not N individual calls.
|
|
48
57
|
- Tool may return large payloads → use filter params + pipe to trim output.
|
|
58
|
+
- Filter script produced no output → **probe one item** to learn the shape, then re-run with a corrected filter. Never fall back to the raw unfiltered call.
|
|
49
59
|
|
|
50
60
|
## Examples
|
|
51
61
|
|
package/dist/SKILL.md
CHANGED
|
@@ -41,11 +41,21 @@ Every command consumes tokens. Pick the cheapest operation that gets the job don
|
|
|
41
41
|
- **Pipe output** through shell tools (`head`, `grep`, `jq`) to trim large responses before they enter context.
|
|
42
42
|
- Never fetch a full list when the user only needs a few fields (names, links, IDs).
|
|
43
43
|
|
|
44
|
+
### When a filter produces no output
|
|
45
|
+
|
|
46
|
+
When you pipe through a filter and get empty output, **do not** fall back to the raw unfiltered call. Use the **probe-then-filter** pattern:
|
|
47
|
+
|
|
48
|
+
1. **Probe**: fetch a *single* item (e.g., `limit: 1`, or a dedicated get-one tool) without any pipe.
|
|
49
|
+
2. **Learn**: read the response to understand the actual field names, nesting, and types.
|
|
50
|
+
3. **Re-run**: call the bulk tool again with a corrected filter based on what you learned.
|
|
51
|
+
|
|
44
52
|
### Decision heuristic
|
|
45
53
|
|
|
46
54
|
- User mentions a specific tool or domain keyword → `grep` for it.
|
|
47
55
|
- User asks a broad question across all tools → list tools first, then act.
|
|
56
|
+
- Task involves multiple items of the same type → use one bulk/list call, not N individual calls.
|
|
48
57
|
- Tool may return large payloads → use filter params + pipe to trim output.
|
|
58
|
+
- Filter script produced no output → **probe one item** to learn the shape, then re-run with a corrected filter. Never fall back to the raw unfiltered call.
|
|
49
59
|
|
|
50
60
|
## Examples
|
|
51
61
|
|
package/dist/daemon.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { appendFileSync, mkdirSync as
|
|
5
|
-
import { homedir as
|
|
6
|
-
import { join as
|
|
4
|
+
import { appendFileSync, mkdirSync as mkdirSync6 } from "fs";
|
|
5
|
+
import { homedir as homedir7 } from "os";
|
|
6
|
+
import { join as join8 } from "path";
|
|
7
7
|
|
|
8
8
|
// src/client.ts
|
|
9
9
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
@@ -190,7 +190,7 @@ async function exchangeAuthorizationCode(tokenEndpoint, clientId, code, codeVeri
|
|
|
190
190
|
}
|
|
191
191
|
async function startCallbackServer(port) {
|
|
192
192
|
const http = await import("http");
|
|
193
|
-
return new Promise((
|
|
193
|
+
return new Promise((resolve5, reject) => {
|
|
194
194
|
const server = http.createServer((req, res) => {
|
|
195
195
|
const url = new URL(req.url ?? "/", `http://localhost:${port}`);
|
|
196
196
|
const code = url.searchParams.get("code");
|
|
@@ -221,7 +221,7 @@ async function startCallbackServer(port) {
|
|
|
221
221
|
</html>
|
|
222
222
|
`);
|
|
223
223
|
server.close();
|
|
224
|
-
|
|
224
|
+
resolve5(code);
|
|
225
225
|
return;
|
|
226
226
|
}
|
|
227
227
|
res.writeHead(400, { "Content-Type": "text/html" });
|
|
@@ -764,6 +764,7 @@ async function loadConfig(explicitPath) {
|
|
|
764
764
|
throw new Error(formatCliError(configMissingFieldError(configPath)));
|
|
765
765
|
}
|
|
766
766
|
const knownFields = [
|
|
767
|
+
"enabled",
|
|
767
768
|
"org",
|
|
768
769
|
"baseUrl",
|
|
769
770
|
"mcp",
|
|
@@ -1017,7 +1018,7 @@ async function runDaemon(serverName, config, settings) {
|
|
|
1017
1018
|
};
|
|
1018
1019
|
}
|
|
1019
1020
|
};
|
|
1020
|
-
await new Promise((
|
|
1021
|
+
await new Promise((resolve5, reject) => {
|
|
1021
1022
|
server = createServer((socket) => {
|
|
1022
1023
|
activeConnections.add(socket);
|
|
1023
1024
|
debug(`[daemon:${serverName}] Client connected`);
|
|
@@ -1049,7 +1050,7 @@ async function runDaemon(serverName, config, settings) {
|
|
|
1049
1050
|
writeFileSync(getReadyPath(), String(process.pid), {
|
|
1050
1051
|
mode: 384
|
|
1051
1052
|
});
|
|
1052
|
-
|
|
1053
|
+
resolve5();
|
|
1053
1054
|
});
|
|
1054
1055
|
}).catch(async (error) => {
|
|
1055
1056
|
console.error(
|
|
@@ -1097,7 +1098,7 @@ function generateRequestId() {
|
|
|
1097
1098
|
return `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
1098
1099
|
}
|
|
1099
1100
|
async function sendRequest(socketPath, request) {
|
|
1100
|
-
return new Promise((
|
|
1101
|
+
return new Promise((resolve5, reject) => {
|
|
1101
1102
|
const socket = createConnection(socketPath);
|
|
1102
1103
|
let settled = false;
|
|
1103
1104
|
const settle = (fn) => {
|
|
@@ -1125,7 +1126,7 @@ async function sendRequest(socketPath, request) {
|
|
|
1125
1126
|
const response = JSON.parse(line);
|
|
1126
1127
|
clearTimeout(timer);
|
|
1127
1128
|
socket.end();
|
|
1128
|
-
settle(() =>
|
|
1129
|
+
settle(() => resolve5(response));
|
|
1129
1130
|
} catch {
|
|
1130
1131
|
clearTimeout(timer);
|
|
1131
1132
|
socket.end();
|
|
@@ -1297,7 +1298,7 @@ async function cleanupOrphanedDaemons() {
|
|
|
1297
1298
|
}
|
|
1298
1299
|
|
|
1299
1300
|
// src/version.ts
|
|
1300
|
-
var VERSION = "0.0.
|
|
1301
|
+
var VERSION = "0.0.12";
|
|
1301
1302
|
|
|
1302
1303
|
// src/client.ts
|
|
1303
1304
|
function getRetryConfig(settings) {
|
|
@@ -1349,7 +1350,7 @@ function calculateDelay(attempt, config) {
|
|
|
1349
1350
|
return Math.round(cappedDelay + jitter);
|
|
1350
1351
|
}
|
|
1351
1352
|
function sleep(ms) {
|
|
1352
|
-
return new Promise((
|
|
1353
|
+
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
1353
1354
|
}
|
|
1354
1355
|
async function withRetry(fn, operationName, config = getRetryConfig()) {
|
|
1355
1356
|
let lastError;
|
|
@@ -1940,6 +1941,247 @@ function clearHistoryCommand() {
|
|
|
1940
1941
|
console.log(`Cleared history from ${HISTORY_PATH}`);
|
|
1941
1942
|
}
|
|
1942
1943
|
|
|
1944
|
+
// 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";
|
|
1948
|
+
var TOP_LEVEL_KEYS = [
|
|
1949
|
+
"enabled",
|
|
1950
|
+
"org",
|
|
1951
|
+
"baseUrl",
|
|
1952
|
+
"mcp",
|
|
1953
|
+
"toolkit",
|
|
1954
|
+
"userAccessKey",
|
|
1955
|
+
"token",
|
|
1956
|
+
"allowedTools",
|
|
1957
|
+
"disabledTools",
|
|
1958
|
+
"settings"
|
|
1959
|
+
];
|
|
1960
|
+
var SETTINGS_KEYS = [
|
|
1961
|
+
"settings.timeout",
|
|
1962
|
+
"settings.maxRetries",
|
|
1963
|
+
"settings.retryDelay",
|
|
1964
|
+
"settings.daemon",
|
|
1965
|
+
"settings.daemonTimeout",
|
|
1966
|
+
"settings.history"
|
|
1967
|
+
];
|
|
1968
|
+
var ALL_KNOWN_KEYS = [...TOP_LEVEL_KEYS, ...SETTINGS_KEYS];
|
|
1969
|
+
function getDefaultConfigPath() {
|
|
1970
|
+
return join4(homedir3(), ".config", "mcp-s-cli", "config.json");
|
|
1971
|
+
}
|
|
1972
|
+
function resolveConfigPath(explicitPath) {
|
|
1973
|
+
if (explicitPath) return resolve2(explicitPath);
|
|
1974
|
+
if (process.env.MCP_S_CLI_CONFIG_PATH)
|
|
1975
|
+
return resolve2(process.env.MCP_S_CLI_CONFIG_PATH);
|
|
1976
|
+
return getDefaultConfigPath();
|
|
1977
|
+
}
|
|
1978
|
+
function readConfigFile(configPath) {
|
|
1979
|
+
if (!existsSync5(configPath)) return {};
|
|
1980
|
+
const content = readFileSync3(configPath, "utf-8").trim();
|
|
1981
|
+
if (!content) return {};
|
|
1982
|
+
try {
|
|
1983
|
+
const parsed = JSON.parse(content);
|
|
1984
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
1985
|
+
return parsed;
|
|
1986
|
+
}
|
|
1987
|
+
return {};
|
|
1988
|
+
} catch {
|
|
1989
|
+
console.error(
|
|
1990
|
+
formatCliError({
|
|
1991
|
+
code: 1 /* CLIENT_ERROR */,
|
|
1992
|
+
type: "CONFIG_INVALID_JSON",
|
|
1993
|
+
message: `Cannot parse config file: ${configPath}`,
|
|
1994
|
+
suggestion: "Fix or delete the file, then run mcp-s-cli init"
|
|
1995
|
+
})
|
|
1996
|
+
);
|
|
1997
|
+
process.exit(1 /* CLIENT_ERROR */);
|
|
1998
|
+
}
|
|
1999
|
+
}
|
|
2000
|
+
function writeConfigFile(configPath, config) {
|
|
2001
|
+
mkdirSync3(dirname3(configPath), { recursive: true });
|
|
2002
|
+
writeFileSync3(configPath, `${JSON.stringify(config, null, 2)}
|
|
2003
|
+
`, "utf-8");
|
|
2004
|
+
}
|
|
2005
|
+
function parseValue(raw) {
|
|
2006
|
+
if (raw === "true") return true;
|
|
2007
|
+
if (raw === "false") return false;
|
|
2008
|
+
const n = Number(raw);
|
|
2009
|
+
if (!Number.isNaN(n) && raw.trim() !== "") return n;
|
|
2010
|
+
return raw;
|
|
2011
|
+
}
|
|
2012
|
+
function setNestedKey(obj, parts, value) {
|
|
2013
|
+
let current = obj;
|
|
2014
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
2015
|
+
const part = parts[i];
|
|
2016
|
+
if (current[part] === void 0 || current[part] === null || typeof current[part] !== "object" || Array.isArray(current[part])) {
|
|
2017
|
+
current[part] = {};
|
|
2018
|
+
}
|
|
2019
|
+
current = current[part];
|
|
2020
|
+
}
|
|
2021
|
+
current[parts[parts.length - 1]] = value;
|
|
2022
|
+
}
|
|
2023
|
+
function getNestedKey(obj, parts) {
|
|
2024
|
+
let current = obj;
|
|
2025
|
+
for (const part of parts) {
|
|
2026
|
+
if (current === null || typeof current !== "object") return void 0;
|
|
2027
|
+
current = current[part];
|
|
2028
|
+
}
|
|
2029
|
+
return current;
|
|
2030
|
+
}
|
|
2031
|
+
function validateKey(key) {
|
|
2032
|
+
const known = ALL_KNOWN_KEYS;
|
|
2033
|
+
if (!known.includes(key)) {
|
|
2034
|
+
console.error(
|
|
2035
|
+
formatCliError({
|
|
2036
|
+
code: 1 /* CLIENT_ERROR */,
|
|
2037
|
+
type: "UNKNOWN_CONFIG_KEY",
|
|
2038
|
+
message: `Unknown config key: "${key}"`,
|
|
2039
|
+
details: `Known keys: ${ALL_KNOWN_KEYS.join(", ")}`,
|
|
2040
|
+
suggestion: "Use dot-notation for nested keys, e.g. settings.timeout, settings.daemon"
|
|
2041
|
+
})
|
|
2042
|
+
);
|
|
2043
|
+
process.exit(1 /* CLIENT_ERROR */);
|
|
2044
|
+
}
|
|
2045
|
+
}
|
|
2046
|
+
function configSetCommand(opts) {
|
|
2047
|
+
const { key, value, configPath: explicitPath } = opts;
|
|
2048
|
+
validateKey(key);
|
|
2049
|
+
const configPath = resolveConfigPath(explicitPath);
|
|
2050
|
+
const config = readConfigFile(configPath);
|
|
2051
|
+
const parts = key.split(".");
|
|
2052
|
+
const parsed = parseValue(value);
|
|
2053
|
+
setNestedKey(config, parts, parsed);
|
|
2054
|
+
writeConfigFile(configPath, config);
|
|
2055
|
+
console.log(`Set ${key} = ${JSON.stringify(parsed)} in ${configPath}`);
|
|
2056
|
+
}
|
|
2057
|
+
function configGetCommand(opts) {
|
|
2058
|
+
const { key, configPath: explicitPath } = opts;
|
|
2059
|
+
validateKey(key);
|
|
2060
|
+
const configPath = resolveConfigPath(explicitPath);
|
|
2061
|
+
if (!existsSync5(configPath)) {
|
|
2062
|
+
console.error(
|
|
2063
|
+
formatCliError({
|
|
2064
|
+
code: 1 /* CLIENT_ERROR */,
|
|
2065
|
+
type: "CONFIG_NOT_FOUND",
|
|
2066
|
+
message: `Config file not found: ${configPath}`,
|
|
2067
|
+
suggestion: "Run mcp-s-cli init or mcp-s-cli config set <key> <value>"
|
|
2068
|
+
})
|
|
2069
|
+
);
|
|
2070
|
+
process.exit(1 /* CLIENT_ERROR */);
|
|
2071
|
+
}
|
|
2072
|
+
const config = readConfigFile(configPath);
|
|
2073
|
+
const parts = key.split(".");
|
|
2074
|
+
const value = getNestedKey(config, parts);
|
|
2075
|
+
if (value === void 0) {
|
|
2076
|
+
console.error(
|
|
2077
|
+
formatCliError({
|
|
2078
|
+
code: 1 /* CLIENT_ERROR */,
|
|
2079
|
+
type: "CONFIG_KEY_NOT_SET",
|
|
2080
|
+
message: `Key "${key}" is not set in ${configPath}`,
|
|
2081
|
+
suggestion: `Set it with: mcp-s-cli config set ${key} <value>`
|
|
2082
|
+
})
|
|
2083
|
+
);
|
|
2084
|
+
process.exit(1 /* CLIENT_ERROR */);
|
|
2085
|
+
}
|
|
2086
|
+
console.log(JSON.stringify(value));
|
|
2087
|
+
}
|
|
2088
|
+
function configShowCommand(opts) {
|
|
2089
|
+
const configPath = resolveConfigPath(opts.configPath);
|
|
2090
|
+
if (!existsSync5(configPath)) {
|
|
2091
|
+
console.error(
|
|
2092
|
+
formatCliError({
|
|
2093
|
+
code: 1 /* CLIENT_ERROR */,
|
|
2094
|
+
type: "CONFIG_NOT_FOUND",
|
|
2095
|
+
message: `Config file not found: ${configPath}`,
|
|
2096
|
+
suggestion: "Run mcp-s-cli init or mcp-s-cli config set <key> <value>"
|
|
2097
|
+
})
|
|
2098
|
+
);
|
|
2099
|
+
process.exit(1 /* CLIENT_ERROR */);
|
|
2100
|
+
}
|
|
2101
|
+
const config = readConfigFile(configPath);
|
|
2102
|
+
console.log(JSON.stringify(config, null, 2));
|
|
2103
|
+
}
|
|
2104
|
+
function printConfigHelp() {
|
|
2105
|
+
console.log(`
|
|
2106
|
+
mcp-s-cli config - Read and write config.json settings
|
|
2107
|
+
|
|
2108
|
+
Usage:
|
|
2109
|
+
mcp-s-cli config set <key> <value> Set a config value
|
|
2110
|
+
mcp-s-cli config get <key> Print a single config value
|
|
2111
|
+
mcp-s-cli config show Print the full config as JSON
|
|
2112
|
+
|
|
2113
|
+
Keys (dot-notation for nested values):
|
|
2114
|
+
|
|
2115
|
+
General
|
|
2116
|
+
enabled boolean Whether mcp-s-cli is enabled (default: true)
|
|
2117
|
+
|
|
2118
|
+
Connection
|
|
2119
|
+
org string Org name \u2014 derives URL as https://<org>.mcp-s.com/mcp
|
|
2120
|
+
baseUrl string Custom server base URL (used when org is not set)
|
|
2121
|
+
mcp string MCP identifier sent as x-mcp header
|
|
2122
|
+
toolkit string Toolkit name sent as x-toolkit header
|
|
2123
|
+
token string Static Bearer token for HTTP auth
|
|
2124
|
+
userAccessKey string User access key \u2014 triggers stdio mode when present
|
|
2125
|
+
|
|
2126
|
+
Tool filtering
|
|
2127
|
+
allowedTools array Glob patterns for tools to allow (JSON array)
|
|
2128
|
+
disabledTools array Glob patterns to exclude (takes precedence over allowedTools)
|
|
2129
|
+
|
|
2130
|
+
Behaviour (settings.*)
|
|
2131
|
+
settings.timeout number Request timeout in seconds (default: 1800)
|
|
2132
|
+
settings.maxRetries number Max retry attempts; 0 = disable (default: 3)
|
|
2133
|
+
settings.retryDelay number Base retry delay in milliseconds (default: 1000)
|
|
2134
|
+
settings.daemon boolean Enable daemon connection caching (default: true)
|
|
2135
|
+
settings.daemonTimeout number Daemon idle timeout in seconds (default: 300)
|
|
2136
|
+
settings.history boolean Append invocations to history.jsonl (default: false)
|
|
2137
|
+
|
|
2138
|
+
Examples:
|
|
2139
|
+
mcp-s-cli config set org my-org
|
|
2140
|
+
mcp-s-cli config set settings.timeout 30
|
|
2141
|
+
mcp-s-cli config set settings.daemon false
|
|
2142
|
+
mcp-s-cli config get settings.timeout
|
|
2143
|
+
mcp-s-cli config show
|
|
2144
|
+
`);
|
|
2145
|
+
}
|
|
2146
|
+
function configCommand(opts) {
|
|
2147
|
+
switch (opts.subcommand) {
|
|
2148
|
+
case "set": {
|
|
2149
|
+
if (!opts.key) {
|
|
2150
|
+
console.error(
|
|
2151
|
+
formatCliError(missingArgumentError("config set", "key"))
|
|
2152
|
+
);
|
|
2153
|
+
process.exit(1 /* CLIENT_ERROR */);
|
|
2154
|
+
}
|
|
2155
|
+
if (opts.value === void 0) {
|
|
2156
|
+
console.error(
|
|
2157
|
+
formatCliError(missingArgumentError("config set", "value"))
|
|
2158
|
+
);
|
|
2159
|
+
process.exit(1 /* CLIENT_ERROR */);
|
|
2160
|
+
}
|
|
2161
|
+
configSetCommand({
|
|
2162
|
+
key: opts.key,
|
|
2163
|
+
value: opts.value,
|
|
2164
|
+
configPath: opts.configPath
|
|
2165
|
+
});
|
|
2166
|
+
break;
|
|
2167
|
+
}
|
|
2168
|
+
case "get": {
|
|
2169
|
+
if (!opts.key) {
|
|
2170
|
+
console.error(
|
|
2171
|
+
formatCliError(missingArgumentError("config get", "key"))
|
|
2172
|
+
);
|
|
2173
|
+
process.exit(1 /* CLIENT_ERROR */);
|
|
2174
|
+
}
|
|
2175
|
+
configGetCommand({ key: opts.key, configPath: opts.configPath });
|
|
2176
|
+
break;
|
|
2177
|
+
}
|
|
2178
|
+
case "show": {
|
|
2179
|
+
configShowCommand({ configPath: opts.configPath });
|
|
2180
|
+
break;
|
|
2181
|
+
}
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2184
|
+
|
|
1943
2185
|
// src/commands/grep.ts
|
|
1944
2186
|
function globToRegex(pattern) {
|
|
1945
2187
|
let escaped = "";
|
|
@@ -2068,97 +2310,97 @@ async function infoCommand(options) {
|
|
|
2068
2310
|
}
|
|
2069
2311
|
|
|
2070
2312
|
// src/commands/init.ts
|
|
2071
|
-
import { existsSync as
|
|
2072
|
-
import { homedir as
|
|
2073
|
-
import { dirname as
|
|
2313
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "fs";
|
|
2314
|
+
import { homedir as homedir5 } from "os";
|
|
2315
|
+
import { dirname as dirname5, join as join6 } from "path";
|
|
2074
2316
|
import * as readline from "readline";
|
|
2075
2317
|
|
|
2076
2318
|
// src/commands/install-skill.ts
|
|
2077
2319
|
import {
|
|
2078
|
-
existsSync as
|
|
2320
|
+
existsSync as existsSync6,
|
|
2079
2321
|
lstatSync,
|
|
2080
|
-
mkdirSync as
|
|
2081
|
-
readFileSync as
|
|
2322
|
+
mkdirSync as mkdirSync4,
|
|
2323
|
+
readFileSync as readFileSync4,
|
|
2082
2324
|
readlinkSync,
|
|
2083
2325
|
rmSync as rmSync2,
|
|
2084
2326
|
symlinkSync,
|
|
2085
|
-
writeFileSync as
|
|
2327
|
+
writeFileSync as writeFileSync4
|
|
2086
2328
|
} from "fs";
|
|
2087
|
-
import { homedir as
|
|
2088
|
-
import { dirname as
|
|
2329
|
+
import { homedir as homedir4, platform } from "os";
|
|
2330
|
+
import { dirname as dirname4, join as join5, normalize, relative, resolve as resolve3, sep } from "path";
|
|
2089
2331
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2090
|
-
var home =
|
|
2332
|
+
var home = homedir4();
|
|
2091
2333
|
var AGENTS = {
|
|
2092
2334
|
cursor: {
|
|
2093
2335
|
displayName: "Cursor",
|
|
2094
|
-
globalSkillsDir:
|
|
2095
|
-
detect: () =>
|
|
2336
|
+
globalSkillsDir: join5(home, ".cursor", "skills"),
|
|
2337
|
+
detect: () => existsSync6(join5(home, ".cursor"))
|
|
2096
2338
|
},
|
|
2097
2339
|
codex: {
|
|
2098
2340
|
displayName: "Codex",
|
|
2099
|
-
globalSkillsDir:
|
|
2100
|
-
process.env.CODEX_HOME?.trim() ||
|
|
2341
|
+
globalSkillsDir: join5(
|
|
2342
|
+
process.env.CODEX_HOME?.trim() || join5(home, ".codex"),
|
|
2101
2343
|
"skills"
|
|
2102
2344
|
),
|
|
2103
|
-
detect: () =>
|
|
2345
|
+
detect: () => existsSync6(process.env.CODEX_HOME?.trim() || join5(home, ".codex")) || existsSync6("/etc/codex")
|
|
2104
2346
|
},
|
|
2105
2347
|
"claude-code": {
|
|
2106
2348
|
displayName: "Claude Code",
|
|
2107
|
-
globalSkillsDir:
|
|
2108
|
-
process.env.CLAUDE_CONFIG_DIR?.trim() ||
|
|
2349
|
+
globalSkillsDir: join5(
|
|
2350
|
+
process.env.CLAUDE_CONFIG_DIR?.trim() || join5(home, ".claude"),
|
|
2109
2351
|
"skills"
|
|
2110
2352
|
),
|
|
2111
|
-
detect: () =>
|
|
2112
|
-
process.env.CLAUDE_CONFIG_DIR?.trim() ||
|
|
2353
|
+
detect: () => existsSync6(
|
|
2354
|
+
process.env.CLAUDE_CONFIG_DIR?.trim() || join5(home, ".claude")
|
|
2113
2355
|
)
|
|
2114
2356
|
},
|
|
2115
2357
|
windsurf: {
|
|
2116
2358
|
displayName: "Windsurf",
|
|
2117
|
-
globalSkillsDir:
|
|
2118
|
-
detect: () =>
|
|
2359
|
+
globalSkillsDir: join5(home, ".codeium", "windsurf", "skills"),
|
|
2360
|
+
detect: () => existsSync6(join5(home, ".codeium", "windsurf"))
|
|
2119
2361
|
},
|
|
2120
2362
|
"github-copilot": {
|
|
2121
2363
|
displayName: "GitHub Copilot",
|
|
2122
|
-
globalSkillsDir:
|
|
2123
|
-
detect: () =>
|
|
2364
|
+
globalSkillsDir: join5(home, ".copilot", "skills"),
|
|
2365
|
+
detect: () => existsSync6(join5(home, ".copilot"))
|
|
2124
2366
|
},
|
|
2125
2367
|
cline: {
|
|
2126
2368
|
displayName: "Cline",
|
|
2127
|
-
globalSkillsDir:
|
|
2128
|
-
detect: () =>
|
|
2369
|
+
globalSkillsDir: join5(home, ".cline", "skills"),
|
|
2370
|
+
detect: () => existsSync6(join5(home, ".cline"))
|
|
2129
2371
|
},
|
|
2130
2372
|
roo: {
|
|
2131
2373
|
displayName: "Roo Code",
|
|
2132
|
-
globalSkillsDir:
|
|
2133
|
-
detect: () =>
|
|
2374
|
+
globalSkillsDir: join5(home, ".roo", "skills"),
|
|
2375
|
+
detect: () => existsSync6(join5(home, ".roo"))
|
|
2134
2376
|
},
|
|
2135
2377
|
continue: {
|
|
2136
2378
|
displayName: "Continue",
|
|
2137
|
-
globalSkillsDir:
|
|
2138
|
-
detect: () =>
|
|
2379
|
+
globalSkillsDir: join5(home, ".continue", "skills"),
|
|
2380
|
+
detect: () => existsSync6(join5(home, ".continue")) || existsSync6(join5(process.cwd(), ".continue"))
|
|
2139
2381
|
}
|
|
2140
2382
|
};
|
|
2141
2383
|
function getCanonicalSkillDir(skillName) {
|
|
2142
|
-
return
|
|
2384
|
+
return join5(home, ".agents", "skills", skillName);
|
|
2143
2385
|
}
|
|
2144
2386
|
function getCanonicalSkillPath() {
|
|
2145
2387
|
return getCanonicalSkillDir(SKILL_NAME);
|
|
2146
2388
|
}
|
|
2147
2389
|
function isPathSafe(base, target) {
|
|
2148
|
-
const normalizedBase = normalize(
|
|
2149
|
-
const normalizedTarget = normalize(
|
|
2390
|
+
const normalizedBase = normalize(resolve3(base));
|
|
2391
|
+
const normalizedTarget = normalize(resolve3(target));
|
|
2150
2392
|
return normalizedTarget.startsWith(normalizedBase + sep) || normalizedTarget === normalizedBase;
|
|
2151
2393
|
}
|
|
2152
2394
|
function createSymlinkSync(target, linkPath) {
|
|
2153
2395
|
try {
|
|
2154
|
-
const resolvedTarget =
|
|
2155
|
-
const resolvedLinkPath =
|
|
2396
|
+
const resolvedTarget = resolve3(target);
|
|
2397
|
+
const resolvedLinkPath = resolve3(linkPath);
|
|
2156
2398
|
if (resolvedTarget === resolvedLinkPath) return true;
|
|
2157
2399
|
try {
|
|
2158
2400
|
const stats = lstatSync(linkPath);
|
|
2159
2401
|
if (stats.isSymbolicLink()) {
|
|
2160
2402
|
const existing = readlinkSync(linkPath);
|
|
2161
|
-
const resolvedExisting =
|
|
2403
|
+
const resolvedExisting = resolve3(dirname4(linkPath), existing);
|
|
2162
2404
|
if (resolvedExisting === resolvedTarget) return true;
|
|
2163
2405
|
rmSync2(linkPath);
|
|
2164
2406
|
} else {
|
|
@@ -2172,8 +2414,8 @@ function createSymlinkSync(target, linkPath) {
|
|
|
2172
2414
|
}
|
|
2173
2415
|
}
|
|
2174
2416
|
}
|
|
2175
|
-
const linkDir =
|
|
2176
|
-
|
|
2417
|
+
const linkDir = dirname4(linkPath);
|
|
2418
|
+
mkdirSync4(linkDir, { recursive: true });
|
|
2177
2419
|
const relativePath = relative(linkDir, target);
|
|
2178
2420
|
symlinkSync(
|
|
2179
2421
|
relativePath,
|
|
@@ -2188,15 +2430,15 @@ function createSymlinkSync(target, linkPath) {
|
|
|
2188
2430
|
var SKILL_NAME = "mcp-s-cli";
|
|
2189
2431
|
function getSkillMdContent() {
|
|
2190
2432
|
const __filename = fileURLToPath2(import.meta.url);
|
|
2191
|
-
const __dirname =
|
|
2433
|
+
const __dirname = dirname4(__filename);
|
|
2192
2434
|
const candidates = [
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2435
|
+
join5(__dirname, "SKILL.md"),
|
|
2436
|
+
join5(__dirname, "..", "SKILL.md"),
|
|
2437
|
+
join5(__dirname, "..", "..", "SKILL.md")
|
|
2196
2438
|
];
|
|
2197
2439
|
for (const candidate of candidates) {
|
|
2198
|
-
if (
|
|
2199
|
-
return
|
|
2440
|
+
if (existsSync6(candidate)) {
|
|
2441
|
+
return readFileSync4(candidate, "utf-8");
|
|
2200
2442
|
}
|
|
2201
2443
|
}
|
|
2202
2444
|
throw new Error(
|
|
@@ -2206,12 +2448,12 @@ function getSkillMdContent() {
|
|
|
2206
2448
|
function installSkill() {
|
|
2207
2449
|
const content = getSkillMdContent();
|
|
2208
2450
|
const canonicalDir = getCanonicalSkillDir(SKILL_NAME);
|
|
2209
|
-
if (!isPathSafe(
|
|
2451
|
+
if (!isPathSafe(join5(home, ".agents", "skills"), canonicalDir)) {
|
|
2210
2452
|
throw new Error("Invalid skill name: potential path traversal detected");
|
|
2211
2453
|
}
|
|
2212
2454
|
rmSync2(canonicalDir, { recursive: true, force: true });
|
|
2213
|
-
|
|
2214
|
-
|
|
2455
|
+
mkdirSync4(canonicalDir, { recursive: true });
|
|
2456
|
+
writeFileSync4(join5(canonicalDir, "SKILL.md"), content, "utf-8");
|
|
2215
2457
|
const result = {
|
|
2216
2458
|
installed: [],
|
|
2217
2459
|
skipped: [],
|
|
@@ -2223,7 +2465,7 @@ function installSkill() {
|
|
|
2223
2465
|
result.skipped.push(agentName);
|
|
2224
2466
|
continue;
|
|
2225
2467
|
}
|
|
2226
|
-
const agentSkillDir =
|
|
2468
|
+
const agentSkillDir = join5(agent.globalSkillsDir, SKILL_NAME);
|
|
2227
2469
|
if (!isPathSafe(agent.globalSkillsDir, agentSkillDir)) {
|
|
2228
2470
|
result.failed.push({
|
|
2229
2471
|
agent: agentName,
|
|
@@ -2235,8 +2477,8 @@ function installSkill() {
|
|
|
2235
2477
|
const symlinkOk = createSymlinkSync(canonicalDir, agentSkillDir);
|
|
2236
2478
|
if (!symlinkOk) {
|
|
2237
2479
|
rmSync2(agentSkillDir, { recursive: true, force: true });
|
|
2238
|
-
|
|
2239
|
-
|
|
2480
|
+
mkdirSync4(agentSkillDir, { recursive: true });
|
|
2481
|
+
writeFileSync4(join5(agentSkillDir, "SKILL.md"), content, "utf-8");
|
|
2240
2482
|
}
|
|
2241
2483
|
result.installed.push(agentName);
|
|
2242
2484
|
} catch (err) {
|
|
@@ -2256,8 +2498,8 @@ function clearSkill() {
|
|
|
2256
2498
|
canonicalRemoved: false
|
|
2257
2499
|
};
|
|
2258
2500
|
for (const [agentName, agent] of Object.entries(AGENTS)) {
|
|
2259
|
-
const agentSkillDir =
|
|
2260
|
-
if (!
|
|
2501
|
+
const agentSkillDir = join5(agent.globalSkillsDir, SKILL_NAME);
|
|
2502
|
+
if (!existsSync6(agentSkillDir)) {
|
|
2261
2503
|
result.skipped.push(agentName);
|
|
2262
2504
|
continue;
|
|
2263
2505
|
}
|
|
@@ -2272,7 +2514,7 @@ function clearSkill() {
|
|
|
2272
2514
|
}
|
|
2273
2515
|
}
|
|
2274
2516
|
const canonicalDir = getCanonicalSkillDir(SKILL_NAME);
|
|
2275
|
-
if (
|
|
2517
|
+
if (existsSync6(canonicalDir)) {
|
|
2276
2518
|
try {
|
|
2277
2519
|
rmSync2(canonicalDir, { recursive: true, force: true });
|
|
2278
2520
|
result.canonicalRemoved = true;
|
|
@@ -2283,12 +2525,12 @@ function clearSkill() {
|
|
|
2283
2525
|
}
|
|
2284
2526
|
function getSkillInstallInfo() {
|
|
2285
2527
|
return Object.entries(AGENTS).filter(([, agent]) => agent.detect()).map(([agentName, agent]) => {
|
|
2286
|
-
const agentSkillDir =
|
|
2528
|
+
const agentSkillDir = join5(agent.globalSkillsDir, SKILL_NAME);
|
|
2287
2529
|
return {
|
|
2288
2530
|
agent: agentName,
|
|
2289
2531
|
displayName: agent.displayName,
|
|
2290
2532
|
path: agentSkillDir,
|
|
2291
|
-
installed:
|
|
2533
|
+
installed: existsSync6(join5(agentSkillDir, "SKILL.md"))
|
|
2292
2534
|
};
|
|
2293
2535
|
});
|
|
2294
2536
|
}
|
|
@@ -2358,7 +2600,7 @@ function cancel(msg) {
|
|
|
2358
2600
|
`);
|
|
2359
2601
|
}
|
|
2360
2602
|
function readLine(prompt) {
|
|
2361
|
-
return new Promise((
|
|
2603
|
+
return new Promise((resolve5) => {
|
|
2362
2604
|
const rl = readline.createInterface({
|
|
2363
2605
|
input: process.stdin,
|
|
2364
2606
|
output: process.stdout,
|
|
@@ -2367,16 +2609,16 @@ function readLine(prompt) {
|
|
|
2367
2609
|
process.stdout.write(prompt);
|
|
2368
2610
|
rl.once("line", (line) => {
|
|
2369
2611
|
rl.close();
|
|
2370
|
-
|
|
2612
|
+
resolve5(line);
|
|
2371
2613
|
});
|
|
2372
2614
|
rl.once("SIGINT", () => {
|
|
2373
2615
|
rl.close();
|
|
2374
2616
|
process.stdout.write("\n");
|
|
2375
|
-
|
|
2617
|
+
resolve5(CANCEL);
|
|
2376
2618
|
});
|
|
2377
2619
|
process.stdin.once("end", () => {
|
|
2378
2620
|
rl.close();
|
|
2379
|
-
|
|
2621
|
+
resolve5(CANCEL);
|
|
2380
2622
|
});
|
|
2381
2623
|
});
|
|
2382
2624
|
}
|
|
@@ -2409,14 +2651,14 @@ async function select(opts) {
|
|
|
2409
2651
|
`);
|
|
2410
2652
|
});
|
|
2411
2653
|
};
|
|
2412
|
-
return new Promise((
|
|
2654
|
+
return new Promise((resolve5) => {
|
|
2413
2655
|
readline.emitKeypressEvents(process.stdin);
|
|
2414
2656
|
process.stdin.setRawMode(true);
|
|
2415
2657
|
process.stdin.resume();
|
|
2416
2658
|
const onKey = (_, key) => {
|
|
2417
2659
|
if (key.ctrl && key.name === "c") {
|
|
2418
2660
|
cleanup();
|
|
2419
|
-
|
|
2661
|
+
resolve5(CANCEL);
|
|
2420
2662
|
return;
|
|
2421
2663
|
}
|
|
2422
2664
|
if (key.name === "up") {
|
|
@@ -2429,7 +2671,7 @@ async function select(opts) {
|
|
|
2429
2671
|
}
|
|
2430
2672
|
if (key.name === "return") {
|
|
2431
2673
|
cleanup();
|
|
2432
|
-
|
|
2674
|
+
resolve5(options[idx].value);
|
|
2433
2675
|
}
|
|
2434
2676
|
};
|
|
2435
2677
|
const cleanup = () => {
|
|
@@ -2480,24 +2722,24 @@ ${BAR} ${c(A.cyan, "\u25C6")} `
|
|
|
2480
2722
|
}
|
|
2481
2723
|
}
|
|
2482
2724
|
var p = { intro, outro, cancel, isCancel, select, text };
|
|
2483
|
-
var GLOBAL_CONFIG_PATH2 =
|
|
2484
|
-
|
|
2725
|
+
var GLOBAL_CONFIG_PATH2 = join6(
|
|
2726
|
+
homedir5(),
|
|
2485
2727
|
".config",
|
|
2486
2728
|
"mcp-s-cli",
|
|
2487
2729
|
"config.json"
|
|
2488
2730
|
);
|
|
2489
2731
|
function readExistingConfig() {
|
|
2490
2732
|
try {
|
|
2491
|
-
if (
|
|
2492
|
-
return JSON.parse(
|
|
2733
|
+
if (existsSync7(GLOBAL_CONFIG_PATH2)) {
|
|
2734
|
+
return JSON.parse(readFileSync5(GLOBAL_CONFIG_PATH2, "utf-8"));
|
|
2493
2735
|
}
|
|
2494
2736
|
} catch {
|
|
2495
2737
|
}
|
|
2496
2738
|
return {};
|
|
2497
2739
|
}
|
|
2498
2740
|
function writeConfig(config) {
|
|
2499
|
-
|
|
2500
|
-
|
|
2741
|
+
mkdirSync5(dirname5(GLOBAL_CONFIG_PATH2), { recursive: true });
|
|
2742
|
+
writeFileSync5(
|
|
2501
2743
|
GLOBAL_CONFIG_PATH2,
|
|
2502
2744
|
`${JSON.stringify(config, null, 2)}
|
|
2503
2745
|
`,
|
|
@@ -2618,10 +2860,10 @@ async function initInteractive() {
|
|
|
2618
2860
|
}
|
|
2619
2861
|
|
|
2620
2862
|
// src/commands/kill-daemon.ts
|
|
2621
|
-
import { existsSync as
|
|
2863
|
+
import { existsSync as existsSync8, readdirSync as readdirSync2 } from "fs";
|
|
2622
2864
|
function killDaemonCommand() {
|
|
2623
2865
|
const socketDir = getSocketDir();
|
|
2624
|
-
if (!
|
|
2866
|
+
if (!existsSync8(socketDir)) {
|
|
2625
2867
|
console.log("No daemons running.");
|
|
2626
2868
|
return;
|
|
2627
2869
|
}
|
|
@@ -2720,18 +2962,18 @@ async function logoutCommand(options) {
|
|
|
2720
2962
|
}
|
|
2721
2963
|
|
|
2722
2964
|
// src/commands/whoami.ts
|
|
2723
|
-
import { existsSync as
|
|
2724
|
-
import { homedir as
|
|
2725
|
-
import { join as
|
|
2965
|
+
import { existsSync as existsSync9, readFileSync as readFileSync6, statSync } from "fs";
|
|
2966
|
+
import { homedir as homedir6 } from "os";
|
|
2967
|
+
import { join as join7, resolve as resolve4 } from "path";
|
|
2726
2968
|
function getResolvedConfigPath(explicitPath) {
|
|
2727
2969
|
if (explicitPath) {
|
|
2728
|
-
return
|
|
2970
|
+
return resolve4(explicitPath);
|
|
2729
2971
|
}
|
|
2730
2972
|
if (process.env.MCP_S_CLI_CONFIG_PATH) {
|
|
2731
|
-
return
|
|
2973
|
+
return resolve4(process.env.MCP_S_CLI_CONFIG_PATH);
|
|
2732
2974
|
}
|
|
2733
|
-
const defaultPath =
|
|
2734
|
-
if (
|
|
2975
|
+
const defaultPath = join7(homedir6(), ".config", "mcp-s-cli", "config.json");
|
|
2976
|
+
if (existsSync9(defaultPath)) {
|
|
2735
2977
|
return defaultPath;
|
|
2736
2978
|
}
|
|
2737
2979
|
return null;
|
|
@@ -2759,15 +3001,15 @@ async function whoamiCommand(options) {
|
|
|
2759
3001
|
console.log(` Error loading config: ${err.message}`);
|
|
2760
3002
|
return;
|
|
2761
3003
|
}
|
|
2762
|
-
const authFilePath =
|
|
3004
|
+
const authFilePath = join7(homedir6(), ".config", "mcp-s-cli", "auth.json");
|
|
2763
3005
|
console.log("");
|
|
2764
3006
|
console.log("OAuth Tokens");
|
|
2765
3007
|
console.log(` Path: ${authFilePath}`);
|
|
2766
|
-
if (!
|
|
3008
|
+
if (!existsSync9(authFilePath)) {
|
|
2767
3009
|
console.log(" (no tokens stored)");
|
|
2768
3010
|
} else {
|
|
2769
3011
|
try {
|
|
2770
|
-
const raw2 =
|
|
3012
|
+
const raw2 = readFileSync6(authFilePath, "utf-8");
|
|
2771
3013
|
const data = JSON.parse(raw2);
|
|
2772
3014
|
const tokenEntries = Object.entries(data.tokens ?? {});
|
|
2773
3015
|
if (tokenEntries.length === 0) {
|
|
@@ -2777,9 +3019,9 @@ async function whoamiCommand(options) {
|
|
|
2777
3019
|
console.log(" (could not read auth file)");
|
|
2778
3020
|
}
|
|
2779
3021
|
}
|
|
2780
|
-
const historyPath =
|
|
2781
|
-
if (
|
|
2782
|
-
const lines =
|
|
3022
|
+
const historyPath = join7(homedir6(), ".config", "mcp-s-cli", "history.jsonl");
|
|
3023
|
+
if (existsSync9(historyPath)) {
|
|
3024
|
+
const lines = readFileSync6(historyPath, "utf-8").split("\n").filter(Boolean);
|
|
2783
3025
|
const size = statSync(historyPath).size;
|
|
2784
3026
|
const sizeStr = size >= 1024 * 1024 ? `${(size / (1024 * 1024)).toFixed(1)} MB` : size >= 1024 ? `${(size / 1024).toFixed(1)} KB` : `${size} B`;
|
|
2785
3027
|
console.log("");
|
|
@@ -2841,6 +3083,10 @@ function parseArgs2(args) {
|
|
|
2841
3083
|
switch (arg) {
|
|
2842
3084
|
case "-h":
|
|
2843
3085
|
case "--help":
|
|
3086
|
+
if (positional[0] === "config") {
|
|
3087
|
+
positional.push(arg);
|
|
3088
|
+
break;
|
|
3089
|
+
}
|
|
2844
3090
|
result.command = "help";
|
|
2845
3091
|
return result;
|
|
2846
3092
|
case "-v":
|
|
@@ -3004,6 +3250,38 @@ function parseArgs2(args) {
|
|
|
3004
3250
|
result.command = "kill-daemon";
|
|
3005
3251
|
return result;
|
|
3006
3252
|
}
|
|
3253
|
+
if (firstArg === "enable") {
|
|
3254
|
+
result.command = "enable";
|
|
3255
|
+
return result;
|
|
3256
|
+
}
|
|
3257
|
+
if (firstArg === "disable") {
|
|
3258
|
+
result.command = "disable";
|
|
3259
|
+
return result;
|
|
3260
|
+
}
|
|
3261
|
+
if (firstArg === "config") {
|
|
3262
|
+
result.command = "config";
|
|
3263
|
+
const sub = positional[1];
|
|
3264
|
+
if (!sub || sub === "--help" || sub === "-h" || sub === "help") {
|
|
3265
|
+
printConfigHelp();
|
|
3266
|
+
process.exit(0);
|
|
3267
|
+
}
|
|
3268
|
+
if (sub !== "set" && sub !== "get" && sub !== "show") {
|
|
3269
|
+
console.error(
|
|
3270
|
+
formatCliError({
|
|
3271
|
+
code: 1 /* CLIENT_ERROR */,
|
|
3272
|
+
type: "UNKNOWN_SUBCOMMAND",
|
|
3273
|
+
message: `Unknown config subcommand: "${sub}"`,
|
|
3274
|
+
details: "Valid subcommands: set, get, show",
|
|
3275
|
+
suggestion: "Run mcp-s-cli config --help to see usage and valid keys"
|
|
3276
|
+
})
|
|
3277
|
+
);
|
|
3278
|
+
process.exit(1 /* CLIENT_ERROR */);
|
|
3279
|
+
}
|
|
3280
|
+
result.configSubcommand = sub;
|
|
3281
|
+
result.configKey = positional[2];
|
|
3282
|
+
result.configValue = positional[3];
|
|
3283
|
+
return result;
|
|
3284
|
+
}
|
|
3007
3285
|
if (firstArg === "init") {
|
|
3008
3286
|
result.command = "init";
|
|
3009
3287
|
if (result.baseUrl && result.org) {
|
|
@@ -3043,10 +3321,16 @@ Usage:
|
|
|
3043
3321
|
mcp-s-cli whoami Show config location and auth state
|
|
3044
3322
|
mcp-s-cli login Log in to the configured server via OAuth
|
|
3045
3323
|
mcp-s-cli logout Log out (remove stored OAuth tokens)
|
|
3324
|
+
mcp-s-cli config set <key> <value> Set a value in config.json
|
|
3325
|
+
mcp-s-cli config get <key> Get a value from config.json
|
|
3326
|
+
mcp-s-cli config show Print full config.json
|
|
3327
|
+
mcp-s-cli config --help List all config keys with types and defaults
|
|
3046
3328
|
mcp-s-cli clear Clear server config (resets config.json to {})
|
|
3047
3329
|
mcp-s-cli clear-auth Clear all stored auth data (sets auth.json to {})
|
|
3048
3330
|
mcp-s-cli clear-history Delete the history file (~/.config/mcp-s-cli/history.jsonl)
|
|
3049
3331
|
mcp-s-cli clear-skill Remove the mcp-s-cli skill from all agent skill directories
|
|
3332
|
+
mcp-s-cli enable Enable mcp-s-cli
|
|
3333
|
+
mcp-s-cli disable Disable mcp-s-cli
|
|
3050
3334
|
|
|
3051
3335
|
Options:
|
|
3052
3336
|
-h, --help Show this help message
|
|
@@ -3078,10 +3362,13 @@ Examples:
|
|
|
3078
3362
|
mcp-s-cli whoami # Show config and auth state
|
|
3079
3363
|
mcp-s-cli login # Authenticate via OAuth
|
|
3080
3364
|
mcp-s-cli logout # Remove stored OAuth tokens
|
|
3365
|
+
mcp-s-cli config set settings.timeout 30 # Set request timeout to 30s
|
|
3081
3366
|
mcp-s-cli clear # Reset server config
|
|
3082
3367
|
mcp-s-cli clear-auth # Clear stored auth data
|
|
3083
3368
|
mcp-s-cli clear-skill # Remove installed skill from all agents
|
|
3084
3369
|
mcp-s-cli kill-daemon # Kill the running daemon process
|
|
3370
|
+
mcp-s-cli enable # Enable mcp-s-cli
|
|
3371
|
+
mcp-s-cli disable # Disable mcp-s-cli
|
|
3085
3372
|
|
|
3086
3373
|
Environment Variables:
|
|
3087
3374
|
MCP_S_CLI_DEBUG=1 Enable debug output
|
|
@@ -3098,10 +3385,10 @@ Config File:
|
|
|
3098
3385
|
function appendHistory(argv, history) {
|
|
3099
3386
|
if (!history) return;
|
|
3100
3387
|
try {
|
|
3101
|
-
const dir =
|
|
3102
|
-
|
|
3388
|
+
const dir = join8(homedir7(), ".config", "mcp-s-cli");
|
|
3389
|
+
mkdirSync6(dir, { recursive: true });
|
|
3103
3390
|
const entry = JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), args: argv });
|
|
3104
|
-
appendFileSync(
|
|
3391
|
+
appendFileSync(join8(dir, "history.jsonl"), `${entry}
|
|
3105
3392
|
`, "utf8");
|
|
3106
3393
|
} catch (err) {
|
|
3107
3394
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -3115,12 +3402,26 @@ async function main() {
|
|
|
3115
3402
|
const argv = process.argv.slice(2);
|
|
3116
3403
|
const args = parseArgs2(argv);
|
|
3117
3404
|
let settings;
|
|
3405
|
+
let enabled;
|
|
3118
3406
|
try {
|
|
3119
3407
|
const loaded = await loadConfig(args.configPath);
|
|
3120
3408
|
settings = loaded.settings;
|
|
3409
|
+
enabled = loaded.raw.enabled;
|
|
3121
3410
|
} catch {
|
|
3122
3411
|
}
|
|
3123
3412
|
appendHistory(argv, settings?.history);
|
|
3413
|
+
const META_COMMANDS = /* @__PURE__ */ new Set([
|
|
3414
|
+
"enable",
|
|
3415
|
+
"disable",
|
|
3416
|
+
"config",
|
|
3417
|
+
"help",
|
|
3418
|
+
"version",
|
|
3419
|
+
"init"
|
|
3420
|
+
]);
|
|
3421
|
+
if (!(enabled ?? true) && !META_COMMANDS.has(args.command)) {
|
|
3422
|
+
console.error("mcp-s-cli is disabled. To enable, run: mcp-s-cli enable");
|
|
3423
|
+
process.exit(1);
|
|
3424
|
+
}
|
|
3124
3425
|
switch (args.command) {
|
|
3125
3426
|
case "help":
|
|
3126
3427
|
printHelp();
|
|
@@ -3155,6 +3456,14 @@ async function main() {
|
|
|
3155
3456
|
configPath: args.configPath
|
|
3156
3457
|
});
|
|
3157
3458
|
break;
|
|
3459
|
+
case "config":
|
|
3460
|
+
configCommand({
|
|
3461
|
+
subcommand: args.configSubcommand ?? "show",
|
|
3462
|
+
key: args.configKey,
|
|
3463
|
+
value: args.configValue,
|
|
3464
|
+
configPath: args.configPath
|
|
3465
|
+
});
|
|
3466
|
+
break;
|
|
3158
3467
|
case "init":
|
|
3159
3468
|
if (args.baseUrl || args.org) {
|
|
3160
3469
|
await initCommand({
|
|
@@ -3208,6 +3517,22 @@ async function main() {
|
|
|
3208
3517
|
}
|
|
3209
3518
|
break;
|
|
3210
3519
|
}
|
|
3520
|
+
case "enable":
|
|
3521
|
+
configSetCommand({
|
|
3522
|
+
key: "enabled",
|
|
3523
|
+
value: "true",
|
|
3524
|
+
configPath: args.configPath
|
|
3525
|
+
});
|
|
3526
|
+
console.log("mcp-s-cli is now enabled.");
|
|
3527
|
+
break;
|
|
3528
|
+
case "disable":
|
|
3529
|
+
configSetCommand({
|
|
3530
|
+
key: "enabled",
|
|
3531
|
+
value: "false",
|
|
3532
|
+
configPath: args.configPath
|
|
3533
|
+
});
|
|
3534
|
+
console.log("mcp-s-cli is now disabled.");
|
|
3535
|
+
break;
|
|
3211
3536
|
}
|
|
3212
3537
|
}
|
|
3213
3538
|
process.on("SIGINT", () => {
|