@ai-ecoverse/mcpcat 0.16.3 → 0.17.1
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 +11 -1
- package/dist/host/mcpcat.mjs +37 -20
- package/dist/kernel/mcpcat.mjs +37 -20
- package/dist/skills/mcpcat/SKILL.md +72 -0
- package/package.json +10 -5
package/README.md
CHANGED
|
@@ -26,7 +26,13 @@ mcpcat runs natively on macOS, Linux and Windows (Node ≥ 24), and inside Slicc
|
|
|
26
26
|
|
|
27
27
|
## Releases: certified = latest
|
|
28
28
|
|
|
29
|
-
Every change that alters what mcpcat ships is certified before it reaches `main`. A pull request that touches `src/`, the build, the dependencies or the release workflows gets mcpcat's cert-review on a pack of its head, and again in the merge queue on the exact tree that lands on `main`; a FAIL blocks the merge. Docs-, test- and CI-only changes skip it as "not behaviour". Each merge to `main` is then released to npm as `latest` in one step by semantic-release, with provenance, so `npm install @ai-ecoverse/mcpcat` and the installer always get certified code. The verdict line is a comment on the pull request and the `cert-review` commit status.
|
|
29
|
+
Every change that alters what mcpcat ships is certified before it reaches `main`. A pull request that touches `src/`, the build, the dependencies or the release workflows gets mcpcat's cert-review on a pack of its head, and again in the merge queue on the exact tree that lands on `main`; a FAIL blocks the merge. Docs-, test- and CI-only changes skip it as "not behaviour". A draft pull request is not reviewed: its `cert-review` is red with `draft: not reviewed, mark it ready for review`, and marking it ready starts the review (converted_to_draft cancels a running one). Mark ready = reviewed, and merges on green; a pull request held for anything stays a draft. For a PR that needs the coordinator's security read, the order is: the coordinator reads the draft, it is marked ready, the cert runs, then the queue. Each merge to `main` is then released to npm as `latest` in one step by semantic-release, with provenance, so `npm install @ai-ecoverse/mcpcat` and the installer always get certified code. The verdict line is a comment on the pull request and the `cert-review` commit status. It reads `certified …`, `cert-ci: FAIL …` when the reviewer or a check in code found a defect (status `failure`), or `cert-ci: INCOMPLETE …` when the review couldn't decide: the review job didn't succeed or was skipped, no verdict could be read, or the artifact or the runner's own `gh` was missing (status `error`). A fork's change to shipped behaviour also reads INCOMPLETE, since it gets no review. INCOMPLETE never certifies; re-run the failed jobs. A run that ends without `record` posting its status (a crashed plan, record or verdict job) still posts one on the head: `error` with its INCOMPLETE line, `failure` for a FAIL, or `pending` while the coordinator approval is missing. So the required `cert-review` check never waits forever, and a label change reuses only a `success` or `failure` verdict, never an INCOMPLETE one. The reviewer's verdict can add defects but never clear one found in code: `record` re-reads the suites' evidence (`stage.json`, `suites.json`), uploaded before the reviewer runs and bound by the artifact id and digest the review job output. A suite or security row that failed on the host or a pinned kernel, a changed test that fails on the head, an import in the kernel bundle or an unexplained rebuild difference is a FAIL. Missing suites, kernels or rows are INCOMPLETE. The NEGATIVE runs each changed test on the base's code: the subject's tests, helpers and fixtures with the base's `dist`, `scripts/` and `.github/workflows/`, all from the base sha, so a test of the cert tooling runs against main's tooling. The record names both shas. A changed test that also passes on the base is named in the line as `gaps: no-negative <file>`. Every artifact a later job reads (the tarball, the verdict, the suites' evidence) is downloaded by the artifact id its producer output, after `bind.mjs` checks the artifact's name, run and digest against that producer's outputs. Any other artifact from this attempt of the run under `mcpcat-tarball-`, `mcpcat-npm-tarball-` or `cert-review-` makes the line INCOMPLETE. An earlier attempt's leftovers, created before this attempt started, are never read.
|
|
30
|
+
|
|
31
|
+
After each release, `cert-verify.yml` (on the Release run's success, or dispatched from `main` with a `version`) checks again that npm serves the SLSA subject built by `release.yml` on `main` from the tag, and that every pull request merged since the previous tag carries a green `cert-review` status on its head. Its hosted, write-only `notes` job then puts one line into the GitHub release notes, between `<!-- cert-verify -->` markers: `certified @ai-ecoverse/mcpcat@<version> sha256=<sha> (…; pr-certified since v<prev>: …)`, or `verified …` when some pull request lacks a green cert-review. A re-run with the same bytes leaves the notes as they are. Notes that already name another sha for the version, or npm bytes that don't verify, are a supply-chain alert: nothing is written and an issue is filed. Checks that couldn't run read `cert-ci: INCOMPLETE` and write nothing.
|
|
32
|
+
|
|
33
|
+
Silence never passes: every gate in the workflows prints a verdict line, and its caller requires that line, not just an exit code. These are `all-green: PASS|FAIL …`, the cert job's `cert: kernel <v> — N kernel tests passed`, shipped.mjs's `behaviour:`/`not behaviour:`, release verify's `verified …`, and the installer tests' `All installer tests passed.`. A gate that exits 0 without its line fails.
|
|
34
|
+
|
|
35
|
+
A pull request or merge group that touches the certification itself (the `cert-review` and `ci` workflows, `scripts/cert-review/` and `rows.json`, `scripts/cert.mjs`, `scripts/kernel-test-files.mjs`, and the cert-row, cert-gate, cert-review, cert-runner, cert-self-guard, cert-verify and cert-verdicts tests) fails `cert-review` with "needs coordinator review" until the label `cert-review-approved` is applied after its head arrived. The check is the shared [cert self-guard](https://github.com/ai-ecoverse/slicc-shared-web/tree/f342d7cf558c817ce517bbbbc4cc46df6eac2f97/.github/actions/cert-self-guard), pinned to a full sha in `cert-review.yml`. Only the lane coordinator applies the label, after reading the diff; workers never add or remove it. Any push removes it, so every new head needs a fresh approval; a label change re-runs the checks but reuses the recorded cert-review verdict of an unchanged head. To re-check an approval, run the label event, not the earlier push. The label is procedural: every agent acts as one admin account, so the permission check cannot tell a coordinator from a worker, and a deliberate rewrite of the workflow is not stopped. The `kernel-cert-*` pins in `package.json` are not guarded, because the guard matches whole files and guarding `package.json` would hold every Renovate bump for approval.
|
|
30
36
|
|
|
31
37
|
## Quick start
|
|
32
38
|
|
|
@@ -36,6 +42,10 @@ mcpcat weather get-forecast --lat 51.5 --lon -0.12
|
|
|
36
42
|
mcpcat serve --http 8808 ./scripts/jira.jsh
|
|
37
43
|
```
|
|
38
44
|
|
|
45
|
+
## Agent skill
|
|
46
|
+
|
|
47
|
+
The package ships an [Agent Skill](https://agentskills.io/specification) that teaches an agent mcpcat in one file: `dist/skills/mcpcat/SKILL.md`, built from [`src/skill/SKILL.txt`](src/skill/SKILL.txt) (the repo keeps no Markdown besides README.md and AGENTS.md). It covers targets, listing and calling tools, `mcp.json`, exit codes, and the secrets rule: `${NAME}` placeholders in single quotes, never a value in a command, a file or the conversation, and never a token in `mcp.json`. `mcpcat help --skill` prints the copy that matches the installed version. `package.json` declares it as a pi package skill (`"pi": { "skills": ["dist/skills"] }`). In seven, bios links it into `/usr/share/skills` for the certified catalog install. It is shipped behaviour under `src/`, so a change to it gets a cert review.
|
|
48
|
+
|
|
39
49
|
## License
|
|
40
50
|
|
|
41
51
|
Apache-2.0
|
package/dist/host/mcpcat.mjs
CHANGED
|
@@ -11248,17 +11248,17 @@ function sanitizeName(raw) {
|
|
|
11248
11248
|
const cleaned = raw.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
11249
11249
|
return cleaned || "cli";
|
|
11250
11250
|
}
|
|
11251
|
-
function cliPrefix(
|
|
11251
|
+
function cliPrefix(help2, filePath, explicit) {
|
|
11252
11252
|
if (explicit) return sanitizeName(explicit);
|
|
11253
|
-
const fromUsage = usageProgramName(
|
|
11253
|
+
const fromUsage = usageProgramName(help2);
|
|
11254
11254
|
if (fromUsage) return sanitizeName(fromUsage);
|
|
11255
11255
|
const base = filePath.split("/").pop() ?? filePath;
|
|
11256
11256
|
return sanitizeName(base.replace(/\.jsh$/i, ""));
|
|
11257
11257
|
}
|
|
11258
|
-
function parseHelp(
|
|
11259
|
-
const block = commandsFromBlock(
|
|
11260
|
-
const program = usageProgramName(
|
|
11261
|
-
const commands = mergeCommandLists(block.commands, usageCommands(
|
|
11258
|
+
function parseHelp(help2) {
|
|
11259
|
+
const block = commandsFromBlock(help2);
|
|
11260
|
+
const program = usageProgramName(help2);
|
|
11261
|
+
const commands = mergeCommandLists(block.commands, usageCommands(help2, program));
|
|
11262
11262
|
return {
|
|
11263
11263
|
...program ? { program } : {},
|
|
11264
11264
|
commands: applyScopedFlags(commands, block.scoped),
|
|
@@ -11402,12 +11402,12 @@ function encodeFlag(flag, value) {
|
|
|
11402
11402
|
}
|
|
11403
11403
|
return { argv: [`--${flag.name}`, String(value)] };
|
|
11404
11404
|
}
|
|
11405
|
-
function commandsFromBlock(
|
|
11405
|
+
function commandsFromBlock(help2) {
|
|
11406
11406
|
let section = "none";
|
|
11407
11407
|
const commands = [];
|
|
11408
11408
|
const globalFlags = [];
|
|
11409
11409
|
const scoped = [];
|
|
11410
|
-
for (const rawLine of
|
|
11410
|
+
for (const rawLine of help2.split("\n")) {
|
|
11411
11411
|
const line = rawLine.replace(/\t/g, " ");
|
|
11412
11412
|
const header = classifyHeader(line);
|
|
11413
11413
|
if (header) {
|
|
@@ -11455,8 +11455,8 @@ function parseCommandLine(line) {
|
|
|
11455
11455
|
flags: spec.flags
|
|
11456
11456
|
};
|
|
11457
11457
|
}
|
|
11458
|
-
function usageProgramName(
|
|
11459
|
-
for (const raw of
|
|
11458
|
+
function usageProgramName(help2) {
|
|
11459
|
+
for (const raw of help2.split("\n")) {
|
|
11460
11460
|
if (!/^[\s>*]*usage:/i.test(raw)) continue;
|
|
11461
11461
|
const tokens = tokenize2(raw);
|
|
11462
11462
|
const name = tokens[0];
|
|
@@ -11464,9 +11464,9 @@ function usageProgramName(help) {
|
|
|
11464
11464
|
}
|
|
11465
11465
|
return void 0;
|
|
11466
11466
|
}
|
|
11467
|
-
function usageCommands(
|
|
11467
|
+
function usageCommands(help2, program) {
|
|
11468
11468
|
const commands = [];
|
|
11469
|
-
for (const raw of
|
|
11469
|
+
for (const raw of help2.split("\n")) {
|
|
11470
11470
|
const command = usageCommand(raw, program);
|
|
11471
11471
|
if (command) commands.push(command);
|
|
11472
11472
|
}
|
|
@@ -15432,11 +15432,15 @@ Client verbs:
|
|
|
15432
15432
|
tools <target> List tools
|
|
15433
15433
|
call <target> <tool> Call a tool
|
|
15434
15434
|
describe <target> [tool] Tool schema and help
|
|
15435
|
+
resources <target> List resources
|
|
15436
|
+
read <target> <uri> Read a resource
|
|
15437
|
+
prompts <target> List prompts
|
|
15438
|
+
prompt <target> <name> Get a prompt
|
|
15435
15439
|
|
|
15436
15440
|
Configuration verbs:
|
|
15437
15441
|
add <name> <url> Add a server to mcp.json
|
|
15438
|
-
add <name> --bridge --
|
|
15439
|
-
|
|
15442
|
+
add <name> --bridge -- <command> [args\u2026]
|
|
15443
|
+
Bridge a stdio server to HTTP (kernel only,
|
|
15440
15444
|
runs in foreground, does not survive reload)
|
|
15441
15445
|
rm <name> Remove a server
|
|
15442
15446
|
list List configured servers
|
|
@@ -15452,11 +15456,12 @@ Server and code verbs:
|
|
|
15452
15456
|
grep <pattern> [target\u2026] Search tools by name or description
|
|
15453
15457
|
discover <url> Find MCP servers at a URL
|
|
15454
15458
|
inbox [--json] List discovery candidates
|
|
15455
|
-
help
|
|
15459
|
+
help Show this help
|
|
15460
|
+
help --skill Print the agent skill (SKILL.md) for this version
|
|
15456
15461
|
|
|
15457
15462
|
Options:
|
|
15458
15463
|
--json JSON output
|
|
15459
|
-
-H, --header 'N: V'
|
|
15464
|
+
-H, --header 'N: V' Extra header
|
|
15460
15465
|
-d <json> Tool arguments as JSON (or @file, @- for stdin)
|
|
15461
15466
|
-m, --timeout <s> Request timeout (default 60)
|
|
15462
15467
|
--memory <MiB> Code mode memory limit (default 256, max 1024)
|
|
@@ -15470,7 +15475,7 @@ Options:
|
|
|
15470
15475
|
-s, --server <name> Server target (code, repeatable)
|
|
15471
15476
|
--allow-shell Enable shell() global (code)
|
|
15472
15477
|
-v, --verbose Trace on stderr
|
|
15473
|
-
|
|
15478
|
+
--silent Suppress progress
|
|
15474
15479
|
-o, --output <path> Write result to file
|
|
15475
15480
|
-V, --version Print version
|
|
15476
15481
|
-h, --help Show this help
|
|
@@ -16302,6 +16307,19 @@ async function run(platform2, version, args2) {
|
|
|
16302
16307
|
throw err;
|
|
16303
16308
|
}
|
|
16304
16309
|
}
|
|
16310
|
+
async function help(platform2, version, args2) {
|
|
16311
|
+
if (args2[0] !== "help" || !args2.includes("--skill")) {
|
|
16312
|
+
await platform2.write(1, helpText(version));
|
|
16313
|
+
return 0;
|
|
16314
|
+
}
|
|
16315
|
+
const skill = true ? "---\nname: mcpcat\ndescription: Call Model Context Protocol (MCP) servers from the shell with mcpcat, a curl for MCP. List and call tools, read resources and prompts, keep servers in mcp.json, and send secrets as ${VAR} placeholders that the vault fills in, never as values. Use when a task needs a tool from an MCP server over HTTP or a stdio command.\n---\n\n# mcpcat\n\n`mcpcat` talks to MCP servers over Streamable HTTP or stdio, one shell command per request. Options go before the target.\n\n## Targets\n\n- A URL: `https://example.com/mcp`.\n- A stdio command: `stdio:<command> [args\u2026]`, as one argument (`'stdio:my-server --flag'`).\n- A name from `mcp.json`, the same `~/.pi/agent/mcp.json` the agent reads.\n\n## Discover and call\n\n```bash\nmcpcat info https://example.com/mcp\nmcpcat tools https://example.com/mcp\nmcpcat describe https://example.com/mcp search\nmcpcat call https://example.com/mcp search --q oauth --limit 5\nmcpcat call docs search -d '{\"q\":\"oauth\",\"limit\":5}'\nmcpcat --json call docs search --q oauth\nmcpcat grep issue\n```\n\n- Tool arguments are `--name value` flags, coerced to the schema's types, or one JSON object with `-d` (`-d @file`, `-d @-` for stdin).\n- `describe` prints a tool's schema and help; `--schema` prints the raw JSON Schema, `--ts` TypeScript declarations.\n- `--json` prints deterministic JSON for scripts; human output otherwise. `-m <s>` sets the timeout (default 60 s).\n- `code -e '<js>'` runs a short script over configured servers (`tools.mcp__<server>__<tool>(args)`), with `-s <server>` to pick servers.\n\n## Configure servers\n\n```bash\nmcpcat add docs https://example.com/mcp\nmcpcat add docs https://example.com/mcp -H 'Authorization: Bearer ${DOCS_TOKEN}'\nmcpcat list\nmcpcat rm docs\nmcpcat import ~/.cursor/mcp.json\nmcpcat login docs\n```\n\n`login` runs the OAuth sign-in for a server that needs one; `logout` removes its tokens.\n\n## Secrets: never values, always placeholders\n\n- **Never print, echo, log or paste a secret's value**, not in a command, a file, a message or your answer. The value must never reach the conversation.\n- **Use `${NAME}` placeholders in single quotes**: `-H 'Authorization: Bearer ${GH_TOKEN}'`. mcpcat expands them itself, so the value stays out of argv. In seven, `NAME` holds the vault's placeholder, and the vault fills in the value where a secret rule allows that host.\n- **Never put a token into `mcp.json`.** Write `${NAME}` there too; `mcpcat add` refuses a literal `Bearer \u2026` value.\n- `-v` traces show expanded headers as `[REDACTED]`. Don't try to work around redaction.\n- If a request needs a secret that isn't there, ask the user to add it to the vault; don't ask them to paste it.\n\n## Exit codes\n\n| Code | Meaning |\n|---|---|\n| 0 | Success |\n| 1 | The tool reported an error (`isError`), or `grep` found nothing |\n| 2 | Usage: bad flags, unknown tool, invalid arguments or config |\n| 3 | Connection, transport or protocol failure |\n| 4 | Authentication required or failed: run the `mcpcat login` the message names |\n| 5 | JSON-RPC error from the server |\n| 6 | The server asked for input and there is no terminal |\n| 124 | Timeout |\n| 130 | Interrupted |\n\n## In seven\n\n- Host servers on the user's machine are `http://host.slicc.internal:<port>`, through a linked slicc CLI.\n- Stdio servers must be kernel commands; `npx` needs the optional Node.js package.\n- The agent's own MCP tools come from `mcp.json`; `mcpcat add` is how to add a server to it.\n" : void 0;
|
|
16316
|
+
if (!skill) {
|
|
16317
|
+
await platform2.write(2, "mcpcat help: this build has no skill\n");
|
|
16318
|
+
return EXIT_USAGE;
|
|
16319
|
+
}
|
|
16320
|
+
await platform2.write(1, skill);
|
|
16321
|
+
return 0;
|
|
16322
|
+
}
|
|
16305
16323
|
async function runInner(platform2, version, args2) {
|
|
16306
16324
|
if (args2.includes("--version") || args2.includes("-V")) {
|
|
16307
16325
|
await platform2.write(1, `${version}
|
|
@@ -16309,8 +16327,7 @@ async function runInner(platform2, version, args2) {
|
|
|
16309
16327
|
return 0;
|
|
16310
16328
|
}
|
|
16311
16329
|
if (args2.includes("--help") || args2.includes("-h") || args2[0] === "help") {
|
|
16312
|
-
|
|
16313
|
-
return 0;
|
|
16330
|
+
return help(platform2, version, args2);
|
|
16314
16331
|
}
|
|
16315
16332
|
if (args2.length === 0) {
|
|
16316
16333
|
await platform2.write(1, helpText(version));
|
|
@@ -16975,5 +16992,5 @@ try {
|
|
|
16975
16992
|
});
|
|
16976
16993
|
} catch {
|
|
16977
16994
|
}
|
|
16978
|
-
var code = await run(platform, "0.
|
|
16995
|
+
var code = await run(platform, "0.17.1", args);
|
|
16979
16996
|
process.exit(code);
|
package/dist/kernel/mcpcat.mjs
CHANGED
|
@@ -10869,17 +10869,17 @@ function sanitizeName(raw) {
|
|
|
10869
10869
|
const cleaned = raw.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
10870
10870
|
return cleaned || "cli";
|
|
10871
10871
|
}
|
|
10872
|
-
function cliPrefix(
|
|
10872
|
+
function cliPrefix(help2, filePath, explicit) {
|
|
10873
10873
|
if (explicit) return sanitizeName(explicit);
|
|
10874
|
-
const fromUsage = usageProgramName(
|
|
10874
|
+
const fromUsage = usageProgramName(help2);
|
|
10875
10875
|
if (fromUsage) return sanitizeName(fromUsage);
|
|
10876
10876
|
const base = filePath.split("/").pop() ?? filePath;
|
|
10877
10877
|
return sanitizeName(base.replace(/\.jsh$/i, ""));
|
|
10878
10878
|
}
|
|
10879
|
-
function parseHelp(
|
|
10880
|
-
const block = commandsFromBlock(
|
|
10881
|
-
const program = usageProgramName(
|
|
10882
|
-
const commands = mergeCommandLists(block.commands, usageCommands(
|
|
10879
|
+
function parseHelp(help2) {
|
|
10880
|
+
const block = commandsFromBlock(help2);
|
|
10881
|
+
const program = usageProgramName(help2);
|
|
10882
|
+
const commands = mergeCommandLists(block.commands, usageCommands(help2, program));
|
|
10883
10883
|
return {
|
|
10884
10884
|
...program ? { program } : {},
|
|
10885
10885
|
commands: applyScopedFlags(commands, block.scoped),
|
|
@@ -11023,12 +11023,12 @@ function encodeFlag(flag, value) {
|
|
|
11023
11023
|
}
|
|
11024
11024
|
return { argv: [`--${flag.name}`, String(value)] };
|
|
11025
11025
|
}
|
|
11026
|
-
function commandsFromBlock(
|
|
11026
|
+
function commandsFromBlock(help2) {
|
|
11027
11027
|
let section = "none";
|
|
11028
11028
|
const commands = [];
|
|
11029
11029
|
const globalFlags = [];
|
|
11030
11030
|
const scoped = [];
|
|
11031
|
-
for (const rawLine of
|
|
11031
|
+
for (const rawLine of help2.split("\n")) {
|
|
11032
11032
|
const line = rawLine.replace(/\t/g, " ");
|
|
11033
11033
|
const header = classifyHeader(line);
|
|
11034
11034
|
if (header) {
|
|
@@ -11076,8 +11076,8 @@ function parseCommandLine(line) {
|
|
|
11076
11076
|
flags: spec.flags
|
|
11077
11077
|
};
|
|
11078
11078
|
}
|
|
11079
|
-
function usageProgramName(
|
|
11080
|
-
for (const raw of
|
|
11079
|
+
function usageProgramName(help2) {
|
|
11080
|
+
for (const raw of help2.split("\n")) {
|
|
11081
11081
|
if (!/^[\s>*]*usage:/i.test(raw)) continue;
|
|
11082
11082
|
const tokens = tokenize2(raw);
|
|
11083
11083
|
const name = tokens[0];
|
|
@@ -11085,9 +11085,9 @@ function usageProgramName(help) {
|
|
|
11085
11085
|
}
|
|
11086
11086
|
return void 0;
|
|
11087
11087
|
}
|
|
11088
|
-
function usageCommands(
|
|
11088
|
+
function usageCommands(help2, program) {
|
|
11089
11089
|
const commands = [];
|
|
11090
|
-
for (const raw of
|
|
11090
|
+
for (const raw of help2.split("\n")) {
|
|
11091
11091
|
const command = usageCommand(raw, program);
|
|
11092
11092
|
if (command) commands.push(command);
|
|
11093
11093
|
}
|
|
@@ -15053,11 +15053,15 @@ Client verbs:
|
|
|
15053
15053
|
tools <target> List tools
|
|
15054
15054
|
call <target> <tool> Call a tool
|
|
15055
15055
|
describe <target> [tool] Tool schema and help
|
|
15056
|
+
resources <target> List resources
|
|
15057
|
+
read <target> <uri> Read a resource
|
|
15058
|
+
prompts <target> List prompts
|
|
15059
|
+
prompt <target> <name> Get a prompt
|
|
15056
15060
|
|
|
15057
15061
|
Configuration verbs:
|
|
15058
15062
|
add <name> <url> Add a server to mcp.json
|
|
15059
|
-
add <name> --bridge --
|
|
15060
|
-
|
|
15063
|
+
add <name> --bridge -- <command> [args\u2026]
|
|
15064
|
+
Bridge a stdio server to HTTP (kernel only,
|
|
15061
15065
|
runs in foreground, does not survive reload)
|
|
15062
15066
|
rm <name> Remove a server
|
|
15063
15067
|
list List configured servers
|
|
@@ -15073,11 +15077,12 @@ Server and code verbs:
|
|
|
15073
15077
|
grep <pattern> [target\u2026] Search tools by name or description
|
|
15074
15078
|
discover <url> Find MCP servers at a URL
|
|
15075
15079
|
inbox [--json] List discovery candidates
|
|
15076
|
-
help
|
|
15080
|
+
help Show this help
|
|
15081
|
+
help --skill Print the agent skill (SKILL.md) for this version
|
|
15077
15082
|
|
|
15078
15083
|
Options:
|
|
15079
15084
|
--json JSON output
|
|
15080
|
-
-H, --header 'N: V'
|
|
15085
|
+
-H, --header 'N: V' Extra header
|
|
15081
15086
|
-d <json> Tool arguments as JSON (or @file, @- for stdin)
|
|
15082
15087
|
-m, --timeout <s> Request timeout (default 60)
|
|
15083
15088
|
--memory <MiB> Code mode memory limit (default 256, max 1024)
|
|
@@ -15091,7 +15096,7 @@ Options:
|
|
|
15091
15096
|
-s, --server <name> Server target (code, repeatable)
|
|
15092
15097
|
--allow-shell Enable shell() global (code)
|
|
15093
15098
|
-v, --verbose Trace on stderr
|
|
15094
|
-
|
|
15099
|
+
--silent Suppress progress
|
|
15095
15100
|
-o, --output <path> Write result to file
|
|
15096
15101
|
-V, --version Print version
|
|
15097
15102
|
-h, --help Show this help
|
|
@@ -15923,6 +15928,19 @@ async function run(platform2, version, args) {
|
|
|
15923
15928
|
throw err;
|
|
15924
15929
|
}
|
|
15925
15930
|
}
|
|
15931
|
+
async function help(platform2, version, args) {
|
|
15932
|
+
if (args[0] !== "help" || !args.includes("--skill")) {
|
|
15933
|
+
await platform2.write(1, helpText(version));
|
|
15934
|
+
return 0;
|
|
15935
|
+
}
|
|
15936
|
+
const skill = true ? "---\nname: mcpcat\ndescription: Call Model Context Protocol (MCP) servers from the shell with mcpcat, a curl for MCP. List and call tools, read resources and prompts, keep servers in mcp.json, and send secrets as ${VAR} placeholders that the vault fills in, never as values. Use when a task needs a tool from an MCP server over HTTP or a stdio command.\n---\n\n# mcpcat\n\n`mcpcat` talks to MCP servers over Streamable HTTP or stdio, one shell command per request. Options go before the target.\n\n## Targets\n\n- A URL: `https://example.com/mcp`.\n- A stdio command: `stdio:<command> [args\u2026]`, as one argument (`'stdio:my-server --flag'`).\n- A name from `mcp.json`, the same `~/.pi/agent/mcp.json` the agent reads.\n\n## Discover and call\n\n```bash\nmcpcat info https://example.com/mcp\nmcpcat tools https://example.com/mcp\nmcpcat describe https://example.com/mcp search\nmcpcat call https://example.com/mcp search --q oauth --limit 5\nmcpcat call docs search -d '{\"q\":\"oauth\",\"limit\":5}'\nmcpcat --json call docs search --q oauth\nmcpcat grep issue\n```\n\n- Tool arguments are `--name value` flags, coerced to the schema's types, or one JSON object with `-d` (`-d @file`, `-d @-` for stdin).\n- `describe` prints a tool's schema and help; `--schema` prints the raw JSON Schema, `--ts` TypeScript declarations.\n- `--json` prints deterministic JSON for scripts; human output otherwise. `-m <s>` sets the timeout (default 60 s).\n- `code -e '<js>'` runs a short script over configured servers (`tools.mcp__<server>__<tool>(args)`), with `-s <server>` to pick servers.\n\n## Configure servers\n\n```bash\nmcpcat add docs https://example.com/mcp\nmcpcat add docs https://example.com/mcp -H 'Authorization: Bearer ${DOCS_TOKEN}'\nmcpcat list\nmcpcat rm docs\nmcpcat import ~/.cursor/mcp.json\nmcpcat login docs\n```\n\n`login` runs the OAuth sign-in for a server that needs one; `logout` removes its tokens.\n\n## Secrets: never values, always placeholders\n\n- **Never print, echo, log or paste a secret's value**, not in a command, a file, a message or your answer. The value must never reach the conversation.\n- **Use `${NAME}` placeholders in single quotes**: `-H 'Authorization: Bearer ${GH_TOKEN}'`. mcpcat expands them itself, so the value stays out of argv. In seven, `NAME` holds the vault's placeholder, and the vault fills in the value where a secret rule allows that host.\n- **Never put a token into `mcp.json`.** Write `${NAME}` there too; `mcpcat add` refuses a literal `Bearer \u2026` value.\n- `-v` traces show expanded headers as `[REDACTED]`. Don't try to work around redaction.\n- If a request needs a secret that isn't there, ask the user to add it to the vault; don't ask them to paste it.\n\n## Exit codes\n\n| Code | Meaning |\n|---|---|\n| 0 | Success |\n| 1 | The tool reported an error (`isError`), or `grep` found nothing |\n| 2 | Usage: bad flags, unknown tool, invalid arguments or config |\n| 3 | Connection, transport or protocol failure |\n| 4 | Authentication required or failed: run the `mcpcat login` the message names |\n| 5 | JSON-RPC error from the server |\n| 6 | The server asked for input and there is no terminal |\n| 124 | Timeout |\n| 130 | Interrupted |\n\n## In seven\n\n- Host servers on the user's machine are `http://host.slicc.internal:<port>`, through a linked slicc CLI.\n- Stdio servers must be kernel commands; `npx` needs the optional Node.js package.\n- The agent's own MCP tools come from `mcp.json`; `mcpcat add` is how to add a server to it.\n" : void 0;
|
|
15937
|
+
if (!skill) {
|
|
15938
|
+
await platform2.write(2, "mcpcat help: this build has no skill\n");
|
|
15939
|
+
return EXIT_USAGE;
|
|
15940
|
+
}
|
|
15941
|
+
await platform2.write(1, skill);
|
|
15942
|
+
return 0;
|
|
15943
|
+
}
|
|
15926
15944
|
async function runInner(platform2, version, args) {
|
|
15927
15945
|
if (args.includes("--version") || args.includes("-V")) {
|
|
15928
15946
|
await platform2.write(1, `${version}
|
|
@@ -15930,8 +15948,7 @@ async function runInner(platform2, version, args) {
|
|
|
15930
15948
|
return 0;
|
|
15931
15949
|
}
|
|
15932
15950
|
if (args.includes("--help") || args.includes("-h") || args[0] === "help") {
|
|
15933
|
-
|
|
15934
|
-
return 0;
|
|
15951
|
+
return help(platform2, version, args);
|
|
15935
15952
|
}
|
|
15936
15953
|
if (args.length === 0) {
|
|
15937
15954
|
await platform2.write(1, helpText(version));
|
|
@@ -16697,7 +16714,7 @@ async function main(ctx) {
|
|
|
16697
16714
|
packageDir: "/node_modules/@ai-ecoverse/mcpcat"
|
|
16698
16715
|
});
|
|
16699
16716
|
const args = ctx.argv.slice(1);
|
|
16700
|
-
return run(platform2, "0.
|
|
16717
|
+
return run(platform2, "0.17.1", args);
|
|
16701
16718
|
}
|
|
16702
16719
|
export {
|
|
16703
16720
|
main as default
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mcpcat
|
|
3
|
+
description: Call Model Context Protocol (MCP) servers from the shell with mcpcat, a curl for MCP. List and call tools, read resources and prompts, keep servers in mcp.json, and send secrets as ${VAR} placeholders that the vault fills in, never as values. Use when a task needs a tool from an MCP server over HTTP or a stdio command.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# mcpcat
|
|
7
|
+
|
|
8
|
+
`mcpcat` talks to MCP servers over Streamable HTTP or stdio, one shell command per request. Options go before the target.
|
|
9
|
+
|
|
10
|
+
## Targets
|
|
11
|
+
|
|
12
|
+
- A URL: `https://example.com/mcp`.
|
|
13
|
+
- A stdio command: `stdio:<command> [args…]`, as one argument (`'stdio:my-server --flag'`).
|
|
14
|
+
- A name from `mcp.json`, the same `~/.pi/agent/mcp.json` the agent reads.
|
|
15
|
+
|
|
16
|
+
## Discover and call
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
mcpcat info https://example.com/mcp
|
|
20
|
+
mcpcat tools https://example.com/mcp
|
|
21
|
+
mcpcat describe https://example.com/mcp search
|
|
22
|
+
mcpcat call https://example.com/mcp search --q oauth --limit 5
|
|
23
|
+
mcpcat call docs search -d '{"q":"oauth","limit":5}'
|
|
24
|
+
mcpcat --json call docs search --q oauth
|
|
25
|
+
mcpcat grep issue
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
- Tool arguments are `--name value` flags, coerced to the schema's types, or one JSON object with `-d` (`-d @file`, `-d @-` for stdin).
|
|
29
|
+
- `describe` prints a tool's schema and help; `--schema` prints the raw JSON Schema, `--ts` TypeScript declarations.
|
|
30
|
+
- `--json` prints deterministic JSON for scripts; human output otherwise. `-m <s>` sets the timeout (default 60 s).
|
|
31
|
+
- `code -e '<js>'` runs a short script over configured servers (`tools.mcp__<server>__<tool>(args)`), with `-s <server>` to pick servers.
|
|
32
|
+
|
|
33
|
+
## Configure servers
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
mcpcat add docs https://example.com/mcp
|
|
37
|
+
mcpcat add docs https://example.com/mcp -H 'Authorization: Bearer ${DOCS_TOKEN}'
|
|
38
|
+
mcpcat list
|
|
39
|
+
mcpcat rm docs
|
|
40
|
+
mcpcat import ~/.cursor/mcp.json
|
|
41
|
+
mcpcat login docs
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
`login` runs the OAuth sign-in for a server that needs one; `logout` removes its tokens.
|
|
45
|
+
|
|
46
|
+
## Secrets: never values, always placeholders
|
|
47
|
+
|
|
48
|
+
- **Never print, echo, log or paste a secret's value**, not in a command, a file, a message or your answer. The value must never reach the conversation.
|
|
49
|
+
- **Use `${NAME}` placeholders in single quotes**: `-H 'Authorization: Bearer ${GH_TOKEN}'`. mcpcat expands them itself, so the value stays out of argv. In seven, `NAME` holds the vault's placeholder, and the vault fills in the value where a secret rule allows that host.
|
|
50
|
+
- **Never put a token into `mcp.json`.** Write `${NAME}` there too; `mcpcat add` refuses a literal `Bearer …` value.
|
|
51
|
+
- `-v` traces show expanded headers as `[REDACTED]`. Don't try to work around redaction.
|
|
52
|
+
- If a request needs a secret that isn't there, ask the user to add it to the vault; don't ask them to paste it.
|
|
53
|
+
|
|
54
|
+
## Exit codes
|
|
55
|
+
|
|
56
|
+
| Code | Meaning |
|
|
57
|
+
|---|---|
|
|
58
|
+
| 0 | Success |
|
|
59
|
+
| 1 | The tool reported an error (`isError`), or `grep` found nothing |
|
|
60
|
+
| 2 | Usage: bad flags, unknown tool, invalid arguments or config |
|
|
61
|
+
| 3 | Connection, transport or protocol failure |
|
|
62
|
+
| 4 | Authentication required or failed: run the `mcpcat login` the message names |
|
|
63
|
+
| 5 | JSON-RPC error from the server |
|
|
64
|
+
| 6 | The server asked for input and there is no terminal |
|
|
65
|
+
| 124 | Timeout |
|
|
66
|
+
| 130 | Interrupted |
|
|
67
|
+
|
|
68
|
+
## In seven
|
|
69
|
+
|
|
70
|
+
- Host servers on the user's machine are `http://host.slicc.internal:<port>`, through a linked slicc CLI.
|
|
71
|
+
- Stdio servers must be kernel commands; `npx` needs the optional Node.js package.
|
|
72
|
+
- The agent's own MCP tools come from `mcp.json`; `mcpcat add` is how to add a server to it.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-ecoverse/mcpcat",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.1",
|
|
4
4
|
"description": "A curl for the Model Context Protocol",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -34,6 +34,11 @@
|
|
|
34
34
|
"README.md",
|
|
35
35
|
"dist"
|
|
36
36
|
],
|
|
37
|
+
"pi": {
|
|
38
|
+
"skills": [
|
|
39
|
+
"dist/skills"
|
|
40
|
+
]
|
|
41
|
+
},
|
|
37
42
|
"slicc": {
|
|
38
43
|
"commands": {
|
|
39
44
|
"mcpcat": {
|
|
@@ -56,15 +61,15 @@
|
|
|
56
61
|
"trust:dry": "npx fledgling sync --dry-run -y --skip-publish"
|
|
57
62
|
},
|
|
58
63
|
"devDependencies": {
|
|
59
|
-
"@ai-ecoverse/slicc-bios": "1.
|
|
60
|
-
"@ai-ecoverse/slicc-kernel": "1.
|
|
61
|
-
"@ai-ecoverse/slicc-shared-web": "1.
|
|
64
|
+
"@ai-ecoverse/slicc-bios": "1.30.4",
|
|
65
|
+
"@ai-ecoverse/slicc-kernel": "1.133.3",
|
|
66
|
+
"@ai-ecoverse/slicc-shared-web": "1.23.2",
|
|
62
67
|
"@biomejs/biome": "2.5.15",
|
|
63
68
|
"@earendil-works/pi-codemode": "1.2.0",
|
|
64
69
|
"@earendil-works/pi-mcp": "1.2.0",
|
|
65
70
|
"@types/node": "26.6.5",
|
|
66
71
|
"esbuild": "0.28.2",
|
|
67
|
-
"kernel-cert-new": "npm:@ai-ecoverse/slicc-kernel@1.
|
|
72
|
+
"kernel-cert-new": "npm:@ai-ecoverse/slicc-kernel@1.133.3",
|
|
68
73
|
"monocart-coverage-reports": "2.13.2",
|
|
69
74
|
"proper-lockfile": "4.1.2",
|
|
70
75
|
"quickjs-wasi": "3.6.2",
|