@mcp-s/cli 0.0.9 → 0.0.11

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/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 mkdirSync5 } from "fs";
5
- import { homedir as homedir6 } from "os";
6
- import { join as join7 } from "path";
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";
@@ -60,7 +60,7 @@ async function saveAuthData() {
60
60
  mode: 384
61
61
  });
62
62
  } catch (err) {
63
- if (process.env.MCP_DEBUG) {
63
+ if (process.env.MCP_S_CLI_DEBUG) {
64
64
  console.error(
65
65
  `[mcp-s-cli] Failed to save auth data: ${err.message}`
66
66
  );
@@ -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((resolve4, reject) => {
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
- resolve4(code);
224
+ resolve5(code);
225
225
  return;
226
226
  }
227
227
  res.writeHead(400, { "Content-Type": "text/html" });
@@ -583,51 +583,35 @@ var DEFAULT_MAX_RETRIES = 3;
583
583
  var DEFAULT_RETRY_DELAY_MS = 1e3;
584
584
  var DEFAULT_DAEMON_TIMEOUT_SECONDS = 300;
585
585
  function debug(message) {
586
- if (process.env.MCP_DEBUG) {
586
+ if (process.env.MCP_S_CLI_DEBUG) {
587
587
  console.error(`[mcp-s-cli] ${message}`);
588
588
  }
589
589
  }
590
- function getTimeoutMs() {
591
- const envTimeout = process.env.MCP_TIMEOUT;
592
- if (envTimeout) {
593
- const seconds = Number.parseInt(envTimeout, 10);
594
- if (!Number.isNaN(seconds) && seconds > 0) {
595
- return seconds * 1e3;
596
- }
590
+ function getTimeoutMs(settings) {
591
+ if (settings?.timeout != null && settings.timeout > 0) {
592
+ return settings.timeout * 1e3;
597
593
  }
598
594
  return DEFAULT_TIMEOUT_MS;
599
595
  }
600
- function getMaxRetries() {
601
- const envRetries = process.env.MCP_MAX_RETRIES;
602
- if (envRetries) {
603
- const retries = Number.parseInt(envRetries, 10);
604
- if (!Number.isNaN(retries) && retries >= 0) {
605
- return retries;
606
- }
596
+ function getMaxRetries(settings) {
597
+ if (settings?.maxRetries != null && settings.maxRetries >= 0) {
598
+ return settings.maxRetries;
607
599
  }
608
600
  return DEFAULT_MAX_RETRIES;
609
601
  }
610
- function getRetryDelayMs() {
611
- const envDelay = process.env.MCP_RETRY_DELAY;
612
- if (envDelay) {
613
- const delay = Number.parseInt(envDelay, 10);
614
- if (!Number.isNaN(delay) && delay > 0) {
615
- return delay;
616
- }
602
+ function getRetryDelayMs(settings) {
603
+ if (settings?.retryDelay != null && settings.retryDelay > 0) {
604
+ return settings.retryDelay;
617
605
  }
618
606
  return DEFAULT_RETRY_DELAY_MS;
619
607
  }
620
608
  var DAEMON_SERVER_NAME = "mcp-s-cli";
621
- function isDaemonEnabled() {
622
- return process.env.MCP_DAEMON !== "0";
609
+ function isDaemonEnabled(settings) {
610
+ return settings?.daemon !== false;
623
611
  }
624
- function getDaemonTimeoutMs() {
625
- const envTimeout = process.env.MCP_DAEMON_TIMEOUT;
626
- if (envTimeout) {
627
- const seconds = Number.parseInt(envTimeout, 10);
628
- if (!Number.isNaN(seconds) && seconds > 0) {
629
- return seconds * 1e3;
630
- }
612
+ function getDaemonTimeoutMs(settings) {
613
+ if (settings?.daemonTimeout != null && settings.daemonTimeout > 0) {
614
+ return settings.daemonTimeout * 1e3;
631
615
  }
632
616
  return DEFAULT_DAEMON_TIMEOUT_SECONDS * 1e3;
633
617
  }
@@ -650,7 +634,7 @@ function getConfigHash(config) {
650
634
  return createHash("sha256").update(str).digest("hex").slice(0, 16);
651
635
  }
652
636
  function isStrictEnvMode() {
653
- const value = process.env.MCP_STRICT_ENV?.toLowerCase();
637
+ const value = process.env.MCP_S_CLI_STRICT_ENV?.toLowerCase();
654
638
  return value !== "false" && value !== "0";
655
639
  }
656
640
  function substituteEnvVars(value) {
@@ -673,7 +657,7 @@ function substituteEnvVars(value) {
673
657
  type: "MISSING_ENV_VAR",
674
658
  message,
675
659
  details: "Referenced in config but not set in environment",
676
- suggestion: `Set the variable(s) before running: export ${missingVars[0]}="value" or set MCP_STRICT_ENV=false to use empty values`
660
+ suggestion: `Set the variable(s) before running: export ${missingVars[0]}="value" or set MCP_S_CLI_STRICT_ENV=false to use empty values`
677
661
  })
678
662
  );
679
663
  }
@@ -748,8 +732,8 @@ async function loadConfig(explicitPath) {
748
732
  let configPath;
749
733
  if (explicitPath) {
750
734
  configPath = resolve(explicitPath);
751
- } else if (process.env.MCP_CONFIG_PATH) {
752
- configPath = resolve(process.env.MCP_CONFIG_PATH);
735
+ } else if (process.env.MCP_S_CLI_CONFIG_PATH) {
736
+ configPath = resolve(process.env.MCP_S_CLI_CONFIG_PATH);
753
737
  }
754
738
  if (configPath) {
755
739
  if (!existsSync(configPath)) {
@@ -780,6 +764,7 @@ async function loadConfig(explicitPath) {
780
764
  throw new Error(formatCliError(configMissingFieldError(configPath)));
781
765
  }
782
766
  const knownFields = [
767
+ "enabled",
783
768
  "org",
784
769
  "baseUrl",
785
770
  "mcp",
@@ -787,7 +772,8 @@ async function loadConfig(explicitPath) {
787
772
  "userAccessKey",
788
773
  "token",
789
774
  "allowedTools",
790
- "disabledTools"
775
+ "disabledTools",
776
+ "settings"
791
777
  ];
792
778
  const hasKnownField = knownFields.some((f) => f in raw);
793
779
  if (!hasKnownField) {
@@ -795,7 +781,7 @@ async function loadConfig(explicitPath) {
795
781
  }
796
782
  raw = substituteEnvVarsInObject(raw);
797
783
  const serverConfig = deriveServerConfig(raw);
798
- return { raw, serverConfig };
784
+ return { raw, serverConfig, settings: raw.settings ?? {} };
799
785
  }
800
786
 
801
787
  // src/daemon-client.ts
@@ -883,10 +869,10 @@ function killProcess(pid) {
883
869
  return false;
884
870
  }
885
871
  }
886
- async function runDaemon(serverName, config) {
872
+ async function runDaemon(serverName, config, settings) {
887
873
  const socketPath = getSocketPath();
888
874
  const configHash = getConfigHash(config);
889
- const timeoutMs = getDaemonTimeoutMs();
875
+ const timeoutMs = getDaemonTimeoutMs(settings);
890
876
  let idleTimer = null;
891
877
  let mcpClient = null;
892
878
  let server = null;
@@ -1032,7 +1018,7 @@ async function runDaemon(serverName, config) {
1032
1018
  };
1033
1019
  }
1034
1020
  };
1035
- await new Promise((resolve4, reject) => {
1021
+ await new Promise((resolve5, reject) => {
1036
1022
  server = createServer((socket) => {
1037
1023
  activeConnections.add(socket);
1038
1024
  debug(`[daemon:${serverName}] Client connected`);
@@ -1064,7 +1050,7 @@ async function runDaemon(serverName, config) {
1064
1050
  writeFileSync(getReadyPath(), String(process.pid), {
1065
1051
  mode: 384
1066
1052
  });
1067
- resolve4();
1053
+ resolve5();
1068
1054
  });
1069
1055
  }).catch(async (error) => {
1070
1056
  console.error(
@@ -1078,8 +1064,11 @@ async function runDaemon(serverName, config) {
1078
1064
  if (process.argv[2] === "--daemon") {
1079
1065
  const serverName = process.argv[3];
1080
1066
  const configJson = process.argv[4];
1067
+ const settingsJson = process.argv[5];
1081
1068
  if (!serverName || !configJson) {
1082
- console.error("Usage: daemon.ts --daemon <serverName> <configJson>");
1069
+ console.error(
1070
+ "Usage: daemon.ts --daemon <serverName> <configJson> [settingsJson]"
1071
+ );
1083
1072
  process.exit(1);
1084
1073
  }
1085
1074
  let config;
@@ -1089,7 +1078,16 @@ if (process.argv[2] === "--daemon") {
1089
1078
  console.error("Invalid config JSON");
1090
1079
  process.exit(1);
1091
1080
  }
1092
- runDaemon(serverName, config).catch((error) => {
1081
+ let settings;
1082
+ if (settingsJson) {
1083
+ try {
1084
+ settings = JSON.parse(settingsJson);
1085
+ } catch {
1086
+ console.error("Invalid settings JSON");
1087
+ process.exit(1);
1088
+ }
1089
+ }
1090
+ runDaemon(serverName, config, settings).catch((error) => {
1093
1091
  console.error("Daemon failed:", error);
1094
1092
  process.exit(1);
1095
1093
  });
@@ -1100,7 +1098,7 @@ function generateRequestId() {
1100
1098
  return `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
1101
1099
  }
1102
1100
  async function sendRequest(socketPath, request) {
1103
- return new Promise((resolve4, reject) => {
1101
+ return new Promise((resolve5, reject) => {
1104
1102
  const socket = createConnection(socketPath);
1105
1103
  let settled = false;
1106
1104
  const settle = (fn) => {
@@ -1128,7 +1126,7 @@ async function sendRequest(socketPath, request) {
1128
1126
  const response = JSON.parse(line);
1129
1127
  clearTimeout(timer);
1130
1128
  socket.end();
1131
- settle(() => resolve4(response));
1129
+ settle(() => resolve5(response));
1132
1130
  } catch {
1133
1131
  clearTimeout(timer);
1134
1132
  socket.end();
@@ -1175,17 +1173,18 @@ function isDaemonValid(serverName, config) {
1175
1173
  }
1176
1174
  return true;
1177
1175
  }
1178
- async function spawnDaemon(serverName, config) {
1176
+ async function spawnDaemon(serverName, config, settings) {
1179
1177
  debug(`[daemon-client] Spawning daemon for ${serverName}`);
1180
1178
  const __dirname = join2(fileURLToPath(import.meta.url), "..");
1181
1179
  const daemonScript = join2(__dirname, "daemon.js");
1182
1180
  const configJson = JSON.stringify(config);
1181
+ const settingsJson = JSON.stringify(settings ?? {});
1183
1182
  const proc = spawn(
1184
1183
  "node",
1185
- [daemonScript, "--daemon", serverName, configJson],
1184
+ [daemonScript, "--daemon", serverName, configJson, settingsJson],
1186
1185
  {
1187
1186
  stdio: "ignore",
1188
- env: { ...process.env, MCP_NO_OAUTH: "1" },
1187
+ env: { ...process.env, MCP_S_CLI_NO_OAUTH: "1" },
1189
1188
  detached: true
1190
1189
  }
1191
1190
  );
@@ -1208,10 +1207,10 @@ async function spawnDaemon(serverName, config) {
1208
1207
  debug(`[daemon-client] Daemon spawn timeout for ${serverName}`);
1209
1208
  return false;
1210
1209
  }
1211
- async function getDaemonConnection(serverName, config) {
1210
+ async function getDaemonConnection(serverName, config, settings) {
1212
1211
  const socketPath = getSocketPath();
1213
1212
  if (!isDaemonValid(serverName, config)) {
1214
- const spawned = await spawnDaemon(serverName, config);
1213
+ const spawned = await spawnDaemon(serverName, config, settings);
1215
1214
  if (!spawned) {
1216
1215
  debug(`[daemon-client] Failed to spawn daemon for ${serverName}`);
1217
1216
  return null;
@@ -1299,13 +1298,13 @@ async function cleanupOrphanedDaemons() {
1299
1298
  }
1300
1299
 
1301
1300
  // src/version.ts
1302
- var VERSION = "0.0.9";
1301
+ var VERSION = "0.0.11";
1303
1302
 
1304
1303
  // src/client.ts
1305
- function getRetryConfig() {
1306
- const totalBudgetMs = getTimeoutMs();
1307
- const maxRetries = getMaxRetries();
1308
- const baseDelayMs = getRetryDelayMs();
1304
+ function getRetryConfig(settings) {
1305
+ const totalBudgetMs = getTimeoutMs(settings);
1306
+ const maxRetries = getMaxRetries(settings);
1307
+ const baseDelayMs = getRetryDelayMs(settings);
1309
1308
  const retryBudgetMs = Math.max(0, totalBudgetMs - 5e3);
1310
1309
  return {
1311
1310
  maxRetries,
@@ -1351,7 +1350,7 @@ function calculateDelay(attempt, config) {
1351
1350
  return Math.round(cappedDelay + jitter);
1352
1351
  }
1353
1352
  function sleep(ms) {
1354
- return new Promise((resolve4) => setTimeout(resolve4, ms));
1353
+ return new Promise((resolve5) => setTimeout(resolve5, ms));
1355
1354
  }
1356
1355
  async function withRetry(fn, operationName, config = getRetryConfig()) {
1357
1356
  let lastError;
@@ -1443,7 +1442,7 @@ ${stderrOutput}`;
1443
1442
  }, `connect to ${serverName}`);
1444
1443
  }
1445
1444
  async function connectToHttpServer(serverName, config) {
1446
- const oauthEnabled = process.env.MCP_NO_OAUTH !== "1";
1445
+ const oauthEnabled = process.env.MCP_S_CLI_NO_OAUTH !== "1";
1447
1446
  return withRetry(async () => {
1448
1447
  const configuredAuth = config.headers?.Authorization || config.headers?.authorization;
1449
1448
  let headers = { ...config.headers };
@@ -1593,24 +1592,32 @@ async function listTools(client) {
1593
1592
  }));
1594
1593
  }, "list tools");
1595
1594
  }
1596
- async function callTool(client, toolName, args) {
1597
- return withRetry(async () => {
1598
- const result = await client.callTool(
1599
- {
1600
- name: toolName,
1601
- arguments: args
1602
- },
1603
- void 0,
1604
- { timeout: getTimeoutMs() }
1605
- );
1606
- return result;
1607
- }, `call tool ${toolName}`);
1595
+ async function callTool(client, toolName, args, settings) {
1596
+ return withRetry(
1597
+ async () => {
1598
+ const result = await client.callTool(
1599
+ {
1600
+ name: toolName,
1601
+ arguments: args
1602
+ },
1603
+ void 0,
1604
+ { timeout: getTimeoutMs(settings) }
1605
+ );
1606
+ return result;
1607
+ },
1608
+ `call tool ${toolName}`,
1609
+ getRetryConfig(settings)
1610
+ );
1608
1611
  }
1609
- async function getConnection(serverName, config) {
1612
+ async function getConnection(serverName, config, settings) {
1610
1613
  await cleanupOrphanedDaemons();
1611
- if (isDaemonEnabled()) {
1614
+ if (isDaemonEnabled(settings)) {
1612
1615
  try {
1613
- const daemonConn = await getDaemonConnection(serverName, config);
1616
+ const daemonConn = await getDaemonConnection(
1617
+ serverName,
1618
+ config,
1619
+ settings
1620
+ );
1614
1621
  if (daemonConn) {
1615
1622
  debug(`Using daemon connection for ${serverName}`);
1616
1623
  return {
@@ -1653,7 +1660,7 @@ async function getConnection(serverName, config) {
1653
1660
  if (!isToolAllowed(toolName, config)) {
1654
1661
  throw new Error(`Tool "${toolName}" is disabled by configuration`);
1655
1662
  }
1656
- return callTool(client, toolName, args);
1663
+ return callTool(client, toolName, args, settings);
1657
1664
  },
1658
1665
  async getInstructions() {
1659
1666
  return client.getInstructions();
@@ -1794,12 +1801,12 @@ function formatToolResult(result) {
1794
1801
  }
1795
1802
 
1796
1803
  // src/commands/call.ts
1797
- async function parseArgs(argsString) {
1804
+ async function parseArgs(argsString, settings) {
1798
1805
  let jsonString;
1799
1806
  if (argsString) {
1800
1807
  jsonString = argsString;
1801
1808
  } else if (!process.stdin.isTTY) {
1802
- const timeoutMs = getTimeoutMs();
1809
+ const timeoutMs = getTimeoutMs(settings);
1803
1810
  const chunks = [];
1804
1811
  let timeoutId;
1805
1812
  const readPromise = (async () => {
@@ -1841,11 +1848,11 @@ async function callCommand(options) {
1841
1848
  console.error(error.message);
1842
1849
  process.exit(1 /* CLIENT_ERROR */);
1843
1850
  }
1844
- const { serverConfig } = loaded;
1851
+ const { serverConfig, settings } = loaded;
1845
1852
  const serverLabel = "server";
1846
1853
  if (options.args !== void 0) {
1847
1854
  try {
1848
- await parseArgs(options.args);
1855
+ await parseArgs(options.args, settings);
1849
1856
  } catch (error) {
1850
1857
  console.error(error.message);
1851
1858
  process.exit(1 /* CLIENT_ERROR */);
@@ -1853,14 +1860,14 @@ async function callCommand(options) {
1853
1860
  }
1854
1861
  let args;
1855
1862
  try {
1856
- args = await parseArgs(options.args);
1863
+ args = await parseArgs(options.args, settings);
1857
1864
  } catch (error) {
1858
1865
  console.error(error.message);
1859
1866
  process.exit(1 /* CLIENT_ERROR */);
1860
1867
  }
1861
1868
  let connection;
1862
1869
  try {
1863
- connection = await getConnection(serverLabel, serverConfig);
1870
+ connection = await getConnection(serverLabel, serverConfig, settings);
1864
1871
  } catch (error) {
1865
1872
  console.error(
1866
1873
  formatCliError(
@@ -1934,6 +1941,247 @@ function clearHistoryCommand() {
1934
1941
  console.log(`Cleared history from ${HISTORY_PATH}`);
1935
1942
  }
1936
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
+
1937
2185
  // src/commands/grep.ts
1938
2186
  function globToRegex(pattern) {
1939
2187
  let escaped = "";
@@ -1970,7 +2218,7 @@ async function grepCommand(options) {
1970
2218
  console.error(error.message);
1971
2219
  process.exit(1 /* CLIENT_ERROR */);
1972
2220
  }
1973
- const { serverConfig } = loaded;
2221
+ const { serverConfig, settings } = loaded;
1974
2222
  const serverLabel = "server";
1975
2223
  const pattern = globToRegex(options.pattern);
1976
2224
  debug(`Searching for pattern "${options.pattern}"`);
@@ -1978,7 +2226,7 @@ async function grepCommand(options) {
1978
2226
  const allResults = [];
1979
2227
  let searchError;
1980
2228
  try {
1981
- connection = await getConnection(serverLabel, serverConfig);
2229
+ connection = await getConnection(serverLabel, serverConfig, settings);
1982
2230
  const tools = await connection.listTools();
1983
2231
  for (const tool of tools) {
1984
2232
  if (pattern.test(tool.name)) {
@@ -2016,11 +2264,11 @@ async function infoCommand(options) {
2016
2264
  console.error(error.message);
2017
2265
  process.exit(1 /* CLIENT_ERROR */);
2018
2266
  }
2019
- const { serverConfig } = loaded;
2267
+ const { serverConfig, settings } = loaded;
2020
2268
  const serverLabel = "server";
2021
2269
  let connection;
2022
2270
  try {
2023
- connection = await getConnection(serverLabel, serverConfig);
2271
+ connection = await getConnection(serverLabel, serverConfig, settings);
2024
2272
  } catch (error) {
2025
2273
  console.error(
2026
2274
  formatCliError(
@@ -2062,97 +2310,97 @@ async function infoCommand(options) {
2062
2310
  }
2063
2311
 
2064
2312
  // src/commands/init.ts
2065
- import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
2066
- import { homedir as homedir4 } from "os";
2067
- import { dirname as dirname4, join as join5 } from "path";
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";
2068
2316
  import * as readline from "readline";
2069
2317
 
2070
2318
  // src/commands/install-skill.ts
2071
2319
  import {
2072
- existsSync as existsSync5,
2320
+ existsSync as existsSync6,
2073
2321
  lstatSync,
2074
- mkdirSync as mkdirSync3,
2075
- readFileSync as readFileSync3,
2322
+ mkdirSync as mkdirSync4,
2323
+ readFileSync as readFileSync4,
2076
2324
  readlinkSync,
2077
2325
  rmSync as rmSync2,
2078
2326
  symlinkSync,
2079
- writeFileSync as writeFileSync3
2327
+ writeFileSync as writeFileSync4
2080
2328
  } from "fs";
2081
- import { homedir as homedir3, platform } from "os";
2082
- import { dirname as dirname3, join as join4, normalize, relative, resolve as resolve2, sep } from "path";
2329
+ import { homedir as homedir4, platform } from "os";
2330
+ import { dirname as dirname4, join as join5, normalize, relative, resolve as resolve3, sep } from "path";
2083
2331
  import { fileURLToPath as fileURLToPath2 } from "url";
2084
- var home = homedir3();
2332
+ var home = homedir4();
2085
2333
  var AGENTS = {
2086
2334
  cursor: {
2087
2335
  displayName: "Cursor",
2088
- globalSkillsDir: join4(home, ".cursor", "skills"),
2089
- detect: () => existsSync5(join4(home, ".cursor"))
2336
+ globalSkillsDir: join5(home, ".cursor", "skills"),
2337
+ detect: () => existsSync6(join5(home, ".cursor"))
2090
2338
  },
2091
2339
  codex: {
2092
2340
  displayName: "Codex",
2093
- globalSkillsDir: join4(
2094
- process.env.CODEX_HOME?.trim() || join4(home, ".codex"),
2341
+ globalSkillsDir: join5(
2342
+ process.env.CODEX_HOME?.trim() || join5(home, ".codex"),
2095
2343
  "skills"
2096
2344
  ),
2097
- detect: () => existsSync5(process.env.CODEX_HOME?.trim() || join4(home, ".codex")) || existsSync5("/etc/codex")
2345
+ detect: () => existsSync6(process.env.CODEX_HOME?.trim() || join5(home, ".codex")) || existsSync6("/etc/codex")
2098
2346
  },
2099
2347
  "claude-code": {
2100
2348
  displayName: "Claude Code",
2101
- globalSkillsDir: join4(
2102
- process.env.CLAUDE_CONFIG_DIR?.trim() || join4(home, ".claude"),
2349
+ globalSkillsDir: join5(
2350
+ process.env.CLAUDE_CONFIG_DIR?.trim() || join5(home, ".claude"),
2103
2351
  "skills"
2104
2352
  ),
2105
- detect: () => existsSync5(
2106
- process.env.CLAUDE_CONFIG_DIR?.trim() || join4(home, ".claude")
2353
+ detect: () => existsSync6(
2354
+ process.env.CLAUDE_CONFIG_DIR?.trim() || join5(home, ".claude")
2107
2355
  )
2108
2356
  },
2109
2357
  windsurf: {
2110
2358
  displayName: "Windsurf",
2111
- globalSkillsDir: join4(home, ".codeium", "windsurf", "skills"),
2112
- detect: () => existsSync5(join4(home, ".codeium", "windsurf"))
2359
+ globalSkillsDir: join5(home, ".codeium", "windsurf", "skills"),
2360
+ detect: () => existsSync6(join5(home, ".codeium", "windsurf"))
2113
2361
  },
2114
2362
  "github-copilot": {
2115
2363
  displayName: "GitHub Copilot",
2116
- globalSkillsDir: join4(home, ".copilot", "skills"),
2117
- detect: () => existsSync5(join4(home, ".copilot"))
2364
+ globalSkillsDir: join5(home, ".copilot", "skills"),
2365
+ detect: () => existsSync6(join5(home, ".copilot"))
2118
2366
  },
2119
2367
  cline: {
2120
2368
  displayName: "Cline",
2121
- globalSkillsDir: join4(home, ".cline", "skills"),
2122
- detect: () => existsSync5(join4(home, ".cline"))
2369
+ globalSkillsDir: join5(home, ".cline", "skills"),
2370
+ detect: () => existsSync6(join5(home, ".cline"))
2123
2371
  },
2124
2372
  roo: {
2125
2373
  displayName: "Roo Code",
2126
- globalSkillsDir: join4(home, ".roo", "skills"),
2127
- detect: () => existsSync5(join4(home, ".roo"))
2374
+ globalSkillsDir: join5(home, ".roo", "skills"),
2375
+ detect: () => existsSync6(join5(home, ".roo"))
2128
2376
  },
2129
2377
  continue: {
2130
2378
  displayName: "Continue",
2131
- globalSkillsDir: join4(home, ".continue", "skills"),
2132
- detect: () => existsSync5(join4(home, ".continue")) || existsSync5(join4(process.cwd(), ".continue"))
2379
+ globalSkillsDir: join5(home, ".continue", "skills"),
2380
+ detect: () => existsSync6(join5(home, ".continue")) || existsSync6(join5(process.cwd(), ".continue"))
2133
2381
  }
2134
2382
  };
2135
2383
  function getCanonicalSkillDir(skillName) {
2136
- return join4(home, ".agents", "skills", skillName);
2384
+ return join5(home, ".agents", "skills", skillName);
2137
2385
  }
2138
2386
  function getCanonicalSkillPath() {
2139
2387
  return getCanonicalSkillDir(SKILL_NAME);
2140
2388
  }
2141
2389
  function isPathSafe(base, target) {
2142
- const normalizedBase = normalize(resolve2(base));
2143
- const normalizedTarget = normalize(resolve2(target));
2390
+ const normalizedBase = normalize(resolve3(base));
2391
+ const normalizedTarget = normalize(resolve3(target));
2144
2392
  return normalizedTarget.startsWith(normalizedBase + sep) || normalizedTarget === normalizedBase;
2145
2393
  }
2146
2394
  function createSymlinkSync(target, linkPath) {
2147
2395
  try {
2148
- const resolvedTarget = resolve2(target);
2149
- const resolvedLinkPath = resolve2(linkPath);
2396
+ const resolvedTarget = resolve3(target);
2397
+ const resolvedLinkPath = resolve3(linkPath);
2150
2398
  if (resolvedTarget === resolvedLinkPath) return true;
2151
2399
  try {
2152
2400
  const stats = lstatSync(linkPath);
2153
2401
  if (stats.isSymbolicLink()) {
2154
2402
  const existing = readlinkSync(linkPath);
2155
- const resolvedExisting = resolve2(dirname3(linkPath), existing);
2403
+ const resolvedExisting = resolve3(dirname4(linkPath), existing);
2156
2404
  if (resolvedExisting === resolvedTarget) return true;
2157
2405
  rmSync2(linkPath);
2158
2406
  } else {
@@ -2166,8 +2414,8 @@ function createSymlinkSync(target, linkPath) {
2166
2414
  }
2167
2415
  }
2168
2416
  }
2169
- const linkDir = dirname3(linkPath);
2170
- mkdirSync3(linkDir, { recursive: true });
2417
+ const linkDir = dirname4(linkPath);
2418
+ mkdirSync4(linkDir, { recursive: true });
2171
2419
  const relativePath = relative(linkDir, target);
2172
2420
  symlinkSync(
2173
2421
  relativePath,
@@ -2182,15 +2430,15 @@ function createSymlinkSync(target, linkPath) {
2182
2430
  var SKILL_NAME = "mcp-s-cli";
2183
2431
  function getSkillMdContent() {
2184
2432
  const __filename = fileURLToPath2(import.meta.url);
2185
- const __dirname = dirname3(__filename);
2433
+ const __dirname = dirname4(__filename);
2186
2434
  const candidates = [
2187
- join4(__dirname, "SKILL.md"),
2188
- join4(__dirname, "..", "SKILL.md"),
2189
- join4(__dirname, "..", "..", "SKILL.md")
2435
+ join5(__dirname, "SKILL.md"),
2436
+ join5(__dirname, "..", "SKILL.md"),
2437
+ join5(__dirname, "..", "..", "SKILL.md")
2190
2438
  ];
2191
2439
  for (const candidate of candidates) {
2192
- if (existsSync5(candidate)) {
2193
- return readFileSync3(candidate, "utf-8");
2440
+ if (existsSync6(candidate)) {
2441
+ return readFileSync4(candidate, "utf-8");
2194
2442
  }
2195
2443
  }
2196
2444
  throw new Error(
@@ -2200,12 +2448,12 @@ function getSkillMdContent() {
2200
2448
  function installSkill() {
2201
2449
  const content = getSkillMdContent();
2202
2450
  const canonicalDir = getCanonicalSkillDir(SKILL_NAME);
2203
- if (!isPathSafe(join4(home, ".agents", "skills"), canonicalDir)) {
2451
+ if (!isPathSafe(join5(home, ".agents", "skills"), canonicalDir)) {
2204
2452
  throw new Error("Invalid skill name: potential path traversal detected");
2205
2453
  }
2206
2454
  rmSync2(canonicalDir, { recursive: true, force: true });
2207
- mkdirSync3(canonicalDir, { recursive: true });
2208
- writeFileSync3(join4(canonicalDir, "SKILL.md"), content, "utf-8");
2455
+ mkdirSync4(canonicalDir, { recursive: true });
2456
+ writeFileSync4(join5(canonicalDir, "SKILL.md"), content, "utf-8");
2209
2457
  const result = {
2210
2458
  installed: [],
2211
2459
  skipped: [],
@@ -2217,7 +2465,7 @@ function installSkill() {
2217
2465
  result.skipped.push(agentName);
2218
2466
  continue;
2219
2467
  }
2220
- const agentSkillDir = join4(agent.globalSkillsDir, SKILL_NAME);
2468
+ const agentSkillDir = join5(agent.globalSkillsDir, SKILL_NAME);
2221
2469
  if (!isPathSafe(agent.globalSkillsDir, agentSkillDir)) {
2222
2470
  result.failed.push({
2223
2471
  agent: agentName,
@@ -2229,8 +2477,8 @@ function installSkill() {
2229
2477
  const symlinkOk = createSymlinkSync(canonicalDir, agentSkillDir);
2230
2478
  if (!symlinkOk) {
2231
2479
  rmSync2(agentSkillDir, { recursive: true, force: true });
2232
- mkdirSync3(agentSkillDir, { recursive: true });
2233
- writeFileSync3(join4(agentSkillDir, "SKILL.md"), content, "utf-8");
2480
+ mkdirSync4(agentSkillDir, { recursive: true });
2481
+ writeFileSync4(join5(agentSkillDir, "SKILL.md"), content, "utf-8");
2234
2482
  }
2235
2483
  result.installed.push(agentName);
2236
2484
  } catch (err) {
@@ -2250,8 +2498,8 @@ function clearSkill() {
2250
2498
  canonicalRemoved: false
2251
2499
  };
2252
2500
  for (const [agentName, agent] of Object.entries(AGENTS)) {
2253
- const agentSkillDir = join4(agent.globalSkillsDir, SKILL_NAME);
2254
- if (!existsSync5(agentSkillDir)) {
2501
+ const agentSkillDir = join5(agent.globalSkillsDir, SKILL_NAME);
2502
+ if (!existsSync6(agentSkillDir)) {
2255
2503
  result.skipped.push(agentName);
2256
2504
  continue;
2257
2505
  }
@@ -2266,7 +2514,7 @@ function clearSkill() {
2266
2514
  }
2267
2515
  }
2268
2516
  const canonicalDir = getCanonicalSkillDir(SKILL_NAME);
2269
- if (existsSync5(canonicalDir)) {
2517
+ if (existsSync6(canonicalDir)) {
2270
2518
  try {
2271
2519
  rmSync2(canonicalDir, { recursive: true, force: true });
2272
2520
  result.canonicalRemoved = true;
@@ -2277,12 +2525,12 @@ function clearSkill() {
2277
2525
  }
2278
2526
  function getSkillInstallInfo() {
2279
2527
  return Object.entries(AGENTS).filter(([, agent]) => agent.detect()).map(([agentName, agent]) => {
2280
- const agentSkillDir = join4(agent.globalSkillsDir, SKILL_NAME);
2528
+ const agentSkillDir = join5(agent.globalSkillsDir, SKILL_NAME);
2281
2529
  return {
2282
2530
  agent: agentName,
2283
2531
  displayName: agent.displayName,
2284
2532
  path: agentSkillDir,
2285
- installed: existsSync5(join4(agentSkillDir, "SKILL.md"))
2533
+ installed: existsSync6(join5(agentSkillDir, "SKILL.md"))
2286
2534
  };
2287
2535
  });
2288
2536
  }
@@ -2352,7 +2600,7 @@ function cancel(msg) {
2352
2600
  `);
2353
2601
  }
2354
2602
  function readLine(prompt) {
2355
- return new Promise((resolve4) => {
2603
+ return new Promise((resolve5) => {
2356
2604
  const rl = readline.createInterface({
2357
2605
  input: process.stdin,
2358
2606
  output: process.stdout,
@@ -2361,16 +2609,16 @@ function readLine(prompt) {
2361
2609
  process.stdout.write(prompt);
2362
2610
  rl.once("line", (line) => {
2363
2611
  rl.close();
2364
- resolve4(line);
2612
+ resolve5(line);
2365
2613
  });
2366
2614
  rl.once("SIGINT", () => {
2367
2615
  rl.close();
2368
2616
  process.stdout.write("\n");
2369
- resolve4(CANCEL);
2617
+ resolve5(CANCEL);
2370
2618
  });
2371
2619
  process.stdin.once("end", () => {
2372
2620
  rl.close();
2373
- resolve4(CANCEL);
2621
+ resolve5(CANCEL);
2374
2622
  });
2375
2623
  });
2376
2624
  }
@@ -2403,14 +2651,14 @@ async function select(opts) {
2403
2651
  `);
2404
2652
  });
2405
2653
  };
2406
- return new Promise((resolve4) => {
2654
+ return new Promise((resolve5) => {
2407
2655
  readline.emitKeypressEvents(process.stdin);
2408
2656
  process.stdin.setRawMode(true);
2409
2657
  process.stdin.resume();
2410
2658
  const onKey = (_, key) => {
2411
2659
  if (key.ctrl && key.name === "c") {
2412
2660
  cleanup();
2413
- resolve4(CANCEL);
2661
+ resolve5(CANCEL);
2414
2662
  return;
2415
2663
  }
2416
2664
  if (key.name === "up") {
@@ -2423,7 +2671,7 @@ async function select(opts) {
2423
2671
  }
2424
2672
  if (key.name === "return") {
2425
2673
  cleanup();
2426
- resolve4(options[idx].value);
2674
+ resolve5(options[idx].value);
2427
2675
  }
2428
2676
  };
2429
2677
  const cleanup = () => {
@@ -2474,15 +2722,24 @@ ${BAR} ${c(A.cyan, "\u25C6")} `
2474
2722
  }
2475
2723
  }
2476
2724
  var p = { intro, outro, cancel, isCancel, select, text };
2477
- var GLOBAL_CONFIG_PATH2 = join5(
2478
- homedir4(),
2725
+ var GLOBAL_CONFIG_PATH2 = join6(
2726
+ homedir5(),
2479
2727
  ".config",
2480
2728
  "mcp-s-cli",
2481
2729
  "config.json"
2482
2730
  );
2731
+ function readExistingConfig() {
2732
+ try {
2733
+ if (existsSync7(GLOBAL_CONFIG_PATH2)) {
2734
+ return JSON.parse(readFileSync5(GLOBAL_CONFIG_PATH2, "utf-8"));
2735
+ }
2736
+ } catch {
2737
+ }
2738
+ return {};
2739
+ }
2483
2740
  function writeConfig(config) {
2484
- mkdirSync4(dirname4(GLOBAL_CONFIG_PATH2), { recursive: true });
2485
- writeFileSync4(
2741
+ mkdirSync5(dirname5(GLOBAL_CONFIG_PATH2), { recursive: true });
2742
+ writeFileSync5(
2486
2743
  GLOBAL_CONFIG_PATH2,
2487
2744
  `${JSON.stringify(config, null, 2)}
2488
2745
  `,
@@ -2491,7 +2748,9 @@ function writeConfig(config) {
2491
2748
  }
2492
2749
  async function initCommand(options) {
2493
2750
  const { baseUrl, org, mcp, toolkit, userAccessKey, token } = options;
2751
+ const existing = readExistingConfig();
2494
2752
  const config = {};
2753
+ if (existing.settings) config.settings = existing.settings;
2495
2754
  if (org) {
2496
2755
  config.org = org;
2497
2756
  } else if (baseUrl) {
@@ -2601,10 +2860,10 @@ async function initInteractive() {
2601
2860
  }
2602
2861
 
2603
2862
  // src/commands/kill-daemon.ts
2604
- import { existsSync as existsSync7, readdirSync as readdirSync2 } from "fs";
2863
+ import { existsSync as existsSync8, readdirSync as readdirSync2 } from "fs";
2605
2864
  function killDaemonCommand() {
2606
2865
  const socketDir = getSocketDir();
2607
- if (!existsSync7(socketDir)) {
2866
+ if (!existsSync8(socketDir)) {
2608
2867
  console.log("No daemons running.");
2609
2868
  return;
2610
2869
  }
@@ -2654,14 +2913,14 @@ async function listCommand(options) {
2654
2913
  console.error(error.message);
2655
2914
  process.exit(1 /* CLIENT_ERROR */);
2656
2915
  }
2657
- const { serverConfig } = loaded;
2916
+ const { serverConfig, settings } = loaded;
2658
2917
  const serverLabel = "server";
2659
2918
  let connection = null;
2660
2919
  let tools = [];
2661
2920
  let instructions;
2662
2921
  let errorMsg;
2663
2922
  try {
2664
- connection = await getConnection(serverLabel, serverConfig);
2923
+ connection = await getConnection(serverLabel, serverConfig, settings);
2665
2924
  tools = await connection.listTools();
2666
2925
  instructions = await connection.getInstructions();
2667
2926
  debug(`${serverLabel}: loaded ${tools.length} tools`);
@@ -2703,18 +2962,18 @@ async function logoutCommand(options) {
2703
2962
  }
2704
2963
 
2705
2964
  // src/commands/whoami.ts
2706
- import { existsSync as existsSync8, readFileSync as readFileSync4, statSync } from "fs";
2707
- import { homedir as homedir5 } from "os";
2708
- import { join as join6, resolve as resolve3 } from "path";
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";
2709
2968
  function getResolvedConfigPath(explicitPath) {
2710
2969
  if (explicitPath) {
2711
- return resolve3(explicitPath);
2970
+ return resolve4(explicitPath);
2712
2971
  }
2713
- if (process.env.MCP_CONFIG_PATH) {
2714
- return resolve3(process.env.MCP_CONFIG_PATH);
2972
+ if (process.env.MCP_S_CLI_CONFIG_PATH) {
2973
+ return resolve4(process.env.MCP_S_CLI_CONFIG_PATH);
2715
2974
  }
2716
- const defaultPath = join6(homedir5(), ".config", "mcp-s-cli", "config.json");
2717
- if (existsSync8(defaultPath)) {
2975
+ const defaultPath = join7(homedir6(), ".config", "mcp-s-cli", "config.json");
2976
+ if (existsSync9(defaultPath)) {
2718
2977
  return defaultPath;
2719
2978
  }
2720
2979
  return null;
@@ -2742,15 +3001,15 @@ async function whoamiCommand(options) {
2742
3001
  console.log(` Error loading config: ${err.message}`);
2743
3002
  return;
2744
3003
  }
2745
- const authFilePath = join6(homedir5(), ".config", "mcp-s-cli", "auth.json");
3004
+ const authFilePath = join7(homedir6(), ".config", "mcp-s-cli", "auth.json");
2746
3005
  console.log("");
2747
3006
  console.log("OAuth Tokens");
2748
3007
  console.log(` Path: ${authFilePath}`);
2749
- if (!existsSync8(authFilePath)) {
3008
+ if (!existsSync9(authFilePath)) {
2750
3009
  console.log(" (no tokens stored)");
2751
3010
  } else {
2752
3011
  try {
2753
- const raw2 = readFileSync4(authFilePath, "utf-8");
3012
+ const raw2 = readFileSync6(authFilePath, "utf-8");
2754
3013
  const data = JSON.parse(raw2);
2755
3014
  const tokenEntries = Object.entries(data.tokens ?? {});
2756
3015
  if (tokenEntries.length === 0) {
@@ -2760,9 +3019,9 @@ async function whoamiCommand(options) {
2760
3019
  console.log(" (could not read auth file)");
2761
3020
  }
2762
3021
  }
2763
- const historyPath = join6(homedir5(), ".config", "mcp-s-cli", "history.jsonl");
2764
- if (existsSync8(historyPath)) {
2765
- const lines = readFileSync4(historyPath, "utf-8").split("\n").filter(Boolean);
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);
2766
3025
  const size = statSync(historyPath).size;
2767
3026
  const sizeStr = size >= 1024 * 1024 ? `${(size / (1024 * 1024)).toFixed(1)} MB` : size >= 1024 ? `${(size / 1024).toFixed(1)} KB` : `${size} B`;
2768
3027
  console.log("");
@@ -2824,6 +3083,10 @@ function parseArgs2(args) {
2824
3083
  switch (arg) {
2825
3084
  case "-h":
2826
3085
  case "--help":
3086
+ if (positional[0] === "config") {
3087
+ positional.push(arg);
3088
+ break;
3089
+ }
2827
3090
  result.command = "help";
2828
3091
  return result;
2829
3092
  case "-v":
@@ -2987,6 +3250,38 @@ function parseArgs2(args) {
2987
3250
  result.command = "kill-daemon";
2988
3251
  return result;
2989
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
+ }
2990
3285
  if (firstArg === "init") {
2991
3286
  result.command = "init";
2992
3287
  if (result.baseUrl && result.org) {
@@ -3026,10 +3321,16 @@ Usage:
3026
3321
  mcp-s-cli whoami Show config location and auth state
3027
3322
  mcp-s-cli login Log in to the configured server via OAuth
3028
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
3029
3328
  mcp-s-cli clear Clear server config (resets config.json to {})
3030
3329
  mcp-s-cli clear-auth Clear all stored auth data (sets auth.json to {})
3031
3330
  mcp-s-cli clear-history Delete the history file (~/.config/mcp-s-cli/history.jsonl)
3032
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
3033
3334
 
3034
3335
  Options:
3035
3336
  -h, --help Show this help message
@@ -3061,30 +3362,33 @@ Examples:
3061
3362
  mcp-s-cli whoami # Show config and auth state
3062
3363
  mcp-s-cli login # Authenticate via OAuth
3063
3364
  mcp-s-cli logout # Remove stored OAuth tokens
3365
+ mcp-s-cli config set settings.timeout 30 # Set request timeout to 30s
3064
3366
  mcp-s-cli clear # Reset server config
3065
3367
  mcp-s-cli clear-auth # Clear stored auth data
3066
3368
  mcp-s-cli clear-skill # Remove installed skill from all agents
3067
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
3068
3372
 
3069
3373
  Environment Variables:
3070
- MCP_DAEMON=0 Disable connection caching (daemon mode, on by default)
3071
- MCP_DAEMON_TIMEOUT=N Set daemon idle timeout in seconds (default: 300)
3374
+ MCP_S_CLI_DEBUG=1 Enable debug output
3375
+ MCP_S_CLI_STRICT_ENV=false Warn (instead of error) on missing \${VAR} in config
3072
3376
 
3073
3377
  Config File:
3074
3378
  The CLI looks for config.json in:
3075
- 1. Path specified by MCP_CONFIG_PATH or -c/--config
3379
+ 1. Path specified by MCP_S_CLI_CONFIG_PATH or -c/--config
3076
3380
  2. ~/.config/mcp-s-cli/config.json
3077
3381
 
3078
3382
  'mcp-s-cli init' always writes to ~/.config/mcp-s-cli/config.json
3079
3383
  `);
3080
3384
  }
3081
- function appendHistory(argv) {
3082
- if (process.env.MCP_CLI_HISTORY !== "1") return;
3385
+ function appendHistory(argv, history) {
3386
+ if (!history) return;
3083
3387
  try {
3084
- const dir = join7(homedir6(), ".config", "mcp-s-cli");
3085
- mkdirSync5(dir, { recursive: true });
3388
+ const dir = join8(homedir7(), ".config", "mcp-s-cli");
3389
+ mkdirSync6(dir, { recursive: true });
3086
3390
  const entry = JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), args: argv });
3087
- appendFileSync(join7(dir, "history.jsonl"), `${entry}
3391
+ appendFileSync(join8(dir, "history.jsonl"), `${entry}
3088
3392
  `, "utf8");
3089
3393
  } catch (err) {
3090
3394
  const msg = err instanceof Error ? err.message : String(err);
@@ -3095,8 +3399,29 @@ function appendHistory(argv) {
3095
3399
  }
3096
3400
  }
3097
3401
  async function main() {
3098
- appendHistory(process.argv.slice(2));
3099
- const args = parseArgs2(process.argv.slice(2));
3402
+ const argv = process.argv.slice(2);
3403
+ const args = parseArgs2(argv);
3404
+ let settings;
3405
+ let enabled;
3406
+ try {
3407
+ const loaded = await loadConfig(args.configPath);
3408
+ settings = loaded.settings;
3409
+ enabled = loaded.raw.enabled;
3410
+ } catch {
3411
+ }
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
+ }
3100
3425
  switch (args.command) {
3101
3426
  case "help":
3102
3427
  printHelp();
@@ -3131,6 +3456,14 @@ async function main() {
3131
3456
  configPath: args.configPath
3132
3457
  });
3133
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;
3134
3467
  case "init":
3135
3468
  if (args.baseUrl || args.org) {
3136
3469
  await initCommand({
@@ -3184,6 +3517,22 @@ async function main() {
3184
3517
  }
3185
3518
  break;
3186
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;
3187
3536
  }
3188
3537
  }
3189
3538
  process.on("SIGINT", () => {