Ships your new Claude Code transcript records to your team's Depot server, so Depot can distill them into cited, per-project Memories. Uploads are incremental (only lines added since the last run) and idempotent (safe to run repeatedly and safe to interrupt).
It is three things in one directory:
- A standalone CLI —
bin/depot-upload.ts— runnable withbunornode, with zero dependencies. Use it from a shell, a cron job, or--watch. - A Claude Code plugin that runs the CLI automatically after each session via a
SessionEndhook, plus a/depot:depot-syncslash command for on-demand syncs. - Depot's MCP server, so the loop closes: your transcripts flow out to
Depot, and Depot's distilled memory flows back in as tools your agent calls
before it works. Bundled via
.mcp.json— installing the plugin wires it up. - The push channel — memory the agent doesn't have to ask for. A
SessionStarthook injects the project's cited brief into every session that starts inside a repo Depot knows, and aPreToolUsehook injects a file's failure dossier the first time the agent is about to edit a file with recorded friction. Pull (the MCP tools) answers when the agent asks; push means it never had to know there was something to ask.
The plugin registers a remote MCP server (depot, at
https://depot.ingram.tech/api/mcp). It authenticates with the token saved by
/depot:depot-login — read at connect time via a headersHelper, so there's no
env var to set and the token is never copied or printed. Once you've logged in,
your agent gets four read-only tools:
| Tool | What it answers |
|---|---|
depot_list_projects |
Which projects does Depot have memory for? (find the slug) |
depot_project_brief |
What's the state of this project — decisions, gotchas, and the files where work has hurt most? |
depot_file_context |
Before I edit this file: what already broke here, and what fixed it? Plus the Memories that cite it. |
depot_search_memories |
Have we decided / hit this before? (searches Memories, not raw transcripts) |
The high-value move is depot_file_context — it's git blame for intent: the
failed edits, their fixes, and the decisions behind a file, each linked to the
transcript that proves it. Ask your agent to "check Depot before editing," or add
it to your CLAUDE.md.
To use the server without the plugin, add it directly:
claude mcp add --transport http depot https://depot.ingram.tech/api/mcp \
--header "Authorization: Bearer dpt_…"Both hooks enter through bin/depot-context.ts and call
GET /api/v1/context?remote=… with the saved token — a repo is identified by
its origin git remote, the same routing key ingest uses, so the context you
get back is for exactly the project this session's transcript will land in.
- SessionStart injects the project brief (state of mind, load-bearing
memories, friction hotspots) as
additionalContext, and caches the server's hot-file list under~/.claude/depot/context/. - PreToolUse (
Edit|Write|NotebookEdit) checks the file about to be edited against that cached list — an ordinary edit costs zero network calls — and only for a known-hot file fetches its dossier (what failed here, what fixed it) and injects it, once per file per session. The list is keyed by the file's repo, not the directory the session started in: a session that edits across repos gets each repo's dossiers (an unseen repo's list is fetched once, then cached like SessionStart's).
Same contract as everything else in this plugin: every failure — no token, offline, repo unknown to Depot, timeout — is silent and costs nothing. With no token the hooks are inert.
Sent to the internal Depot server (over HTTPS, with a bearer token):
- The raw transcript JSONL records for sessions on this machine — i.e. the full content of your Claude Code conversations and tool calls. This is the whole point: Depot reads transcripts to build memory.
- A machine fingerprint (a random per-install UUID hashed with the hostname),
your
hostname, andos(process.platform). - Your Claude account identifier (account UUID and email, read from
~/.claude.json), so uploads are attributed per person+machine. - Per file: the session id, the directory the session started in, the git
originremote URL of every directory the uploaded records sat in (a session thatcds between repos belongs to each of them), the current branch (best effort), and a sha256 of the bytes uploaded.
Kept on this machine: any record whose directory has no git origin
remote. Depot only keeps memory for remote-backed repos; a private scratch
folder — even one a session wandered into mid-way — never leaves the machine.
The sync summary reports these as "kept private".
Never sent: any tokens or secrets. The uploader does not read
~/.claude/.credentials.json or any OAuth/API tokens. Only non-secret account
fields (account UUID, email, user id) are read from ~/.claude.json.
If you don't want a transcript uploaded, don't run the plugin in that session
(or unset DEPOT_TOKEN) — with no token the uploader exits cleanly and sends
nothing.
bun(preferred) or Node.js ≥ 20. The CLI runs the.tsfiles directly:bunruns TypeScript natively; Node ≥ 22.6 strips types natively (--experimental-strip-typeson 20.x — Node 22+ needs no flag).- A connection — easiest via
/depot:depot-login(see Getting connected).
| Var | Required | Default | Meaning |
|---|---|---|---|
DEPOT_TOKEN |
no* | from depot login |
Bearer token. Overrides the saved credentials file. |
DEPOT_URL |
no | https://depot.ingram.tech |
Depot server base URL. |
DEPOT_ACCOUNT_ID |
no | from ~/.claude.json |
Override the account identifier. |
DEPOT_PROJECTS_DIR |
no | ~/.claude/projects |
Where transcript JSONL files live. |
DEPOT_WATCH_SECONDS |
no | 60 |
Poll interval for --watch. |
XDG_STATE_HOME |
no | ~/.claude |
If set, state lives in $XDG_STATE_HOME/depot. |
* Not an env var you must set — /depot:depot-login saves the token to a
credentials file that both the uploader and the MCP read. With no token at all, a
real run exits non-zero and the auto-sync hook stays silent.
Sign in through the browser — one step, no copy-paste, no token in your chat.
Run the login command (or the CLI directly), approve in the browser, and the
token is delivered straight back to this machine and saved to
<stateDir>/credentials.json (mode 0600). The command prints no token:
/depot:depot-login
# or, fully programmatic (no agent, no model turn):
bun bin/depot-login.ts # opens your browser; sign in → AuthorizeBoth the uploader and the MCP tools read the token from that file
automatically — the MCP via a headersHelper that runs at connect time — so
there is nothing to export and nothing to copy. After logging in, run
/reload-plugins (or restart Claude Code) to connect the MCP.
Manual fallback — create a token at https://depot.ingram.tech/tokens and set
export DEPOT_TOKEN="dpt_…" in your shell. DEPOT_TOKEN in the environment always
overrides the saved credentials file (and the MCP headersHelper honors it too).
# one pass (default); prints a one-line summary
bun bin/depot-upload.ts --once
# see what would be sent — no network, no cursor changes
bun bin/depot-upload.ts --dry-run --verbose
# only a specific project (substring match on the project path)
bun bin/depot-upload.ts --once --project depot.ingram.tech
# run continuously, polling every 60s (good for a long-lived terminal/tmux)
bun bin/depot-upload.ts --watch
# with node instead of bun
node bin/depot-upload.ts --onceFlags: --once (default) · --watch · --watch-seconds N · --dry-run ·
--project <substr> · --verbose / -v · --help.
# every 10 minutes
*/10 * * * * DEPOT_TOKEN=dpt_… /usr/bin/node /path/to/plugin/bin/depot-upload.ts --once >> ~/.claude/depot/cron.log 2>&1The plugin auto-syncs after every session and adds the /depot:depot-sync
command.
Via the public marketplace (recommended) — this repo is a single-plugin
marketplace, so add it by owner/repo and install:
/plugin marketplace add ingram-technologies/claude-plugin-depot
/plugin install depot@depot
Local / development — point Claude Code at a checkout of this repo:
claude --plugin-dir /path/to/claude-plugin-depotor add the local checkout as a marketplace:
/plugin marketplace add /path/to/claude-plugin-depot
/plugin install depot@depot
After install, run /depot:depot-login once to connect (see Getting connected).
Then, when a session ends, the SessionEnd hook runs depot-upload --once in
the background — it never blocks you, and every failure is silent (logged to
~/.claude/depot/sync.log). Run /depot:depot-sync any time to sync on demand
and see the summary.
State lives in ~/.claude/depot/ (or $XDG_STATE_HOME/depot):
state.json— per absolute file path →{ bytesUploaded, linesUploaded, sha256OfLastUpload, lastRunAt }. On each run the uploader reads each transcript, takes everything pastbytesUploaded,JSON.parses the new lines (malformed lines are skipped, the rest still upload), and batches them per file. The cursor for a file advances only after that file's upload returns 2xx, so an interrupted run just re-sends the same lines next time and the server dedups them by record uuid. A trailing partial line (a session still appending) is left un-cursored until it's complete.machine.json— the random per-install UUID behind the machine fingerprint.sync.log— rotating log from the auto-sync hook.context/— the push channel's cache: per-project hot-file lists (refreshed each SessionStart, 12 h TTL) and per-session injected-file sets (pruned after 7 days). Safe to delete; it only ever costs a refetch.
Deleting state.json makes the next run re-upload everything (still idempotent —
the server dedups by record uuid).
claude-plugin-depot/
├── .claude-plugin/
│ ├── plugin.json # plugin manifest
│ └── marketplace.json # single-plugin marketplace descriptor
├── bin/depot-upload.ts # CLI entrypoint
├── bin/depot-context.ts # push-channel hook entry (SessionStart/PreToolUse)
├── src/ # config, identity, state, scan, upload, run, context
├── hooks/
│ ├── hooks.json # SessionEnd → sync; SessionStart/PreToolUse → context
│ ├── depot-sync.sh # background, silent, non-fatal sync
│ └── depot-context.sh # foreground (returns context), silent, non-fatal
├── commands/depot-sync.md # /depot:depot-sync slash command
└── package.json