Commands
Three of them, and the one you will type least is the one a host runs.
The invocation a host uses
pmcp |
run the MCP server on stdio; the same as serve
|
pmcp --root ./node_modules |
a leading option with no command is still a server, because hosts pass options without one |
An unrecognised command does not fall through to a server the caller did not ask for. Only an empty argument list, or one that begins with an option, takes that path.
serve
pmcp serve [--root <dir>] [--scope <prefix>]
Reachable by name so it appears in help. It writes nothing to standard output: from the moment it starts, standard output is the protocol, and a diagnostic printed there corrupts the stream.
list
pmcp list [--root <dir>] [--scope <prefix>] [--json]
The skills discovered in node_modules, as two aligned
columns, or as JSON for a script. This is the command that answers
"would the server see anything from here".
An empty catalog is a finding rather than a success. It exits 1 and names the roots it walked, because a project that installed this and sees nothing has a misconfiguration worth an exit code.
index
pmcp index [--root <dir>] [--model <id>] [--json]
Encodes each description once so ranking can be semantic, and stores the vectors beside the tree they describe, so two projects never share one index. Only what changed is re-encoded; an unchanged description produces the same vector at the same cost as the first time.
Vectors written by one model are never scored against another, so
--model is part of the key rather than a preference.
Without the optional peer installed it exits 3 and
tells you ranking stays lexical, which is the default and not a failure.
A partial index is reported rather than called success: it exits 1 and says how many of the catalog it covered, because ranking needs a vector for every entry and falls back to lexical without one.
The written skills
The commands above read what is already installed and need no account. The ones below reach the catalogue we write; reading it is free, and installing from it needs a subscription.
All four take --project <dir>,
--api <origin>, --json, and
--catalog <file>, which reads a saved response so the
command runs with no network at all.
available
pmcp available [--project <dir>] [--catalog <file>] [--json]
Which written skills match the dependencies this project already has.
It reads your package.json to decide what matches and does
not upload it. No account needed.
preview
pmcp preview <skill-package> [--json]
A skill's contents and one complete example, before paying for it. The sample is taken from the skill's own bytes rather than described, so it cannot drift from what you would receive. No account needed.
login · logout · whoami
pmcp login [--issuer <url>]
pmcp logout
pmcp whoami [--json]
login signs this machine in without a browser: it prints a
code and a URL to open anywhere. The credential is kept outside the
project, so it cannot be committed or copied into an image.
whoami answers on stdout, so a script can read it.
install · sync
pmcp install <skill-package>... | --all [--dry-run | --yes]
pmcp sync [--project <dir>] [--dry-run | --yes]
install adds skill packages as dev dependencies with your
own package manager, and what lands is checked against the digest the
catalogue published — not its name and version, which any registry can
claim. sync updates the ones already declared and removes
nothing else.
Both show the project and lockfile changes and stop:
--yes approves them, --dry-run prints the plan
and changes nothing.
Options
| Option | Where | Effect |
|---|---|---|
--root <dir> |
all three |
a node_modules directory to walk; repeatable, and
the first one wins on a duplicate package
|
--scope <prefix> |
all three | only packages whose names start with this; repeatable, matched with a plain prefix test |
--model <id> |
index |
the encoder to use; rows from another model are never scored |
--json |
list, index |
emit JSON on standard output rather than a table or a summary |
--help, -h |
anywhere | the help for that command, generated from the command itself |
--version |
on its own | the version, on standard output |
Options are parsed against the command's own list, so an unknown flag is an error rather than a silently ignored typo. Giving a non-repeatable option twice, or leaving off a value it needs, is the same error.
Exit codes
| Code | Means | For example |
|---|---|---|
0 |
it did what you asked | skills listed, or the whole catalog indexed |
1 |
it ran and found a problem | no skills under those roots, a partial index, or no SQLite in this runtime to store vectors in |
2 |
the arguments were unusable | an unknown command, an unknown option, a missing value |
3 |
an optional dependency is missing |
index without
@huggingface/transformers
|
The distinction between 1 and 3 is worth wiring into a script: 1 says the project is not what you expected, and 3 says the machine is missing something optional and the server still works without it.
Data on stdout, everything else on stderr. The JSON
from --json and the string from --version go
to standard output so they can be piped. Tables, help and messages go
to standard error, which is what keeps a pipeline's input clean.
Runtime
- Node 22 or later, or Bun 1.3 or later.
-
Runtime dependencies:
@modelcontextprotocol/serverandzod, both MIT. - Vector storage uses whichever SQLite the running binary already has. Neither is a dependency; without one, the server still runs and ranking falls back.