@mcp-s/cli 0.0.9 → 0.0.10
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 +37 -14
- package/SKILL.md +42 -34
- package/dist/SKILL.md +42 -34
- package/dist/daemon.js +52 -52
- package/dist/index.js +118 -94
- package/package.json +2 -2
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`/`
|
|
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
|
|
359
|
-
|
|
|
360
|
-
| `
|
|
361
|
-
| `
|
|
362
|
-
| `
|
|
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
|
|
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
|
-
|
|
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
|
|
16
|
-
| `mcp-s-cli
|
|
17
|
-
| `mcp-s-cli
|
|
18
|
-
| `mcp-s-cli call <tool
|
|
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` →
|
|
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
|
|
60
|
+
mcp-s-cli info my-tool
|
|
41
61
|
|
|
42
62
|
# Call tool
|
|
43
|
-
mcp-s-cli call
|
|
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
|
|
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`
|
|
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
|
|
16
|
-
| `mcp-s-cli
|
|
17
|
-
| `mcp-s-cli
|
|
18
|
-
| `mcp-s-cli call <tool
|
|
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` →
|
|
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
|
|
60
|
+
mcp-s-cli info my-tool
|
|
41
61
|
|
|
42
62
|
# Call tool
|
|
43
|
-
mcp-s-cli call
|
|
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
|
|
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`
|
|
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.
|
|
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.
|
|
361
|
+
if (process.env.MCP_S_CLI_DEBUG) {
|
|
362
362
|
console.error(`[mcp-s-cli] ${message}`);
|
|
363
363
|
}
|
|
364
364
|
}
|
|
365
|
-
function getTimeoutMs() {
|
|
366
|
-
|
|
367
|
-
|
|
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
|
-
|
|
377
|
-
|
|
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
|
-
|
|
387
|
-
|
|
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
|
-
|
|
398
|
-
|
|
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.
|
|
417
|
+
var VERSION = "0.0.10";
|
|
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.
|
|
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(
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
});
|
package/dist/index.js
CHANGED
|
@@ -60,7 +60,7 @@ async function saveAuthData() {
|
|
|
60
60
|
mode: 384
|
|
61
61
|
});
|
|
62
62
|
} catch (err) {
|
|
63
|
-
if (process.env.
|
|
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
|
);
|
|
@@ -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.
|
|
586
|
+
if (process.env.MCP_S_CLI_DEBUG) {
|
|
587
587
|
console.error(`[mcp-s-cli] ${message}`);
|
|
588
588
|
}
|
|
589
589
|
}
|
|
590
|
-
function getTimeoutMs() {
|
|
591
|
-
|
|
592
|
-
|
|
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
|
-
|
|
602
|
-
|
|
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
|
-
|
|
612
|
-
|
|
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
|
|
609
|
+
function isDaemonEnabled(settings) {
|
|
610
|
+
return settings?.daemon !== false;
|
|
623
611
|
}
|
|
624
|
-
function getDaemonTimeoutMs() {
|
|
625
|
-
|
|
626
|
-
|
|
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.
|
|
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
|
|
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.
|
|
752
|
-
configPath = resolve(process.env.
|
|
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)) {
|
|
@@ -787,7 +771,8 @@ async function loadConfig(explicitPath) {
|
|
|
787
771
|
"userAccessKey",
|
|
788
772
|
"token",
|
|
789
773
|
"allowedTools",
|
|
790
|
-
"disabledTools"
|
|
774
|
+
"disabledTools",
|
|
775
|
+
"settings"
|
|
791
776
|
];
|
|
792
777
|
const hasKnownField = knownFields.some((f) => f in raw);
|
|
793
778
|
if (!hasKnownField) {
|
|
@@ -795,7 +780,7 @@ async function loadConfig(explicitPath) {
|
|
|
795
780
|
}
|
|
796
781
|
raw = substituteEnvVarsInObject(raw);
|
|
797
782
|
const serverConfig = deriveServerConfig(raw);
|
|
798
|
-
return { raw, serverConfig };
|
|
783
|
+
return { raw, serverConfig, settings: raw.settings ?? {} };
|
|
799
784
|
}
|
|
800
785
|
|
|
801
786
|
// src/daemon-client.ts
|
|
@@ -883,10 +868,10 @@ function killProcess(pid) {
|
|
|
883
868
|
return false;
|
|
884
869
|
}
|
|
885
870
|
}
|
|
886
|
-
async function runDaemon(serverName, config) {
|
|
871
|
+
async function runDaemon(serverName, config, settings) {
|
|
887
872
|
const socketPath = getSocketPath();
|
|
888
873
|
const configHash = getConfigHash(config);
|
|
889
|
-
const timeoutMs = getDaemonTimeoutMs();
|
|
874
|
+
const timeoutMs = getDaemonTimeoutMs(settings);
|
|
890
875
|
let idleTimer = null;
|
|
891
876
|
let mcpClient = null;
|
|
892
877
|
let server = null;
|
|
@@ -1078,8 +1063,11 @@ async function runDaemon(serverName, config) {
|
|
|
1078
1063
|
if (process.argv[2] === "--daemon") {
|
|
1079
1064
|
const serverName = process.argv[3];
|
|
1080
1065
|
const configJson = process.argv[4];
|
|
1066
|
+
const settingsJson = process.argv[5];
|
|
1081
1067
|
if (!serverName || !configJson) {
|
|
1082
|
-
console.error(
|
|
1068
|
+
console.error(
|
|
1069
|
+
"Usage: daemon.ts --daemon <serverName> <configJson> [settingsJson]"
|
|
1070
|
+
);
|
|
1083
1071
|
process.exit(1);
|
|
1084
1072
|
}
|
|
1085
1073
|
let config;
|
|
@@ -1089,7 +1077,16 @@ if (process.argv[2] === "--daemon") {
|
|
|
1089
1077
|
console.error("Invalid config JSON");
|
|
1090
1078
|
process.exit(1);
|
|
1091
1079
|
}
|
|
1092
|
-
|
|
1080
|
+
let settings;
|
|
1081
|
+
if (settingsJson) {
|
|
1082
|
+
try {
|
|
1083
|
+
settings = JSON.parse(settingsJson);
|
|
1084
|
+
} catch {
|
|
1085
|
+
console.error("Invalid settings JSON");
|
|
1086
|
+
process.exit(1);
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
runDaemon(serverName, config, settings).catch((error) => {
|
|
1093
1090
|
console.error("Daemon failed:", error);
|
|
1094
1091
|
process.exit(1);
|
|
1095
1092
|
});
|
|
@@ -1175,17 +1172,18 @@ function isDaemonValid(serverName, config) {
|
|
|
1175
1172
|
}
|
|
1176
1173
|
return true;
|
|
1177
1174
|
}
|
|
1178
|
-
async function spawnDaemon(serverName, config) {
|
|
1175
|
+
async function spawnDaemon(serverName, config, settings) {
|
|
1179
1176
|
debug(`[daemon-client] Spawning daemon for ${serverName}`);
|
|
1180
1177
|
const __dirname = join2(fileURLToPath(import.meta.url), "..");
|
|
1181
1178
|
const daemonScript = join2(__dirname, "daemon.js");
|
|
1182
1179
|
const configJson = JSON.stringify(config);
|
|
1180
|
+
const settingsJson = JSON.stringify(settings ?? {});
|
|
1183
1181
|
const proc = spawn(
|
|
1184
1182
|
"node",
|
|
1185
|
-
[daemonScript, "--daemon", serverName, configJson],
|
|
1183
|
+
[daemonScript, "--daemon", serverName, configJson, settingsJson],
|
|
1186
1184
|
{
|
|
1187
1185
|
stdio: "ignore",
|
|
1188
|
-
env: { ...process.env,
|
|
1186
|
+
env: { ...process.env, MCP_S_CLI_NO_OAUTH: "1" },
|
|
1189
1187
|
detached: true
|
|
1190
1188
|
}
|
|
1191
1189
|
);
|
|
@@ -1208,10 +1206,10 @@ async function spawnDaemon(serverName, config) {
|
|
|
1208
1206
|
debug(`[daemon-client] Daemon spawn timeout for ${serverName}`);
|
|
1209
1207
|
return false;
|
|
1210
1208
|
}
|
|
1211
|
-
async function getDaemonConnection(serverName, config) {
|
|
1209
|
+
async function getDaemonConnection(serverName, config, settings) {
|
|
1212
1210
|
const socketPath = getSocketPath();
|
|
1213
1211
|
if (!isDaemonValid(serverName, config)) {
|
|
1214
|
-
const spawned = await spawnDaemon(serverName, config);
|
|
1212
|
+
const spawned = await spawnDaemon(serverName, config, settings);
|
|
1215
1213
|
if (!spawned) {
|
|
1216
1214
|
debug(`[daemon-client] Failed to spawn daemon for ${serverName}`);
|
|
1217
1215
|
return null;
|
|
@@ -1299,13 +1297,13 @@ async function cleanupOrphanedDaemons() {
|
|
|
1299
1297
|
}
|
|
1300
1298
|
|
|
1301
1299
|
// src/version.ts
|
|
1302
|
-
var VERSION = "0.0.
|
|
1300
|
+
var VERSION = "0.0.10";
|
|
1303
1301
|
|
|
1304
1302
|
// src/client.ts
|
|
1305
|
-
function getRetryConfig() {
|
|
1306
|
-
const totalBudgetMs = getTimeoutMs();
|
|
1307
|
-
const maxRetries = getMaxRetries();
|
|
1308
|
-
const baseDelayMs = getRetryDelayMs();
|
|
1303
|
+
function getRetryConfig(settings) {
|
|
1304
|
+
const totalBudgetMs = getTimeoutMs(settings);
|
|
1305
|
+
const maxRetries = getMaxRetries(settings);
|
|
1306
|
+
const baseDelayMs = getRetryDelayMs(settings);
|
|
1309
1307
|
const retryBudgetMs = Math.max(0, totalBudgetMs - 5e3);
|
|
1310
1308
|
return {
|
|
1311
1309
|
maxRetries,
|
|
@@ -1443,7 +1441,7 @@ ${stderrOutput}`;
|
|
|
1443
1441
|
}, `connect to ${serverName}`);
|
|
1444
1442
|
}
|
|
1445
1443
|
async function connectToHttpServer(serverName, config) {
|
|
1446
|
-
const oauthEnabled = process.env.
|
|
1444
|
+
const oauthEnabled = process.env.MCP_S_CLI_NO_OAUTH !== "1";
|
|
1447
1445
|
return withRetry(async () => {
|
|
1448
1446
|
const configuredAuth = config.headers?.Authorization || config.headers?.authorization;
|
|
1449
1447
|
let headers = { ...config.headers };
|
|
@@ -1593,24 +1591,32 @@ async function listTools(client) {
|
|
|
1593
1591
|
}));
|
|
1594
1592
|
}, "list tools");
|
|
1595
1593
|
}
|
|
1596
|
-
async function callTool(client, toolName, args) {
|
|
1597
|
-
return withRetry(
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1594
|
+
async function callTool(client, toolName, args, settings) {
|
|
1595
|
+
return withRetry(
|
|
1596
|
+
async () => {
|
|
1597
|
+
const result = await client.callTool(
|
|
1598
|
+
{
|
|
1599
|
+
name: toolName,
|
|
1600
|
+
arguments: args
|
|
1601
|
+
},
|
|
1602
|
+
void 0,
|
|
1603
|
+
{ timeout: getTimeoutMs(settings) }
|
|
1604
|
+
);
|
|
1605
|
+
return result;
|
|
1606
|
+
},
|
|
1607
|
+
`call tool ${toolName}`,
|
|
1608
|
+
getRetryConfig(settings)
|
|
1609
|
+
);
|
|
1608
1610
|
}
|
|
1609
|
-
async function getConnection(serverName, config) {
|
|
1611
|
+
async function getConnection(serverName, config, settings) {
|
|
1610
1612
|
await cleanupOrphanedDaemons();
|
|
1611
|
-
if (isDaemonEnabled()) {
|
|
1613
|
+
if (isDaemonEnabled(settings)) {
|
|
1612
1614
|
try {
|
|
1613
|
-
const daemonConn = await getDaemonConnection(
|
|
1615
|
+
const daemonConn = await getDaemonConnection(
|
|
1616
|
+
serverName,
|
|
1617
|
+
config,
|
|
1618
|
+
settings
|
|
1619
|
+
);
|
|
1614
1620
|
if (daemonConn) {
|
|
1615
1621
|
debug(`Using daemon connection for ${serverName}`);
|
|
1616
1622
|
return {
|
|
@@ -1653,7 +1659,7 @@ async function getConnection(serverName, config) {
|
|
|
1653
1659
|
if (!isToolAllowed(toolName, config)) {
|
|
1654
1660
|
throw new Error(`Tool "${toolName}" is disabled by configuration`);
|
|
1655
1661
|
}
|
|
1656
|
-
return callTool(client, toolName, args);
|
|
1662
|
+
return callTool(client, toolName, args, settings);
|
|
1657
1663
|
},
|
|
1658
1664
|
async getInstructions() {
|
|
1659
1665
|
return client.getInstructions();
|
|
@@ -1794,12 +1800,12 @@ function formatToolResult(result) {
|
|
|
1794
1800
|
}
|
|
1795
1801
|
|
|
1796
1802
|
// src/commands/call.ts
|
|
1797
|
-
async function parseArgs(argsString) {
|
|
1803
|
+
async function parseArgs(argsString, settings) {
|
|
1798
1804
|
let jsonString;
|
|
1799
1805
|
if (argsString) {
|
|
1800
1806
|
jsonString = argsString;
|
|
1801
1807
|
} else if (!process.stdin.isTTY) {
|
|
1802
|
-
const timeoutMs = getTimeoutMs();
|
|
1808
|
+
const timeoutMs = getTimeoutMs(settings);
|
|
1803
1809
|
const chunks = [];
|
|
1804
1810
|
let timeoutId;
|
|
1805
1811
|
const readPromise = (async () => {
|
|
@@ -1841,11 +1847,11 @@ async function callCommand(options) {
|
|
|
1841
1847
|
console.error(error.message);
|
|
1842
1848
|
process.exit(1 /* CLIENT_ERROR */);
|
|
1843
1849
|
}
|
|
1844
|
-
const { serverConfig } = loaded;
|
|
1850
|
+
const { serverConfig, settings } = loaded;
|
|
1845
1851
|
const serverLabel = "server";
|
|
1846
1852
|
if (options.args !== void 0) {
|
|
1847
1853
|
try {
|
|
1848
|
-
await parseArgs(options.args);
|
|
1854
|
+
await parseArgs(options.args, settings);
|
|
1849
1855
|
} catch (error) {
|
|
1850
1856
|
console.error(error.message);
|
|
1851
1857
|
process.exit(1 /* CLIENT_ERROR */);
|
|
@@ -1853,14 +1859,14 @@ async function callCommand(options) {
|
|
|
1853
1859
|
}
|
|
1854
1860
|
let args;
|
|
1855
1861
|
try {
|
|
1856
|
-
args = await parseArgs(options.args);
|
|
1862
|
+
args = await parseArgs(options.args, settings);
|
|
1857
1863
|
} catch (error) {
|
|
1858
1864
|
console.error(error.message);
|
|
1859
1865
|
process.exit(1 /* CLIENT_ERROR */);
|
|
1860
1866
|
}
|
|
1861
1867
|
let connection;
|
|
1862
1868
|
try {
|
|
1863
|
-
connection = await getConnection(serverLabel, serverConfig);
|
|
1869
|
+
connection = await getConnection(serverLabel, serverConfig, settings);
|
|
1864
1870
|
} catch (error) {
|
|
1865
1871
|
console.error(
|
|
1866
1872
|
formatCliError(
|
|
@@ -1970,7 +1976,7 @@ async function grepCommand(options) {
|
|
|
1970
1976
|
console.error(error.message);
|
|
1971
1977
|
process.exit(1 /* CLIENT_ERROR */);
|
|
1972
1978
|
}
|
|
1973
|
-
const { serverConfig } = loaded;
|
|
1979
|
+
const { serverConfig, settings } = loaded;
|
|
1974
1980
|
const serverLabel = "server";
|
|
1975
1981
|
const pattern = globToRegex(options.pattern);
|
|
1976
1982
|
debug(`Searching for pattern "${options.pattern}"`);
|
|
@@ -1978,7 +1984,7 @@ async function grepCommand(options) {
|
|
|
1978
1984
|
const allResults = [];
|
|
1979
1985
|
let searchError;
|
|
1980
1986
|
try {
|
|
1981
|
-
connection = await getConnection(serverLabel, serverConfig);
|
|
1987
|
+
connection = await getConnection(serverLabel, serverConfig, settings);
|
|
1982
1988
|
const tools = await connection.listTools();
|
|
1983
1989
|
for (const tool of tools) {
|
|
1984
1990
|
if (pattern.test(tool.name)) {
|
|
@@ -2016,11 +2022,11 @@ async function infoCommand(options) {
|
|
|
2016
2022
|
console.error(error.message);
|
|
2017
2023
|
process.exit(1 /* CLIENT_ERROR */);
|
|
2018
2024
|
}
|
|
2019
|
-
const { serverConfig } = loaded;
|
|
2025
|
+
const { serverConfig, settings } = loaded;
|
|
2020
2026
|
const serverLabel = "server";
|
|
2021
2027
|
let connection;
|
|
2022
2028
|
try {
|
|
2023
|
-
connection = await getConnection(serverLabel, serverConfig);
|
|
2029
|
+
connection = await getConnection(serverLabel, serverConfig, settings);
|
|
2024
2030
|
} catch (error) {
|
|
2025
2031
|
console.error(
|
|
2026
2032
|
formatCliError(
|
|
@@ -2062,7 +2068,7 @@ async function infoCommand(options) {
|
|
|
2062
2068
|
}
|
|
2063
2069
|
|
|
2064
2070
|
// src/commands/init.ts
|
|
2065
|
-
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
2071
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
2066
2072
|
import { homedir as homedir4 } from "os";
|
|
2067
2073
|
import { dirname as dirname4, join as join5 } from "path";
|
|
2068
2074
|
import * as readline from "readline";
|
|
@@ -2480,6 +2486,15 @@ var GLOBAL_CONFIG_PATH2 = join5(
|
|
|
2480
2486
|
"mcp-s-cli",
|
|
2481
2487
|
"config.json"
|
|
2482
2488
|
);
|
|
2489
|
+
function readExistingConfig() {
|
|
2490
|
+
try {
|
|
2491
|
+
if (existsSync6(GLOBAL_CONFIG_PATH2)) {
|
|
2492
|
+
return JSON.parse(readFileSync4(GLOBAL_CONFIG_PATH2, "utf-8"));
|
|
2493
|
+
}
|
|
2494
|
+
} catch {
|
|
2495
|
+
}
|
|
2496
|
+
return {};
|
|
2497
|
+
}
|
|
2483
2498
|
function writeConfig(config) {
|
|
2484
2499
|
mkdirSync4(dirname4(GLOBAL_CONFIG_PATH2), { recursive: true });
|
|
2485
2500
|
writeFileSync4(
|
|
@@ -2491,7 +2506,9 @@ function writeConfig(config) {
|
|
|
2491
2506
|
}
|
|
2492
2507
|
async function initCommand(options) {
|
|
2493
2508
|
const { baseUrl, org, mcp, toolkit, userAccessKey, token } = options;
|
|
2509
|
+
const existing = readExistingConfig();
|
|
2494
2510
|
const config = {};
|
|
2511
|
+
if (existing.settings) config.settings = existing.settings;
|
|
2495
2512
|
if (org) {
|
|
2496
2513
|
config.org = org;
|
|
2497
2514
|
} else if (baseUrl) {
|
|
@@ -2654,14 +2671,14 @@ async function listCommand(options) {
|
|
|
2654
2671
|
console.error(error.message);
|
|
2655
2672
|
process.exit(1 /* CLIENT_ERROR */);
|
|
2656
2673
|
}
|
|
2657
|
-
const { serverConfig } = loaded;
|
|
2674
|
+
const { serverConfig, settings } = loaded;
|
|
2658
2675
|
const serverLabel = "server";
|
|
2659
2676
|
let connection = null;
|
|
2660
2677
|
let tools = [];
|
|
2661
2678
|
let instructions;
|
|
2662
2679
|
let errorMsg;
|
|
2663
2680
|
try {
|
|
2664
|
-
connection = await getConnection(serverLabel, serverConfig);
|
|
2681
|
+
connection = await getConnection(serverLabel, serverConfig, settings);
|
|
2665
2682
|
tools = await connection.listTools();
|
|
2666
2683
|
instructions = await connection.getInstructions();
|
|
2667
2684
|
debug(`${serverLabel}: loaded ${tools.length} tools`);
|
|
@@ -2703,15 +2720,15 @@ async function logoutCommand(options) {
|
|
|
2703
2720
|
}
|
|
2704
2721
|
|
|
2705
2722
|
// src/commands/whoami.ts
|
|
2706
|
-
import { existsSync as existsSync8, readFileSync as
|
|
2723
|
+
import { existsSync as existsSync8, readFileSync as readFileSync5, statSync } from "fs";
|
|
2707
2724
|
import { homedir as homedir5 } from "os";
|
|
2708
2725
|
import { join as join6, resolve as resolve3 } from "path";
|
|
2709
2726
|
function getResolvedConfigPath(explicitPath) {
|
|
2710
2727
|
if (explicitPath) {
|
|
2711
2728
|
return resolve3(explicitPath);
|
|
2712
2729
|
}
|
|
2713
|
-
if (process.env.
|
|
2714
|
-
return resolve3(process.env.
|
|
2730
|
+
if (process.env.MCP_S_CLI_CONFIG_PATH) {
|
|
2731
|
+
return resolve3(process.env.MCP_S_CLI_CONFIG_PATH);
|
|
2715
2732
|
}
|
|
2716
2733
|
const defaultPath = join6(homedir5(), ".config", "mcp-s-cli", "config.json");
|
|
2717
2734
|
if (existsSync8(defaultPath)) {
|
|
@@ -2750,7 +2767,7 @@ async function whoamiCommand(options) {
|
|
|
2750
2767
|
console.log(" (no tokens stored)");
|
|
2751
2768
|
} else {
|
|
2752
2769
|
try {
|
|
2753
|
-
const raw2 =
|
|
2770
|
+
const raw2 = readFileSync5(authFilePath, "utf-8");
|
|
2754
2771
|
const data = JSON.parse(raw2);
|
|
2755
2772
|
const tokenEntries = Object.entries(data.tokens ?? {});
|
|
2756
2773
|
if (tokenEntries.length === 0) {
|
|
@@ -2762,7 +2779,7 @@ async function whoamiCommand(options) {
|
|
|
2762
2779
|
}
|
|
2763
2780
|
const historyPath = join6(homedir5(), ".config", "mcp-s-cli", "history.jsonl");
|
|
2764
2781
|
if (existsSync8(historyPath)) {
|
|
2765
|
-
const lines =
|
|
2782
|
+
const lines = readFileSync5(historyPath, "utf-8").split("\n").filter(Boolean);
|
|
2766
2783
|
const size = statSync(historyPath).size;
|
|
2767
2784
|
const sizeStr = size >= 1024 * 1024 ? `${(size / (1024 * 1024)).toFixed(1)} MB` : size >= 1024 ? `${(size / 1024).toFixed(1)} KB` : `${size} B`;
|
|
2768
2785
|
console.log("");
|
|
@@ -3067,19 +3084,19 @@ Examples:
|
|
|
3067
3084
|
mcp-s-cli kill-daemon # Kill the running daemon process
|
|
3068
3085
|
|
|
3069
3086
|
Environment Variables:
|
|
3070
|
-
|
|
3071
|
-
|
|
3087
|
+
MCP_S_CLI_DEBUG=1 Enable debug output
|
|
3088
|
+
MCP_S_CLI_STRICT_ENV=false Warn (instead of error) on missing \${VAR} in config
|
|
3072
3089
|
|
|
3073
3090
|
Config File:
|
|
3074
3091
|
The CLI looks for config.json in:
|
|
3075
|
-
1. Path specified by
|
|
3092
|
+
1. Path specified by MCP_S_CLI_CONFIG_PATH or -c/--config
|
|
3076
3093
|
2. ~/.config/mcp-s-cli/config.json
|
|
3077
3094
|
|
|
3078
3095
|
'mcp-s-cli init' always writes to ~/.config/mcp-s-cli/config.json
|
|
3079
3096
|
`);
|
|
3080
3097
|
}
|
|
3081
|
-
function appendHistory(argv) {
|
|
3082
|
-
if (
|
|
3098
|
+
function appendHistory(argv, history) {
|
|
3099
|
+
if (!history) return;
|
|
3083
3100
|
try {
|
|
3084
3101
|
const dir = join7(homedir6(), ".config", "mcp-s-cli");
|
|
3085
3102
|
mkdirSync5(dir, { recursive: true });
|
|
@@ -3095,8 +3112,15 @@ function appendHistory(argv) {
|
|
|
3095
3112
|
}
|
|
3096
3113
|
}
|
|
3097
3114
|
async function main() {
|
|
3098
|
-
|
|
3099
|
-
const args = parseArgs2(
|
|
3115
|
+
const argv = process.argv.slice(2);
|
|
3116
|
+
const args = parseArgs2(argv);
|
|
3117
|
+
let settings;
|
|
3118
|
+
try {
|
|
3119
|
+
const loaded = await loadConfig(args.configPath);
|
|
3120
|
+
settings = loaded.settings;
|
|
3121
|
+
} catch {
|
|
3122
|
+
}
|
|
3123
|
+
appendHistory(argv, settings?.history);
|
|
3100
3124
|
switch (args.command) {
|
|
3101
3125
|
case "help":
|
|
3102
3126
|
printHelp();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mcp-s/cli",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "A lightweight CLI for
|
|
3
|
+
"version": "0.0.10",
|
|
4
|
+
"description": "A lightweight CLI for connecting AI agents to the Webrix MCP Gateway",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"bin": {
|