Shepherd for agents
Your connected sources as a read-only, versioned filesystem ($SOURCES), a private writable scratch ($SCRATCH) and read-only SQL over structured records such as Messages, email and tickets, for any agent: through the shepherd CLI, its MCP server, and an installable agent skill. Everything runs as you and sees only your own data. You need only your Shepherd account with sources connected: no cloud account, API key or deployment.
1 · Install the CLI and sign in
One self-contained file, Node.js 22.13 or later. It talks to https://sources-api.askshepherd.ai by default.
mkdir -p ~/.local/bin
curl -fsSL https://sources.askshepherd.ai/downloads/shepherd.mjs -o ~/.local/bin/shepherd
chmod +x ~/.local/bin/shepherd
shasum -a 256 ~/.local/bin/shepherd # expect b0ad8f683ab339cb195514ce1de2d24a35bb6c7c0d00e6f8987200f90f3a65d3
export PATH="$HOME/.local/bin:$PATH"sha256 b0ad8f683ab339cb195514ce1de2d24a35bb6c7c0d00e6f8987200f90f3a65d3 (also in the download's x-content-sha256 header)
shepherd login # opens your browser to sign in; nothing to paste
shepherd whoamiThe refreshable token stays in ~/.config/shepherd/credentials.json (0600) on your machine. Agents never need it in their config, and you never paste it anywhere. shepherd login --no-browser prints the sign-in link instead.
2 · Connect an MCP client
The CLI runs a stdio MCP server as you. Add it with one command:
claude mcp add shepherd -- node ~/.local/bin/shepherd mcp --server https://sources-api.askshepherd.aicodex mcp add shepherd -- node ~/.local/bin/shepherd mcp --server https://sources-api.askshepherd.aipi mcp add shepherd -- node ~/.local/bin/shepherd mcp --server https://sources-api.askshepherd.ai{
"mcpServers": {
"shepherd": {
"command": "node",
"args": [
"/Users/<you>/.local/bin/shepherd",
"mcp",
"--server",
"https://sources-api.askshepherd.ai"
]
}
}
}The tool names and parameters follow the familiar coding-agent set (read, ls, find, grep, write, edit, as in Pi and Claude Code), so agents use them without new instructions. The service also exposes a remote MCP endpoint at https://sources-api.askshepherd.ai/mcp for clients that sign in with OAuth themselves; the stdio route above is the tested one.
3 · Install the agent skill
The shepherd-sources skill teaches an agent when to use files versus SQL, how to continue bounded answers, and the permission rules. It installs into ~/.agents/skills (Pi and other Agent Skills clients) and, when present, ~/.claude/skills and ~/.codex/skills.
shepherd skill installd=~/.agents/skills/shepherd-sources; mkdir -p "$d/references"
for f in SKILL.md references/setup.md references/sql.md references/tools.md; do
curl -fsSL "https://sources.askshepherd.ai/downloads/skills/shepherd-sources/$f" -o "$d/$f"
doneFiles: SKILL.md · references/sql.md · references/tools.md · references/setup.md
The filesystem
$SOURCES/<provider>/…: one directory per connected integration (imessage,gmail,slack,google-docs,notion,linear,granola,coding-sessions, …), then account, workspace or mailbox directories. Personal sources sit in your own owner directory (@u-…) that only you can list.- Readable projections (
.md), the provider's raw records as JSON (_raw/), and exact attachment bytes. - Every file keeps its versions and deletions (
sources_history). It is a captured copy: checksources_statusfor coverage and freshness. $SCRATCH/…: your private, persistent, writable space, shared by this website (Files → Scratch), the CLI, MCP and sandboxes.
shepherd ls '$SOURCES' # one directory per connected provider
shepherd status # coverage and freshness per provider
shepherd find '$SOURCES/notion' '*.md'
shepherd grep -i "launch plan" '$SOURCES/google-docs' --limit 20
shepherd read '$SOURCES/imessage/<owner>/conversations/<chat>/2026-10.md' --offset 1 --limit 80Files or SQL
Use SQL to count, filter, sort and join structured records (messages, emails, events, issues). Every row carries path, the exact $SOURCES file it came from, so an agent can open the source. Use grep/read for text inside documents and transcripts, and for fields no view models: the raw JSON keeps every provider field (json_extract(data, '$.field') on records).
shepherd schema # tables and views, row counts, completeness
shepherd schema imessage_messages # columns, source layout, example queries
shepherd sql "SELECT name, last_message_at, conversation_id FROM imessage_conversations
ORDER BY last_message_at DESC LIMIT 10"
shepherd sql "SELECT sent_at, coalesce(sender_name, sender_handle) AS who, text
FROM imessage_messages WHERE conversation_id = ? AND is_reaction = 0
ORDER BY sent_at DESC" --param '<conversation_id>' --max-rows 50
shepherd sql "SELECT subject, from_header, projection_path FROM gmail_messages
ORDER BY internal_date DESC" --max-rows 20 --offset 20 # page 2shepherd sql "SELECT path, size FROM files WHERE provider = 'linear'" --format csv > /tmp/f.csv
shepherd put /tmp/f.csv '$SCRATCH/reports/linear-files.csv'
echo "# notes" | shepherd write '$SCRATCH/notes.md'Tool reference
The same tools and receipts in the hosted MCP endpoint, the CLI's stdio MCP server and the CLI. Paths start with $SOURCES or $SCRATCH.
Read and search
| MCP tool | Parameters | CLI | What it does |
|---|---|---|---|
| ls | path, limit, after | shepherd ls PATH | List a directory: providers at $SOURCES, then accounts/workspaces/mailboxes. |
| read | path, offset, limit | byteOffset, byteLimit | shepherd read PATH · shepherd cat PATH · shepherd get PATH | Lines of a text file at any depth, or exact byte ranges of any file; PNG/JPEG/GIF/WebP come back as images. |
| find | pattern, path, limit, after | shepherd find PATH PATTERN | Paths by glob (names, or relative paths when the glob has '/'). |
| grep | pattern, path, literal, ignoreCase, glob, context, limit, after | shepherd grep PATTERN PATH [-C N] | Matching lines in text files (regex or literal), with context lines. |
| sources_status | provider | shepherd status | What is connected, coverage (complete/partial/blocked) and freshness. |
| sources_history | path, limit, before, versionId, byteOffset, byteLimit | shepherd history PATH | Every version of a file, including deletions; read any prior version. |
Search by meaning
| MCP tool | Parameters | CLI | What it does |
|---|---|---|---|
| semantic_search | query, providers, pathPrefixes, roles, conversationIds, driveFolderIds, documentIds, updatedAfter/Before, versions, limit, cursor | shepherd search "…" | Find text by meaning across sources you can read, with the exact source range of each hit (hosted server). |
| semantic_status | provider, state, pathPrefix, after, limit, facets | shepherd search-status | What search by meaning covers: indexed/pending/unsupported counts with reasons, and the filter values you have. |
SQL
| MCP tool | Parameters | CLI | What it does |
|---|---|---|---|
| sql_schema | provider, table | shepherd schema [TABLE] | Tables and views with row counts and completeness; one table's columns and example queries. |
| sql_query | sql, params, maxRows, offset, seq, maxCellChars, timeoutMs | shepherd sql "SELECT …" --param V | One read-only SELECT with ? parameters; page with offset = nextOffset; long values in full with maxCellChars. |
Scratch (the only writes)
| MCP tool | Parameters | CLI | What it does |
|---|---|---|---|
| write | path, content | shepherd write PATH < file · shepherd put FILE PATH | Create or replace a file below $SCRATCH. |
| edit | path, edits[{oldText, newText}] | — | Exact-text replacements in a $SCRATCH file. |
| scratch_mkdir | path | shepherd mkdir PATH | Create a $SCRATCH directory. |
| scratch_move | from, to, overwrite | shepherd mv FROM TO | Move or rename within $SCRATCH. |
| scratch_delete | path, recursive | shepherd rm PATH [-r] | Delete from $SCRATCH. |
SQL views
Read-only SQLite over a private snapshot of the files you can read; it refreshes as sources change. sql_schema lists every table with its columns; these are the ones agents use most:
imessage_messages— One row per iMessage/SMS message: sender, resolved name, text and original text, replies (reply_to), tapbacks with actor, target message and removal, edits, unsends, attachment count.imessage_message_versions— Every version of edited messages as Messages recorded it (version 0 = original).imessage_conversations— Conversations (direct/group), names, first/last message, directory.imessage_participants— Handles in each conversation with names and message counts.imessage_attachments— Attachment byte state (stored/pending/unavailable + reason), the stored file's path, and bytes_verified against the file's sha256 and size.imessage_conversation_months— The readable monthly transcript file of each conversation.imessage_contacts— Address-book names the Mac synced.imessage_message_edits— Earlier text of edited or unsent messages.gmail_messages— Mailbox, thread, labels, folder, Subject/From/To and snippet.gmail_attachments— Attachment parts and their stored files.slack_messages— Slack messages from the raw ledger (channel, ts, thread, user id, text).slack_conversations— Channels and DMs with access/history state.calendar_events— Current Google Calendar events.linear_issues— Linear issues with state, assignee and dates.jira_issues— Jira issues with status, people and dates.activity_events— Every provider's raw event ledger with the full raw payload.files— Every readable file: path, provider, size, hash, capture time.records— Every JSON document (query fields with json_extract).
Message names are what your Mac resolved from Contacts when it uploaded; a missing name means none was available. Attachments that were only in iCloud show state = 'unavailable' with the reason; stored ones have file_path and bytes_verified = 1. read returns PNG, JPEG, GIF and WebP files to the model as images; shepherd get downloads any file byte-exactly.
Bounded answers
Every answer has a size bound, and nothing is dropped silently: the receipt says what is missing and how to continue.
| truncated + nextAfter (ls, find, grep) | Repeat the call with after = nextAfter. truncatedReason says whether your limit or a per-call server bound stopped it. |
| matchesMayContinueIn (grep) | That file may hold more matches than were returned: grep it alone. |
| skipped.binary / skipped.tooLarge (grep) | Files that are not line-searchable; read them by byte range or use SQL. |
| nextOffset · nextByteOffset · lineCut (read) | The file continues: read again from that line or byte; lineCut.restByteOffset is where a cut line continues. |
| nextOffset · snapshotMoved (sql_query) | More rows: same sql and params with offset = nextOffset and seq = snapshot.seq (with ORDER BY); snapshotMoved says the data changed between pages. |
| complete: false · pending · errors (SQL) | Some files are not indexed yet, or cannot be rows; the answer may be partial. |
| cellsCut (SQL) | Values over maxCellChars (default 16384) are listed with their full length: ask again with a larger maxCellChars (up to 4194304) or page one value with substr(). |
| lineChars (grep) | A matching line longer than 500 characters is shown clipped; read the file at that line for all of it. |
Permissions and privacy
- The website, CLI, both MCP servers, SQL and sandboxes apply the same per-person access rule, from your verified sign-in only. Other people's data is indistinguishable from absent.
$SOURCESrefuses every write; agents write only below$SCRATCH. Nothing is written back to a provider.- SQL runs one SELECT with
?parameters in an isolated read-only process; writes, PRAGMA, ATTACH, extensions and file functions are refused. - Provider credentials stay in the collector and are never sent to agents or sandboxes.
Sandboxes (optional)
For heavy processing, Modal sandboxes mount all your sources read-only at /sources, your scratch at /scratch and the SQL index, with no network and no credentials; the shepherd CLI works inside them. Enable them on the Sandboxes page (this keeps a read-only mirror of your sources on the service's Modal storage), then:
shepherd modal start
shepherd modal exec <id> -- shepherd sql "SELECT count(*) FROM imessage_messages"
shepherd modal stop <id>MCP clients get the same through one tool; it runs only in a workspace you started and never starts one itself:
| MCP tool | Parameters | CLI | What it does |
|---|---|---|---|
| sandbox_exec | command, sessionId, timeoutMs | shepherd modal exec ID -- CMD | Run one command in the Modal workspace you started (never starts one): /sources read-only, /scratch, SQL, no network. |