# Tool and command reference

The same tools exist in three places with the same receipts: the hosted MCP endpoint (`https://sources-api.askshepherd.ai/mcp`), the CLI's stdio
MCP server (`shepherd mcp`) and the CLI commands. Paths start with `$SOURCES` or `$SCRATCH`; `/` lists both roots.

## Read and search

| MCP tool | Parameters | CLI |
| --- | --- | --- |
| `ls` | path, limit (≤2000), after | `shepherd ls PATH [--limit N] [--after NAME]` |
| `read` | path, offset (1-based line), limit (lines) — or byteOffset, byteLimit (≤51200; base64 when not text). PNG/JPEG/GIF/WebP without offsets: an image block | `shepherd read PATH [--offset L --limit N \| --byte-offset B --byte-limit N]`; whole file: `shepherd cat PATH`, download: `shepherd get PATH [FILE]` |
| `find` | path, pattern (glob on names, or relative paths when it has `/`), limit, after | `shepherd find [PATH] PATTERN [--limit N] [--after P]` |
| `grep` | path, pattern (JavaScript regex per line), literal, ignoreCase, glob, context (0–20), limit, after | `shepherd grep PATTERN [PATH] [-F] [-i] [-C N] [--glob G] [--limit N] [--after P]` |
| `sources_status` | provider | `shepherd status [--provider P]` |
| `sources_history` | path, limit, before, versionId, byteOffset, byteLimit | `shepherd history PATH [--version-id V] [--limit N]`; `shepherd cat PATH --version-id V` |

Receipts carry `source` (versionId, sha256, capturedAt, sourceUpdatedAt, coverage) for `$SOURCES` files.
Continuation: `nextAfter` (ls/find/grep), `nextOffset` / `nextByteOffset` (read), `nextBefore` (history). Line reads
work at any depth of a file; a line longer than one answer comes back with `lineCut` (`restByteOffset` = where the rest
starts). In a very large file a line read scans at most 256 MiB per call; past that it answers `linesKnownThrough` and
`nextByteOffset`. A grep line longer than 500 characters is clipped with its full length in `lineChars`.
`truncatedReason`: `limit` = your limit; `walk_limit` = 200,000 entries walked; `scan_limit` = 32 MiB / 5,000 files
loaded by one grep. Grep never line-searches binary files or text files over 4 MiB (`skipped`).

## Search by meaning (hosted server)

| MCP tool | Parameters | CLI |
| --- | --- | --- |
| `semantic_search` | query, providers, pathPrefixes, paths, roles (history only when asked), kinds, contentTypes, accounts, driveFolderIds (immediate parent), driveIds, conversationIds, documentIds, updatedAfter/Before, capturedAfter/Before, versions, collapse, limit (≤50), cursor | `shepherd search "…" [--provider P] [--path P] [--role R] [--conversation ID] [--drive-folder ID] [--since D] [--until D] [--cursor C]` |
| `semantic_status` | provider, state, pathPrefix, after, limit, facets | `shepherd search-status [--facets]` |

Ranking is dense similarity only (no keyword fallback). Each hit has `retrieve` (the exact `read` range or
`sources_history` version). `scope.excluded` and `progress` say what was not searched. When the server has no
semantic index the tools answer `not_installed` / `semantic_unavailable`: use grep or SQL.

## SQL

| MCP tool | Parameters | CLI |
| --- | --- | --- |
| `sql_schema` | provider, table | `shepherd schema [TABLE] [--provider P]` |
| `sql_query` | sql, params[], maxRows (≤1000 MCP / ≤5000 CLI), offset, seq, maxCellChars (≤4194304), timeoutMs (≤30000) | `shepherd sql "SELECT ..." [--param V]... [--max-rows N] [--offset N --seq S] [--max-cell-chars N] [--format table\|json\|csv]` |

`shepherd sql-status` shows indexing progress. CLI `--param` values that look like numbers are sent as numbers. The
CLI's table view shows at most 80 characters per cell (it says so); `--format json|csv` print every value whole. When
one MCP answer cannot hold all rows, whole rows are kept and `nextOffset` moves; when one row alone is too large, its
longest values are shortened and listed in `cellsCut` like any cut value.

## Scratch (writes)

| MCP tool | Parameters | CLI |
| --- | --- | --- |
| `write` | path, content (UTF-8; replaces) | `shepherd write PATH < file` / `shepherd put LOCAL PATH [--no-overwrite]` |
| `edit` | path, edits[{oldText, newText}] (each oldText unique, non-overlapping) | — |
| `scratch_mkdir` | path | `shepherd mkdir PATH` |
| `scratch_move` | from, to, overwrite | `shepherd mv FROM TO [--overwrite]` |
| `scratch_delete` | path, recursive | `shepherd rm PATH [-r]` |

## Sandboxes (Modal, network blocked, no credentials inside)

| MCP tool | Parameters | CLI |
| --- | --- | --- |
| `sandbox_exec` | command (bash), sessionId (default: your only running workspace), timeoutMs (1000–600000) | `shepherd modal exec ID -- CMD...` |

`sandbox_exec` never starts a workspace; with none running it says so (`no_sandbox`).


`shepherd modal status` · `shepherd modal start` (mount mode: `/sources` read-only, `/scratch`, SQL index at
`/opt/shepherd-index/catalog.sqlite`; all three are symlinks to the volume mounts, so use `find -H /sources -type f`,
`du -shL /sources`, `ls -l /sources/`: a plain `find /sources` lists nothing) · `shepherd modal exec ID -- CMD...` (1 s–10 min, default 60 s; 256 KiB of
stdout/stderr each, `stdoutTruncated`) · `shepherd modal sync ID` · `shepherd modal stop ID`. Inside, the same
`shepherd` commands work in local mode. `modal enable/disable` change what the service stores: the user decides.

## Errors

Tool errors return `isError` with `{code, error}`; the CLI exits 1 (2 for not found) with `shepherd: code: message`.

| code | meaning |
| --- | --- |
| `unauthorized` | not signed in / session expired: the user runs `shepherd login` |
| `not_found` | no such path for this user (other users' data is indistinguishable from absent) |
| `read_only` | a write below `$SOURCES` |
| `invalid_path`, `invalid_input` | path grammar (no `..`, absolute or `$SOURCES`-less paths), bad parameter or cursor |
| `sql_forbidden`, `sql_syntax`, `sql_timeout`, `sql_result_too_large` | SQL refused, wrong, too slow (raise timeoutMs ≤30000 or narrow), too big (fewer columns/rows) |
| `snapshot_building` | first SQL index still building: retry shortly |
| `too_large`, `quota_exceeded` | scratch limits |
| `modal_not_configured`, `conflict` | sandbox not available / session busy or stopped |
