@mcp-s/cli 0.0.17 → 0.0.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/SKILL.md CHANGED
@@ -14,6 +14,8 @@ Access a single MCP server through the command line. MCP enables interaction wit
14
14
  | `mcp-s-cli` | List all available tools |
15
15
  | `mcp-s-cli info <tool>` | Get tool JSON schema |
16
16
  | `mcp-s-cli grep <query>` | Search tools by name (case-insensitive substring) |
17
+ | `mcp-s-cli grep <query> -d` | Search tools by name, include descriptions |
18
+ | `mcp-s-cli get-servers` | List available servers (slug, name, description) |
17
19
  | `mcp-s-cli call <tool>` | Call tool (reads JSON from stdin if no args) |
18
20
  | `mcp-s-cli call <tool> '<json>'` | Call tool with arguments |
19
21
 
@@ -30,7 +32,7 @@ mcp-s-cli check-auth
30
32
 
31
33
  ## Workflow
32
34
 
33
- 1. **Discover**: `mcp-s-cli grep <query>` → find tools by keyword; fall back to `mcp-s-cli` only when you need a full inventory
35
+ 1. **Discover**: find the right tool (see discovery ladder below)
34
36
  2. **Inspect**: `mcp-s-cli info <tool>` → get full JSON schema
35
37
  3. **Execute**: `mcp-s-cli call <tool> '<json>'` → run with arguments
36
38
 
@@ -38,13 +40,14 @@ mcp-s-cli check-auth
38
40
 
39
41
  Every command consumes tokens. Pick the cheapest operation that gets the job done.
40
42
 
41
- ### Discovery: start narrow
43
+ ### Discovery ladder (follow in order, stop as soon as you're confident)
42
44
 
43
- - **`grep <query>`** — cheapest. Use when you can guess a keyword from the user's request (e.g., "read a github file" → `grep file` or `grep content`).
44
- - **`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").
45
- - **`mcp-s-cli -d`** (names + descriptions)most expensive. Use only when names alone aren't enough to pick the right tool.
45
+ 1. **`grep <query>`** — cheapest. Guess a keyword from the user's request (e.g., "read a github file" → `grep file`). If the results clearly contain the right tool, skip to **Inspect**.
46
+ 2. **`get-servers`** lightweight REST call, returns each server's **slug**, name, and description. Use when grep results are ambiguous or empty and you need to understand which servers/domains are available.
47
+ 3. **`grep <query>` scoped to a slug** now that you know the relevant server slug(s), grep again to narrow results. If you're not confident you and still need more details, continue to step 4.
48
+ 4. **`grep <query> -d`** — includes tool descriptions in the output. Use to disambiguate between similar-sounding candidates.
46
49
 
47
- **Rule: if you can guess a keyword, grep first.**
50
+ **Rule: always start with a plain `grep`. Escalate only when you're not confident you found the right tool.**
48
51
 
49
52
  ### Execution: keep responses small
50
53
 
@@ -63,7 +66,9 @@ When you pipe through a filter and get empty output, **do not** fall back to the
63
66
  ### Decision heuristic
64
67
 
65
68
  - User mentions a specific tool or domain keyword → `grep` for it.
66
- - User asks a broad question across all tools list tools first, then act.
69
+ - Grep results are empty or ambiguous `get-servers` to see available servers, then grep again with a better keyword or scoped to the relevant slug.
70
+ - You have candidates but can't tell them apart by name → `grep -d` for descriptions.
71
+ - User asks a broad question across all tools → `get-servers` first, then grep per relevant server.
67
72
  - Task involves multiple items of the same type → use one bulk/list call, not N individual calls.
68
73
  - Tool may return large payloads → use filter params + pipe to trim output.
69
74
  - Filter script produced no output → **probe one item** to learn the shape, then re-run with a corrected filter. Never fall back to the raw unfiltered call.
package/dist/SKILL.md CHANGED
@@ -14,6 +14,8 @@ Access a single MCP server through the command line. MCP enables interaction wit
14
14
  | `mcp-s-cli` | List all available tools |
15
15
  | `mcp-s-cli info <tool>` | Get tool JSON schema |
16
16
  | `mcp-s-cli grep <query>` | Search tools by name (case-insensitive substring) |
17
+ | `mcp-s-cli grep <query> -d` | Search tools by name, include descriptions |
18
+ | `mcp-s-cli get-servers` | List available servers (slug, name, description) |
17
19
  | `mcp-s-cli call <tool>` | Call tool (reads JSON from stdin if no args) |
18
20
  | `mcp-s-cli call <tool> '<json>'` | Call tool with arguments |
19
21
 
@@ -30,7 +32,7 @@ mcp-s-cli check-auth
30
32
 
31
33
  ## Workflow
32
34
 
33
- 1. **Discover**: `mcp-s-cli grep <query>` → find tools by keyword; fall back to `mcp-s-cli` only when you need a full inventory
35
+ 1. **Discover**: find the right tool (see discovery ladder below)
34
36
  2. **Inspect**: `mcp-s-cli info <tool>` → get full JSON schema
35
37
  3. **Execute**: `mcp-s-cli call <tool> '<json>'` → run with arguments
36
38
 
@@ -38,13 +40,14 @@ mcp-s-cli check-auth
38
40
 
39
41
  Every command consumes tokens. Pick the cheapest operation that gets the job done.
40
42
 
41
- ### Discovery: start narrow
43
+ ### Discovery ladder (follow in order, stop as soon as you're confident)
42
44
 
43
- - **`grep <query>`** — cheapest. Use when you can guess a keyword from the user's request (e.g., "read a github file" → `grep file` or `grep content`).
44
- - **`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").
45
- - **`mcp-s-cli -d`** (names + descriptions)most expensive. Use only when names alone aren't enough to pick the right tool.
45
+ 1. **`grep <query>`** — cheapest. Guess a keyword from the user's request (e.g., "read a github file" → `grep file`). If the results clearly contain the right tool, skip to **Inspect**.
46
+ 2. **`get-servers`** lightweight REST call, returns each server's **slug**, name, and description. Use when grep results are ambiguous or empty and you need to understand which servers/domains are available.
47
+ 3. **`grep <query>` scoped to a slug** now that you know the relevant server slug(s), grep again to narrow results. If you're not confident you and still need more details, continue to step 4.
48
+ 4. **`grep <query> -d`** — includes tool descriptions in the output. Use to disambiguate between similar-sounding candidates.
46
49
 
47
- **Rule: if you can guess a keyword, grep first.**
50
+ **Rule: always start with a plain `grep`. Escalate only when you're not confident you found the right tool.**
48
51
 
49
52
  ### Execution: keep responses small
50
53
 
@@ -63,7 +66,9 @@ When you pipe through a filter and get empty output, **do not** fall back to the
63
66
  ### Decision heuristic
64
67
 
65
68
  - User mentions a specific tool or domain keyword → `grep` for it.
66
- - User asks a broad question across all tools list tools first, then act.
69
+ - Grep results are empty or ambiguous `get-servers` to see available servers, then grep again with a better keyword or scoped to the relevant slug.
70
+ - You have candidates but can't tell them apart by name → `grep -d` for descriptions.
71
+ - User asks a broad question across all tools → `get-servers` first, then grep per relevant server.
67
72
  - Task involves multiple items of the same type → use one bulk/list call, not N individual calls.
68
73
  - Tool may return large payloads → use filter params + pipe to trim output.
69
74
  - Filter script produced no output → **probe one item** to learn the shape, then re-run with a corrected filter. Never fall back to the raw unfiltered call.
package/dist/daemon.js CHANGED
@@ -427,7 +427,7 @@ import { join as join2 } from "path";
427
427
  import { fileURLToPath } from "url";
428
428
 
429
429
  // src/version.ts
430
- var VERSION = "0.0.17";
430
+ var VERSION = "0.0.18";
431
431
 
432
432
  // src/client.ts
433
433
  function getRetryConfig(settings) {
package/dist/index.js CHANGED
@@ -358,6 +358,48 @@ async function clearTokens(url) {
358
358
  tokens.delete(origin);
359
359
  await saveAuthData();
360
360
  }
361
+ async function fetchWithAuth(url, options = {}) {
362
+ const { timeout = 6e4, ...fetchOptions } = options;
363
+ const tokens = await getStoredTokens(url);
364
+ if (tokens) {
365
+ fetchOptions.headers = {
366
+ ...fetchOptions.headers,
367
+ Authorization: `Bearer ${tokens.access_token}`
368
+ };
369
+ }
370
+ let response = await fetch(url, {
371
+ ...fetchOptions,
372
+ signal: AbortSignal.timeout(timeout)
373
+ });
374
+ if (response.status === 401) {
375
+ const wwwAuthHeader = response.headers.get("WWW-Authenticate");
376
+ if (wwwAuthHeader) {
377
+ const challenge = parseWWWAuthenticateHeader(wwwAuthHeader);
378
+ if (challenge.resource_metadata) {
379
+ const resourceMetadata = await fetchProtectedResourceMetadata(
380
+ challenge.resource_metadata
381
+ );
382
+ if (resourceMetadata?.authorization_servers?.[0]) {
383
+ const newTokens = await performOAuthFlow(
384
+ resourceMetadata.authorization_servers[0],
385
+ resourceMetadata.resource
386
+ );
387
+ if (newTokens) {
388
+ fetchOptions.headers = {
389
+ ...fetchOptions.headers,
390
+ Authorization: `Bearer ${newTokens.access_token}`
391
+ };
392
+ response = await fetch(url, {
393
+ ...fetchOptions,
394
+ signal: AbortSignal.timeout(timeout)
395
+ });
396
+ }
397
+ }
398
+ }
399
+ }
400
+ }
401
+ return response;
402
+ }
361
403
 
362
404
  // src/config.ts
363
405
  import { createHash } from "crypto";
@@ -1313,7 +1355,7 @@ async function cleanupOrphanedDaemons() {
1313
1355
  }
1314
1356
 
1315
1357
  // src/version.ts
1316
- var VERSION = "0.0.17";
1358
+ var VERSION = "0.0.18";
1317
1359
 
1318
1360
  // src/client.ts
1319
1361
  function getRetryConfig(settings) {
@@ -2270,6 +2312,87 @@ function configCommand(opts) {
2270
2312
  }
2271
2313
  }
2272
2314
 
2315
+ // src/commands/get-servers.ts
2316
+ async function getServersCommand(options) {
2317
+ const { configPath } = options;
2318
+ let raw;
2319
+ let serverConfig;
2320
+ try {
2321
+ const loaded = await loadConfig(configPath);
2322
+ raw = loaded.raw;
2323
+ serverConfig = loaded.serverConfig;
2324
+ } catch (error) {
2325
+ console.error(error.message);
2326
+ process.exit(1 /* CLIENT_ERROR */);
2327
+ }
2328
+ if (!isHttpServer(serverConfig)) {
2329
+ console.error(
2330
+ "get-servers is only supported for HTTP servers (org/baseUrl config)."
2331
+ );
2332
+ process.exit(1 /* CLIENT_ERROR */);
2333
+ }
2334
+ const mcpUrl = serverConfig.url.replace(/\/+$/, "");
2335
+ const url = `${mcpUrl}/servers`;
2336
+ const headers = {};
2337
+ if (raw.token) {
2338
+ headers.Authorization = `Bearer ${raw.token}`;
2339
+ }
2340
+ if (raw.mcp) {
2341
+ headers["x-mcp"] = raw.mcp;
2342
+ }
2343
+ if (raw.toolkit) {
2344
+ headers["x-toolkit"] = raw.toolkit;
2345
+ }
2346
+ let response;
2347
+ try {
2348
+ response = await fetchWithAuth(url, { headers });
2349
+ } catch (error) {
2350
+ const msg = error.message;
2351
+ if (msg.includes("ECONNREFUSED") || msg.includes("ENOTFOUND") || msg.includes("fetch failed")) {
2352
+ console.error(
2353
+ `Network error: could not connect to ${mcpUrl}. Is the server running?`
2354
+ );
2355
+ process.exit(3 /* NETWORK_ERROR */);
2356
+ }
2357
+ console.error(`Request failed: ${msg}`);
2358
+ process.exit(2 /* SERVER_ERROR */);
2359
+ }
2360
+ if (response.status === 401) {
2361
+ console.error("Not authenticated. Run: mcp-s-cli login");
2362
+ process.exit(4 /* AUTH_ERROR */);
2363
+ }
2364
+ if (!response.ok) {
2365
+ console.error(`Server returned ${response.status}: ${response.statusText}`);
2366
+ process.exit(2 /* SERVER_ERROR */);
2367
+ }
2368
+ let data;
2369
+ try {
2370
+ data = await response.json();
2371
+ } catch {
2372
+ console.error("Invalid JSON response from server.");
2373
+ process.exit(2 /* SERVER_ERROR */);
2374
+ }
2375
+ const servers = data.data ?? [];
2376
+ if (servers.length === 0) {
2377
+ console.log("No servers available.");
2378
+ return;
2379
+ }
2380
+ const lines = [];
2381
+ for (const server of servers) {
2382
+ lines.push(`slug: ${server.slug}`);
2383
+ if (server.description) {
2384
+ lines.push(`${server.name} - ${server.description}`);
2385
+ } else {
2386
+ lines.push(server.name);
2387
+ }
2388
+ lines.push("");
2389
+ }
2390
+ if (lines.length > 0 && lines[lines.length - 1] === "") {
2391
+ lines.pop();
2392
+ }
2393
+ console.log(lines.join("\n"));
2394
+ }
2395
+
2273
2396
  // src/commands/grep.ts
2274
2397
  function matchesToolName(name, query) {
2275
2398
  return name.toLowerCase().includes(query.toLowerCase());
@@ -3334,6 +3457,10 @@ function parseArgs2(args) {
3334
3457
  result.command = "disable";
3335
3458
  return result;
3336
3459
  }
3460
+ if (firstArg === "get-servers") {
3461
+ result.command = "get-servers";
3462
+ return result;
3463
+ }
3337
3464
  if (firstArg === "config") {
3338
3465
  result.command = "config";
3339
3466
  const sub = positional[1];
@@ -3398,6 +3525,7 @@ Usage:
3398
3525
  mcp-s-cli login Log in to the configured server via OAuth
3399
3526
  mcp-s-cli logout Log out (remove stored OAuth tokens)
3400
3527
  mcp-s-cli check-auth Check auth state offline (no network); exits 0=ok, 4=needs login
3528
+ mcp-s-cli get-servers List available MCP servers
3401
3529
  mcp-s-cli config set <key> <value> Set a value in config.json
3402
3530
  mcp-s-cli config get <key> Get a value from config.json
3403
3531
  mcp-s-cli config show Print full config.json
@@ -3440,6 +3568,7 @@ Examples:
3440
3568
  mcp-s-cli login # Authenticate via OAuth
3441
3569
  mcp-s-cli logout # Remove stored OAuth tokens
3442
3570
  mcp-s-cli check-auth # Check auth state offline (fast preflight)
3571
+ mcp-s-cli get-servers # List available MCP servers
3443
3572
  mcp-s-cli config set settings.timeout 30 # Set request timeout to 30s
3444
3573
  mcp-s-cli clear # Reset server config
3445
3574
  mcp-s-cli clear-auth # Clear stored auth data
@@ -3568,6 +3697,11 @@ async function main() {
3568
3697
  case "check-auth":
3569
3698
  await checkAuthCommand({ configPath: args.configPath });
3570
3699
  break;
3700
+ case "get-servers":
3701
+ await getServersCommand({
3702
+ configPath: args.configPath
3703
+ });
3704
+ break;
3571
3705
  case "clear":
3572
3706
  await clearCommand();
3573
3707
  break;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcp-s/cli",
3
- "version": "0.0.17",
3
+ "version": "0.0.18",
4
4
  "description": "A lightweight CLI for connecting AI agents to the Webrix MCP Gateway",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",