rijks-mcp is a thin command-line client over the
rijksmuseum-mcp+
server. It speaks the same tools an AI assistant uses — so a shell command returns
identical results to a model — but emits clean JSON to stdout, which means
the whole 834,000-object collection drops straight into pipes, scripts, cron jobs, and bash-driven
agents. No API key, no glue code, no second copy of the query logic.
--fieldsFor an AI assistant wired directly to the server, MCP is the better interface — typed schemas, native discovery, no shell-quoting. The CLI earns its place precisely where MCP can't reach: the shell.
A generic coding agent (or a Claude Code session) with shell access but no MCP wiring to this server can still query the collection — one command, structured JSON back.
When … | jq | sort | uniq -c beats a sequence of discrete tool calls,
JSONL on stdout composes with every tool you already use.
Reproducible, replayable invocations with documented exit codes — the building block of scheduled exports, smoke tests, and incremental sync.
The CLI is a client, not a second implementation. It carries no domain logic: it
resolves a short verb to a tool, forwards typed arguments, and prints the result. One
invoke() seam, two transports.
rijks-mcp search …
verb→tool, arg coercion from the live schema, JSON out
--http to a warm server, or spawn dist/index.js
the exact same code paths the LLM hits
vocab + embeddings, locally
Every example on this page calls rijks-mcp — the
bin declared in package.json. Link it onto your PATH once and it runs from
any directory; npm run cli -- … and node scripts/cli.mjs …
drive the exact same client if you'd rather not install anything.
--http — warm server (the default for repeated use)npm run serve or the Railway deployment.Lists print as JSONL — one JSON object per line — on stdout.
Counts, pagination hints and warnings go to stderr. That split is deliberate: the data
channel stays clean for jq, while progress never corrupts a pipe.
JSONL rows on stdout
pluck & transform fields
group & count
no bespoke code
A real one-liner — how Rembrandt's dated works in the collection
distribute by year (his authority vocabId is 2103429):
Swap uniq -c for jq -s, pipe into gnuplot, or
redirect to a file — the producer never changes.
--fields projectionFor a human, output size is cosmetic. For an LLM agent reading the result back, every
byte is a token. --fields a,b,c keeps only the keys you asked for — on each row of
a list, so the saving compounds across hundreds of results.
Measured against the live database. A 50-row search projected to two fields is
roughly 50 × 48 B instead of 50 × 173 B — before any
jq even runs. Need everything? --json prints the full payload verbatim.
Because each invocation is a plain command with a clean exit code, ordinary shell constructs — variables, loops, redirects — turn the collection into a scriptable resource.
Look up an artist's authority ID, then query by it — spelling- and language-proof.
Stream object numbers into a loop that pulls a projected detail record for each.
stats emits JSONL entries; jq turns them into a spreadsheet column.
Walk a change feed page by page. --json surfaces the resumptionToken
alongside the records, so the loop is self-contained.
Exit 0 ok · 1 tool/connection error · 2 usage error
— branchable like any Unix tool.
JSONL out, diagnostics on stderr, exit codes in — a well-behaved Unix
citizen that drops into any pipeline.
--fields projection and JSONL keep payloads small — up to 98% smaller —
which is real money for an LLM consumer.
tools --json bootstraps capabilities; --show-call dry-runs the
argument mapping; help is generated from the live schema, so it never drifts.
No reimplemented query logic: one server, two front doors. The CLI can't disagree with the model, and it doubles as a regression harness.
Point it at a warm server for instant calls, or run cold in ~1 s for a vocab one-off — no 13 s warm-up tax on a throwaway query.
A single script reusing the dependencies the server already ships. npm run cli,
the rijks-mcp bin, or node scripts/cli.mjs.