Skip to content

Commands

Every command is a thin wrapper over one named GraphQL operation. This page documents each command exactly as the package implements it: synopsis, flags, the operation it maps to, and output examples.

These apply to every command and may appear before or after the command path:

FlagEffect
--jsonEmit the GraphQL data payload, pretty-printed.
--jsonlEmit one JSON object per line (for the streamable list of a result).
--humanForce the human table/record view (e.g. into a pipe).
--no-colorDisable ANSI color.
--quietSuppress non-error stderr chrome.
--profile <name>Select the config profile.
--endpoint <url>Override the profile’s api_url.
--help, -hPrint top-level usage.
--version, -vPrint the CLI version.

Format precedence is --jsonl > --json > --human; with none given, the CLI emits human at a TTY and json when piped. See Scripting.


Covered in full on Install & Authentication. In brief: login stores a token (verifying it via query stats), logout forgets it, and whoami prints identity + corpus size (also via query stats). None maps to a dedicated GraphQL auth operation — auth is Clerk; whoami reuses stats.


The highest-leverage group. Each is --json-capable, and the human view echoes the [doc:ID] handle convention the Core tools use, so output reads like what an agent sees.

trove search <query…> [--source <name|id>] [--source-type <t>]
[--author <a>] [--after <date>] [--before <date>] [--type <ct>]
[--tag <t>]… [--feed <id>] [--limit <n> | -l <n>]

Semantic top-K search → query search. Positional words are joined into the query string. --source accepts a name or id; a name is resolved to its id client-side via sources (an id-shaped value, src_…, is used as-is). --tag is repeatable. --type is upper-cased to the ContentType enum.

$ trove search "database indexing" --source "arXiv Papers" --limit 5
SCORE TITLE SOURCE HANDLE
0.847 Learned Indexes for Range Queries arXiv Papers [doc:d_8f2a]
0.812 B-Tree vs LSM under Write Amplification arXiv Papers [doc:d_31c0]
2 match(es) in 41ms

--json emits the SearchResults object ({ totalMatches, queryTimeMs, results[] }); --jsonl emits one results[] entry per line.

trove discover <topic…> [--source <name|id>] [--source-type <t>]
[--feed <id>] [--limit <n> | -l <n>]

Broad thematic browse → query discover. Only source/feed filters take effect — the wider search filters (--author, --after, --type, --tag) are accepted but ignored. Output shape matches search.

trove recent [--source <name|id>] [--author <a>] [--since <date>]
[--limit <n> | -l <n>]

Chronological by indexedAtquery recent. Renders a TITLE / AUTHOR / SOURCE / HANDLE table. --json/--jsonl emit the document array.

trove get <doc-id…> [--offset-words <n>] [--max-words <n>]

Fetch one or more documents with full text → query document(id), once per id. A missing document fails with exit code 5. The human view prints a record header (title, author, source, url, handle) followed by the full text — built for piping into $EDITOR/less:

Terminal window
trove get d_8f2a | $EDITOR -

In --json, a single id emits one document object; multiple ids emit an array. In --jsonl, each document is one line.

Word paging. --offset-words / --max-words page a long transcript client-side: the CLI fetches the document’s fullText, slices it by words, and prints just that window. The human view writes the slice to stdout plus a continuation hint (the next offset) to stderr; --json returns { id, offsetWords, returnedWords, totalWords, nextOffset, text }. Paging applies to a single document id at a time.

Terminal window
trove get d_8f2a --offset-words 0 --max-words 800 # first 800 words
trove get d_8f2a --offset-words 800 --max-words 800 # next page (use nextOffset)
trove list [--source <name|id>] [--type <ct>] [--tag <t>]… [--search <text>]
[--sort <field>] [--order <asc|desc>] [--limit <n> | -l <n>] [--offset <n>]

The exhaustive lister with an authoritative totalCountquery documents. This is the right tool for “all X”, counts, and pagination — search is semantic top-K. --sort maps to DocumentSortField and --order to SortOrder (both upper-cased); --type upper-cases to ContentType.

$ trove list --source "The Guardian" --json | jq '.totalCount'
675

The human view appends a <N> total footer (with (more available) when hasMore). --jsonl emits the nodes[] one per line.

trove sources [--type <t>] [--status <s>]

List your sources → query sources. --status upper-cases to SourceStatus. Renders an ID / NAME / TYPE / STATUS / DOCS table.

trove source <id|name> [--feeds] [--sync-runs]

One source with feeds, top authors, date range, and recent sync runs → query source(id). The name/id argument is resolved via sources. A missing source fails with exit code 5. (--feeds/--sync-runs are accepted; the operation already requests feeds and sync runs.)

trove stats

Corpus aggregates → query stats: total documents, total/active sources, breakdowns by source type and content type, and recent sync runs.


trove save (--text <text> | --text - | --url <url>) [--title <t>]
[--tag <t>]… [--source <s>]

Manual capture of free text or a URL into the Manual Saves source → mutation saveDocument. One of --text or --url is required. --text - reads the body from stdin, so:

Terminal window
cat notes.md | trove save --text - --title "Meeting notes"
trove save --url https://example.com/post --tag reading --tag ai

Here --source is free-form attribution text (e.g. "conversation with Claude"), not a source id. On success the human view writes ✓ saved [doc:…] to stderr and the record (id, title, url, tags) to stdout. --json/--jsonl emit the saved Document.

trove ingest --source <id> --feed <id> --documents <file.jsonl|->
[--cursor <new>] [--cursor-before <expected>]

The lower-level ingest boundary for source-adapter authors and bulk scripts → mutation ingestDocuments. --documents accepts a JSONL stream or a JSON array of IngestDocumentInput; - reads from stdin.

It honors the cursor compare-and-swap: pass --cursor (the new watermark) and --cursor-before (the expected current value). On a CAS rejection the CLI exits with code 8 (retryable) so a script can re-read IngestResult.cursor and retry. Re-sent documents are reported as documentsSkipped, not errors.

Terminal window
generate-docs | trove ingest --source src_123 --feed feed_456 --documents -

The human view prints indexed, skipped, transcriptions queued, cursor, and errors. --json emits the full IngestResult; --jsonl emits the per-document errors[] one per line.


The CLI is the local dev toolchain for source adapters. A source adapter is a folder with manifest.json + index.ts (a defineSource({ sync }) export against @ontrove/sdk). Locally, the CLI runs the adapter on your machine — dev/test run sync(ctx) (transpiled with esbuild) and never upload; sync runs it then pushes via ingestDocuments. In production, most sources run in Trove’s cloud; the ones that need a browser or local files run on the Mac app.

trove source init <name>

Scaffold <name>/manifest.json + <name>/index.ts (a defineSource stub with a sample sync). No GraphQL.

trove source dev [path] [--config <file.json>] [--cursor <json>]

Load the source adapter’s index.ts (esbuild → @ontrove/sdk’s runSource), run sync(ctx), and print the produced documents (human table or --json/--jsonl). --config supplies the source’s preference values; --cursor supplies the starting watermark. Nothing is uploaded.

trove source test [path] [--fixtures <file.json>] [--config <file.json>]

Run sync(ctx) against a fixtures file (a JSON map of request URL → canned response body, default fixtures/responses.json) so the run is deterministic and offline, then assert the document shape. Exits 2 if assertions fail.

trove source validate [path]

Validate manifest.json with @ontrove/sdk’s validateSourceManifest — required fields, the id/version patterns, and the credential-key lint (a source’s config must hold preferences only, never credentials). Exits 2 on an invalid manifest.

trove source sync [path] [--source <name|id>] [--feed <key>] [--create] [--config <file.json>]

The real upload: run sync(ctx) locally, then push the documents via mutation ingestDocuments with cursor compare-and-swap. The CLI reads the target feed’s current cursor as cursorBefore and advances it with the watermark sync returns. --source (defaults to the manifest name) and --feed (defaults to default) select the target; --create creates the source/feed (via createSource/addFeed) if they don’t exist yet.


Toolkit authoring & deployment (@ontrove/mcp)

Section titled “Toolkit authoring & deployment (@ontrove/mcp)”

mcp init/mcp dev are the local toolchain for toolkits (manifest.json + server.ts exporting defineMcpServer({ tools }) — every toolkit runs as a full MCP server on Trove’s cloud). The remaining commands wrap the toolkit control-plane operations; the deployed runtime is PROPOSED, and they ship with the control plane. A toolkit argument is a slug or id, resolved via mcpServers.

trove mcp init <name>

Scaffold <name>/manifest.json + <name>/server.ts (a defineMcpServer stub with a sample tool, annotations, and an output schema). No GraphQL.

trove mcp dev [path] [--port <n>] [--once]

Bundle server.ts (esbuild), wrap it in @ontrove/mcp’s runtime fetch handler, and serve it over http://127.0.0.1:<port> (default 8788) so a client can connect: GET returns tools/list, POST runs a tool. Prints the local URL and the tool list; ctx.secret/ctx.trove resolve against the configured endpoint. Runs until interrupted (--once serves and returns, used in tests).

trove mcp ls

List your toolkits → query mcpServers. Renders a SLUG / NAME / STATUS / VIS / TOOLS / ACTIVE table (ACTIVE is the active deployment’s version).

trove mcp deploy [--dir <path>] [--name <name>] [--slug <slug>]
trove deploy [--dir …] [--name …] [--slug …]

Register/version a toolkit → mutation deployServer. Reads manifest.json from --dir (default .), derives name/slug from the manifest (slug falls back to the manifest id) unless overridden by --name/--slug, bundles server.ts locally (esbuild — a Bundling … (esbuild)… line is written to stderr), and registers the deployment. On success it prints the namespaced tool names ({slug}__{tool}) to stdout and a ✓ deployed … line to stderr. --json emits the McpDeployment. Missing manifest.json is a usage error (exit 2). See Toolkits — Deploying.

trove mcp pause <toolkit>
trove mcp resume <toolkit>
trove mcp rm <toolkit>

Lifecycle, each over a single id → mutation pauseServer, mutation resumeServer, mutation deleteServer (a soft-delete). The human view writes ✓ paused|resumed|deleted <name> to stderr; --json emits the returned McpServer.

trove mcp rollback <toolkit> <deploymentId>

Repoint a toolkit to a prior deployment → mutation rollbackServer. Both positionals are required. The human view writes ✓ rolled back <name> → <version> to stderr.

trove mcp logs <toolkit>

There is no logs GraphQL operation: per-script logs are produced by the deployed toolkit runtime, not the GraphQL API. Rather than invent a fake op, this command explains where logs live (the deployed endpoint, once the runtime ships) and exits 0. --json returns { server, available: false, reason }.

trove secret set <toolkit> <name> (--value <v> | --from-file <path> | --from-stdin)

Seal a secret into the encrypted vault → mutation setServerSecret. The value comes from --value, --from-file <path>, or --from-stdin so it need never land in argv history; it is stored server-side in the vault, never in D1. The human view writes ✓ secret '<name>' sealed for <toolkit> to stderr.

trove secret ls <toolkit>

List a toolkit’s declared secret names → backed by query mcpServers (the secrets field). Values are unreadable by design — only the names are ever returned. The human view prints one name per line (or notes when the toolkit declares none); --json emits { server, secrets } and --jsonl emits one name per line.


trove gql <file|-> [--variables <json|->]

Run a raw, user-supplied GraphQL document over the same endpoint with the same token. The document is read from a file path or stdin (-); --variables takes inline JSON or stdin. Unlike the wrapped commands, gql emits the full { data, errors } envelope (so scripts can branch on .errors) and exits 7 if errors[] is non-empty, 0 otherwise. It is the same authz the server enforces for every client — there is nothing reachable here that the token could not do with curl.

Terminal window
echo '{ stats { totalDocuments } }' | trove gql -
trove gql query.graphql --variables '{"id":"d_8f2a"}'

This table is the contract: every command resolves to a named operation in schema.graphql (or a marked local step). It is a strict subset of the published GraphQL surface — the CLI never exceeds the API, it only makes a slice of it ergonomic.

CommandGraphQL operationKind
login / logout / whoami— (Clerk; whoami calls query stats)Auth
search <query>query searchRead
discover <topic>query discoverRead
recentquery recentRead
get <id…>query document(id) (once per id)Read
listquery documentsRead
sourcesquery sourcesRead
source <id|name>query source(id)Read
statsquery statsRead
savemutation saveDocumentWrite
ingestmutation ingestDocuments (cursor CAS)Write
source init— (scaffold @ontrove/sdk project)Local
source dev— (local sync(ctx) via esbuild)Local
source test— (local fixtures assertion)Local
source validate— (validateSourceManifest)Local
source syncmutation ingestDocuments (+ createSource/addFeed w/ --create)Write
mcp init— (scaffold @ontrove/mcp project)Local
mcp dev— (local server over 127.0.0.1)Local
mcp logs— (no log op; explains the deployed-runtime gap)Info
mcp lsquery mcpServersRead · PROPOSED
mcp deploy / deploymutation deployServerWrite · PROPOSED
mcp pausemutation pauseServerWrite · PROPOSED
mcp resumemutation resumeServerWrite · PROPOSED
mcp rollbackmutation rollbackServerWrite · PROPOSED
mcp rmmutation deleteServer (soft-delete)Write · PROPOSED
secret setmutation setServerSecretWrite · PROPOSED
secret lsquery mcpServers (secrets — names only)Read · PROPOSED
gql <file|->arbitrary, user-suppliedEscape hatch