@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/README.md CHANGED
@@ -263,7 +263,7 @@ mcp-s-cli clear-auth
263
263
 
264
264
  ## Config File Format
265
265
 
266
- The config file lives at `~/.config/mcp-s-cli/config.json` (or a custom path via `-c`/`MCP_CONFIG_PATH`).
266
+ The config file lives at `~/.config/mcp-s-cli/config.json` (or a custom path via `-c`/`MCP_S_CLI_CONFIG_PATH`).
267
267
 
268
268
  ```json
269
269
  {
@@ -296,10 +296,36 @@ For stdio mode (using a user access key):
296
296
  | `userAccessKey` | User access key — triggers stdio mode via `npx @mcp-s/mcp` |
297
297
  | `allowedTools` | Glob patterns of tools to allow (optional) |
298
298
  | `disabledTools` | Glob patterns of tools to exclude (optional) |
299
+ | `settings` | Per-agent behavioral settings (see below) |
299
300
 
300
301
  Environment variable substitution is supported: `"token": "${MY_TOKEN}"`.
301
302
 
302
- ---
303
+ ### `settings` block
304
+
305
+ The optional `settings` block lets you tune behavioral settings per config file — useful when different AI agents use different configs and need different tuning:
306
+
307
+ ```json
308
+ {
309
+ "org": "my-org",
310
+ "settings": {
311
+ "timeout": 60,
312
+ "maxRetries": 1,
313
+ "retryDelay": 500,
314
+ "daemon": false,
315
+ "daemonTimeout": 120,
316
+ "history": true
317
+ }
318
+ }
319
+ ```
320
+
321
+ | Field | Description | Default |
322
+ | --------------- | --------------------------------------------------- | ------- |
323
+ | `timeout` | Request timeout (seconds) | `1800` |
324
+ | `maxRetries` | Retry attempts for transient errors (`0` = disable) | `3` |
325
+ | `retryDelay` | Base retry delay (milliseconds) | `1000` |
326
+ | `daemon` | Enable connection caching (daemon mode) | `true` |
327
+ | `daemonTimeout` | Idle timeout for cached connections (seconds) | `300` |
328
+ | `history` | Append each invocation to `history.jsonl` | `false` |
303
329
 
304
330
  ## Working with Complex JSON Arguments
305
331
 
@@ -355,15 +381,11 @@ fi
355
381
 
356
382
  ### Environment Variables
357
383
 
358
- | Variable | Description | Default |
359
- | -------------------- | ------------------------------------------------- | ------- |
360
- | `MCP_DEBUG` | Enable debug output | `false` |
361
- | `MCP_TIMEOUT` | Request timeout (seconds) | `1800` |
362
- | `MCP_MAX_RETRIES` | Retry attempts for transient errors (0 = disable) | `3` |
363
- | `MCP_RETRY_DELAY` | Base retry delay (milliseconds) | `1000` |
364
- | `MCP_STRICT_ENV` | Error on missing `${VAR}` in config | `true` |
365
- | `MCP_DAEMON` | Enable connection caching (daemon mode) | `true` |
366
- | `MCP_DAEMON_TIMEOUT` | Idle timeout for cached connections (seconds) | `300` |
384
+ | Variable | Description | Default |
385
+ | ----------------------- | ----------------------------------------------------- | ------- |
386
+ | `MCP_S_CLI_DEBUG` | Enable debug output | |
387
+ | `MCP_S_CLI_STRICT_ENV` | Error on missing `${VAR}` in config (`false` to warn) | `true` |
388
+ | `MCP_S_CLI_CONFIG_PATH` | Override config file path | |
367
389
 
368
390
  ---
369
391
 
@@ -431,14 +453,15 @@ Each CLI invocation connects to the single configured server.
431
453
 
432
454
  ### Connection Pooling (Daemon)
433
455
 
434
- Daemon mode is enabled by default. The daemon keeps the MCP server connection open for `MCP_DAEMON_TIMEOUT` seconds (default: 300s) after the last request, avoiding repeated startup latency on subsequent calls.
456
+ Daemon mode is enabled by default. The daemon keeps the server connection open for `settings.daemonTimeout` seconds (default: 300s) after the last request, avoiding repeated startup latency on subsequent calls.
435
457
 
436
458
  ```bash
437
459
  mcp-s-cli call some_tool '{}' # Uses cached connection (default)
438
- MCP_DAEMON=0 mcp-s-cli info # Disable daemon, use a fresh connection
439
- MCP_DEBUG=1 mcp-s-cli info # See connection debug output
460
+ MCP_S_CLI_DEBUG=1 mcp-s-cli info # See connection debug output
440
461
  ```
441
462
 
463
+ To disable daemon for a specific config, add `"settings": { "daemon": false }` to `config.json`.
464
+
442
465
  With daemon disabled, each CLI call opens a fresh connection and closes it when done.
443
466
 
444
467
  ### Error Handling and Retry
package/SKILL.md CHANGED
@@ -9,21 +9,44 @@ Access a single MCP server through the command line. MCP enables interaction wit
9
9
 
10
10
  ## Commands
11
11
 
12
- | Command | Output |
13
- | -------------------------------- | -------------------------------------------- |
14
- | `mcp-s-cli` | List all available tools |
15
- | `mcp-s-cli info` | Show server details |
16
- | `mcp-s-cli info <tool>` | Get tool JSON schema |
17
- | `mcp-s-cli grep "<pattern>"` | Search tools by name |
18
- | `mcp-s-cli call <tool>` | Call tool (reads JSON from stdin if no args) |
19
- | `mcp-s-cli call <tool> '<json>'` | Call tool with arguments |
12
+ | Command | Output |
13
+ | -------------------------------- | --------------------------------------------------- |
14
+ | `mcp-s-cli` | List all available tools |
15
+ | `mcp-s-cli info <tool>` | Get tool JSON schema |
16
+ | `mcp-s-cli grep "<pattern>"` | Search tools by name (names only, not descriptions) |
17
+ | `mcp-s-cli call <tool>` | Call tool (reads JSON from stdin if no args) |
18
+ | `mcp-s-cli call <tool> '<json>'` | Call tool with arguments |
20
19
 
21
20
  ## Workflow
22
21
 
23
- 1. **Discover**: `mcp-s-cli` → see all available tools
22
+ 1. **Discover**: `mcp-s-cli grep "<pattern>"` → find tools by keyword; fall back to `mcp-s-cli` only when you need a full inventory
24
23
  2. **Inspect**: `mcp-s-cli info <tool>` → get full JSON schema
25
24
  3. **Execute**: `mcp-s-cli call <tool> '<json>'` → run with arguments
26
25
 
26
+ ## Cost-Aware Usage
27
+
28
+ Every command consumes tokens. Pick the cheapest operation that gets the job done.
29
+
30
+ ### Discovery: start narrow
31
+
32
+ - **`grep "<pattern>"`** — cheapest. Use when you can guess a keyword from the user's request (e.g., "read a github file" → `grep "file"` or `grep "content"`).
33
+ - **`mcp-s-cli`** (names only) — moderate. Use when you need a full inventory (e.g., "what tools do I have?" or "analyze logs from all tools").
34
+ - **`mcp-s-cli -d`** (names + descriptions) — most expensive. Use only when names alone aren't enough to pick the right tool.
35
+
36
+ **Rule: if you can guess a keyword, grep first.**
37
+
38
+ ### Execution: keep responses small
39
+
40
+ - Use **filter/query parameters** to request only what's needed (e.g., Jira JQL, GitHub search queries, field selectors) instead of fetching everything.
41
+ - **Pipe output** through shell tools (`head`, `grep`, `jq`) to trim large responses before they enter context.
42
+ - Never fetch a full list when the user only needs a few fields (names, links, IDs).
43
+
44
+ ### Decision heuristic
45
+
46
+ - User mentions a specific tool or domain keyword → `grep` for it.
47
+ - User asks a broad question across all tools → list tools first, then act.
48
+ - Tool may return large payloads → use filter params + pipe to trim output.
49
+
27
50
  ## Examples
28
51
 
29
52
  ```bash
@@ -33,17 +56,14 @@ mcp-s-cli
33
56
  # With descriptions
34
57
  mcp-s-cli -d
35
58
 
36
- # Show server details
37
- mcp-s-cli info
38
-
39
59
  # Get tool schema
40
- mcp-s-cli info read_file
60
+ mcp-s-cli info my-tool
41
61
 
42
62
  # Call tool
43
- mcp-s-cli call read_file '{"path": "./README.md"}'
63
+ mcp-s-cli call my-tool '{"arg-name": "arg-value"}'
44
64
 
45
65
  # Pipe from stdin (no '-' needed!)
46
- cat args.json | mcp-s-cli call read_file
66
+ cat args.json | mcp-s-cli call my-tool
47
67
 
48
68
  # Search for tools
49
69
  mcp-s-cli grep "*file*"
@@ -68,24 +88,7 @@ mcp-s-cli call list_directory '{"path": "./src"}' \
68
88
  mcp-s-cli call get_file_contents '{"owner": "x", "repo": "y", "path": "z"}' > output.txt
69
89
  ```
70
90
 
71
- **Note:** `call` outputs raw text content directly (no jq needed for text extraction)
72
-
73
- ## Options
74
-
75
- | Flag | Purpose |
76
- | ----------- | -------------------- |
77
- | `-d` | Include descriptions |
78
- | `-c <path>` | Specify config file |
79
-
80
- ## Config
81
-
82
- The config file (`~/.config/mcp-s-cli/config.json`) uses a flat structure:
83
-
84
- ```json
85
- { "org": "myorg" }
86
- ```
87
-
88
- This derives the server URL as `https://myorg.mcp-s.com/mcp`. Alternatively, use `baseUrl` for a custom URL, or `userAccessKey` for stdio mode.
91
+ **Note:** `call` extracts text content from MCP responses and outputs it directly (no jq needed). Falls back to pretty-printed JSON when the response has no text parts.
89
92
 
90
93
  ## Common Errors
91
94
 
@@ -95,9 +98,14 @@ This derives the server URL as `https://myorg.mcp-s.com/mcp`. Alternatively, use
95
98
  | `mcp-s-cli call` | MISSING_ARGUMENT | Add tool name |
96
99
  | `mcp-s-cli call tool {bad}` | INVALID_JSON | Use valid JSON with quoted string keys |
97
100
 
101
+ ## Debugging
102
+
103
+ Set `MCP_S_CLI_DEBUG=1` to enable verbose debug output to stderr.
104
+
98
105
  ## Exit Codes
99
106
 
100
107
  - `0`: Success
101
108
  - `1`: Client error (bad args, missing config)
102
109
  - `2`: Server error (tool failed)
103
- - `3`: Network error
110
+ - `3`: Network error (connection failed)
111
+ - `4`: Auth error (authentication required)
package/dist/SKILL.md CHANGED
@@ -9,21 +9,44 @@ Access a single MCP server through the command line. MCP enables interaction wit
9
9
 
10
10
  ## Commands
11
11
 
12
- | Command | Output |
13
- | -------------------------------- | -------------------------------------------- |
14
- | `mcp-s-cli` | List all available tools |
15
- | `mcp-s-cli info` | Show server details |
16
- | `mcp-s-cli info <tool>` | Get tool JSON schema |
17
- | `mcp-s-cli grep "<pattern>"` | Search tools by name |
18
- | `mcp-s-cli call <tool>` | Call tool (reads JSON from stdin if no args) |
19
- | `mcp-s-cli call <tool> '<json>'` | Call tool with arguments |
12
+ | Command | Output |
13
+ | -------------------------------- | --------------------------------------------------- |
14
+ | `mcp-s-cli` | List all available tools |
15
+ | `mcp-s-cli info <tool>` | Get tool JSON schema |
16
+ | `mcp-s-cli grep "<pattern>"` | Search tools by name (names only, not descriptions) |
17
+ | `mcp-s-cli call <tool>` | Call tool (reads JSON from stdin if no args) |
18
+ | `mcp-s-cli call <tool> '<json>'` | Call tool with arguments |
20
19
 
21
20
  ## Workflow
22
21
 
23
- 1. **Discover**: `mcp-s-cli` → see all available tools
22
+ 1. **Discover**: `mcp-s-cli grep "<pattern>"` → find tools by keyword; fall back to `mcp-s-cli` only when you need a full inventory
24
23
  2. **Inspect**: `mcp-s-cli info <tool>` → get full JSON schema
25
24
  3. **Execute**: `mcp-s-cli call <tool> '<json>'` → run with arguments
26
25
 
26
+ ## Cost-Aware Usage
27
+
28
+ Every command consumes tokens. Pick the cheapest operation that gets the job done.
29
+
30
+ ### Discovery: start narrow
31
+
32
+ - **`grep "<pattern>"`** — cheapest. Use when you can guess a keyword from the user's request (e.g., "read a github file" → `grep "file"` or `grep "content"`).
33
+ - **`mcp-s-cli`** (names only) — moderate. Use when you need a full inventory (e.g., "what tools do I have?" or "analyze logs from all tools").
34
+ - **`mcp-s-cli -d`** (names + descriptions) — most expensive. Use only when names alone aren't enough to pick the right tool.
35
+
36
+ **Rule: if you can guess a keyword, grep first.**
37
+
38
+ ### Execution: keep responses small
39
+
40
+ - Use **filter/query parameters** to request only what's needed (e.g., Jira JQL, GitHub search queries, field selectors) instead of fetching everything.
41
+ - **Pipe output** through shell tools (`head`, `grep`, `jq`) to trim large responses before they enter context.
42
+ - Never fetch a full list when the user only needs a few fields (names, links, IDs).
43
+
44
+ ### Decision heuristic
45
+
46
+ - User mentions a specific tool or domain keyword → `grep` for it.
47
+ - User asks a broad question across all tools → list tools first, then act.
48
+ - Tool may return large payloads → use filter params + pipe to trim output.
49
+
27
50
  ## Examples
28
51
 
29
52
  ```bash
@@ -33,17 +56,14 @@ mcp-s-cli
33
56
  # With descriptions
34
57
  mcp-s-cli -d
35
58
 
36
- # Show server details
37
- mcp-s-cli info
38
-
39
59
  # Get tool schema
40
- mcp-s-cli info read_file
60
+ mcp-s-cli info my-tool
41
61
 
42
62
  # Call tool
43
- mcp-s-cli call read_file '{"path": "./README.md"}'
63
+ mcp-s-cli call my-tool '{"arg-name": "arg-value"}'
44
64
 
45
65
  # Pipe from stdin (no '-' needed!)
46
- cat args.json | mcp-s-cli call read_file
66
+ cat args.json | mcp-s-cli call my-tool
47
67
 
48
68
  # Search for tools
49
69
  mcp-s-cli grep "*file*"
@@ -68,24 +88,7 @@ mcp-s-cli call list_directory '{"path": "./src"}' \
68
88
  mcp-s-cli call get_file_contents '{"owner": "x", "repo": "y", "path": "z"}' > output.txt
69
89
  ```
70
90
 
71
- **Note:** `call` outputs raw text content directly (no jq needed for text extraction)
72
-
73
- ## Options
74
-
75
- | Flag | Purpose |
76
- | ----------- | -------------------- |
77
- | `-d` | Include descriptions |
78
- | `-c <path>` | Specify config file |
79
-
80
- ## Config
81
-
82
- The config file (`~/.config/mcp-s-cli/config.json`) uses a flat structure:
83
-
84
- ```json
85
- { "org": "myorg" }
86
- ```
87
-
88
- This derives the server URL as `https://myorg.mcp-s.com/mcp`. Alternatively, use `baseUrl` for a custom URL, or `userAccessKey` for stdio mode.
91
+ **Note:** `call` extracts text content from MCP responses and outputs it directly (no jq needed). Falls back to pretty-printed JSON when the response has no text parts.
89
92
 
90
93
  ## Common Errors
91
94
 
@@ -95,9 +98,14 @@ This derives the server URL as `https://myorg.mcp-s.com/mcp`. Alternatively, use
95
98
  | `mcp-s-cli call` | MISSING_ARGUMENT | Add tool name |
96
99
  | `mcp-s-cli call tool {bad}` | INVALID_JSON | Use valid JSON with quoted string keys |
97
100
 
101
+ ## Debugging
102
+
103
+ Set `MCP_S_CLI_DEBUG=1` to enable verbose debug output to stderr.
104
+
98
105
  ## Exit Codes
99
106
 
100
107
  - `0`: Success
101
108
  - `1`: Client error (bad args, missing config)
102
109
  - `2`: Server error (tool failed)
103
- - `3`: Network error
110
+ - `3`: Network error (connection failed)
111
+ - `4`: Auth error (authentication required)
package/dist/daemon.js CHANGED
@@ -64,7 +64,7 @@ async function saveAuthData() {
64
64
  mode: 384
65
65
  });
66
66
  } catch (err) {
67
- if (process.env.MCP_DEBUG) {
67
+ if (process.env.MCP_S_CLI_DEBUG) {
68
68
  console.error(
69
69
  `[mcp-s-cli] Failed to save auth data: ${err.message}`
70
70
  );
@@ -358,48 +358,32 @@ var DEFAULT_MAX_RETRIES = 3;
358
358
  var DEFAULT_RETRY_DELAY_MS = 1e3;
359
359
  var DEFAULT_DAEMON_TIMEOUT_SECONDS = 300;
360
360
  function debug(message) {
361
- if (process.env.MCP_DEBUG) {
361
+ if (process.env.MCP_S_CLI_DEBUG) {
362
362
  console.error(`[mcp-s-cli] ${message}`);
363
363
  }
364
364
  }
365
- function getTimeoutMs() {
366
- const envTimeout = process.env.MCP_TIMEOUT;
367
- if (envTimeout) {
368
- const seconds = Number.parseInt(envTimeout, 10);
369
- if (!Number.isNaN(seconds) && seconds > 0) {
370
- return seconds * 1e3;
371
- }
365
+ function getTimeoutMs(settings) {
366
+ if (settings?.timeout != null && settings.timeout > 0) {
367
+ return settings.timeout * 1e3;
372
368
  }
373
369
  return DEFAULT_TIMEOUT_MS;
374
370
  }
375
- function getMaxRetries() {
376
- const envRetries = process.env.MCP_MAX_RETRIES;
377
- if (envRetries) {
378
- const retries = Number.parseInt(envRetries, 10);
379
- if (!Number.isNaN(retries) && retries >= 0) {
380
- return retries;
381
- }
371
+ function getMaxRetries(settings) {
372
+ if (settings?.maxRetries != null && settings.maxRetries >= 0) {
373
+ return settings.maxRetries;
382
374
  }
383
375
  return DEFAULT_MAX_RETRIES;
384
376
  }
385
- function getRetryDelayMs() {
386
- const envDelay = process.env.MCP_RETRY_DELAY;
387
- if (envDelay) {
388
- const delay = Number.parseInt(envDelay, 10);
389
- if (!Number.isNaN(delay) && delay > 0) {
390
- return delay;
391
- }
377
+ function getRetryDelayMs(settings) {
378
+ if (settings?.retryDelay != null && settings.retryDelay > 0) {
379
+ return settings.retryDelay;
392
380
  }
393
381
  return DEFAULT_RETRY_DELAY_MS;
394
382
  }
395
383
  var DAEMON_SERVER_NAME = "mcp-s-cli";
396
- function getDaemonTimeoutMs() {
397
- const envTimeout = process.env.MCP_DAEMON_TIMEOUT;
398
- if (envTimeout) {
399
- const seconds = Number.parseInt(envTimeout, 10);
400
- if (!Number.isNaN(seconds) && seconds > 0) {
401
- return seconds * 1e3;
402
- }
384
+ function getDaemonTimeoutMs(settings) {
385
+ if (settings?.daemonTimeout != null && settings.daemonTimeout > 0) {
386
+ return settings.daemonTimeout * 1e3;
403
387
  }
404
388
  return DEFAULT_DAEMON_TIMEOUT_SECONDS * 1e3;
405
389
  }
@@ -430,13 +414,13 @@ import { join as join2 } from "path";
430
414
  import { fileURLToPath } from "url";
431
415
 
432
416
  // src/version.ts
433
- var VERSION = "0.0.9";
417
+ var VERSION = "0.0.11";
434
418
 
435
419
  // src/client.ts
436
- function getRetryConfig() {
437
- const totalBudgetMs = getTimeoutMs();
438
- const maxRetries = getMaxRetries();
439
- const baseDelayMs = getRetryDelayMs();
420
+ function getRetryConfig(settings) {
421
+ const totalBudgetMs = getTimeoutMs(settings);
422
+ const maxRetries = getMaxRetries(settings);
423
+ const baseDelayMs = getRetryDelayMs(settings);
440
424
  const retryBudgetMs = Math.max(0, totalBudgetMs - 5e3);
441
425
  return {
442
426
  maxRetries,
@@ -567,7 +551,7 @@ ${stderrOutput}`;
567
551
  }, `connect to ${serverName}`);
568
552
  }
569
553
  async function connectToHttpServer(serverName, config) {
570
- const oauthEnabled = process.env.MCP_NO_OAUTH !== "1";
554
+ const oauthEnabled = process.env.MCP_S_CLI_NO_OAUTH !== "1";
571
555
  return withRetry(async () => {
572
556
  const configuredAuth = config.headers?.Authorization || config.headers?.authorization;
573
557
  let headers = { ...config.headers };
@@ -717,18 +701,22 @@ async function listTools(client) {
717
701
  }));
718
702
  }, "list tools");
719
703
  }
720
- async function callTool(client, toolName, args) {
721
- return withRetry(async () => {
722
- const result = await client.callTool(
723
- {
724
- name: toolName,
725
- arguments: args
726
- },
727
- void 0,
728
- { timeout: getTimeoutMs() }
729
- );
730
- return result;
731
- }, `call tool ${toolName}`);
704
+ async function callTool(client, toolName, args, settings) {
705
+ return withRetry(
706
+ async () => {
707
+ const result = await client.callTool(
708
+ {
709
+ name: toolName,
710
+ arguments: args
711
+ },
712
+ void 0,
713
+ { timeout: getTimeoutMs(settings) }
714
+ );
715
+ return result;
716
+ },
717
+ `call tool ${toolName}`,
718
+ getRetryConfig(settings)
719
+ );
732
720
  }
733
721
 
734
722
  // src/daemon.ts
@@ -800,10 +788,10 @@ function killProcess(pid) {
800
788
  return false;
801
789
  }
802
790
  }
803
- async function runDaemon(serverName, config) {
791
+ async function runDaemon(serverName, config, settings) {
804
792
  const socketPath = getSocketPath();
805
793
  const configHash = getConfigHash(config);
806
- const timeoutMs = getDaemonTimeoutMs();
794
+ const timeoutMs = getDaemonTimeoutMs(settings);
807
795
  let idleTimer = null;
808
796
  let mcpClient = null;
809
797
  let server = null;
@@ -995,8 +983,11 @@ async function runDaemon(serverName, config) {
995
983
  if (process.argv[2] === "--daemon") {
996
984
  const serverName = process.argv[3];
997
985
  const configJson = process.argv[4];
986
+ const settingsJson = process.argv[5];
998
987
  if (!serverName || !configJson) {
999
- console.error("Usage: daemon.ts --daemon <serverName> <configJson>");
988
+ console.error(
989
+ "Usage: daemon.ts --daemon <serverName> <configJson> [settingsJson]"
990
+ );
1000
991
  process.exit(1);
1001
992
  }
1002
993
  let config;
@@ -1006,7 +997,16 @@ if (process.argv[2] === "--daemon") {
1006
997
  console.error("Invalid config JSON");
1007
998
  process.exit(1);
1008
999
  }
1009
- runDaemon(serverName, config).catch((error) => {
1000
+ let settings;
1001
+ if (settingsJson) {
1002
+ try {
1003
+ settings = JSON.parse(settingsJson);
1004
+ } catch {
1005
+ console.error("Invalid settings JSON");
1006
+ process.exit(1);
1007
+ }
1008
+ }
1009
+ runDaemon(serverName, config, settings).catch((error) => {
1010
1010
  console.error("Daemon failed:", error);
1011
1011
  process.exit(1);
1012
1012
  });