@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 +12 -7
- package/dist/SKILL.md +12 -7
- package/dist/daemon.js +1 -1
- package/dist/index.js +135 -1
- package/package.json +1 -1
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**:
|
|
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
|
|
43
|
+
### Discovery ladder (follow in order, stop as soon as you're confident)
|
|
42
44
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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:
|
|
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
|
-
-
|
|
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**:
|
|
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
|
|
43
|
+
### Discovery ladder (follow in order, stop as soon as you're confident)
|
|
42
44
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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:
|
|
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
|
-
-
|
|
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
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.
|
|
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;
|