Content
# Second Brain — a portable knowledge vault for Claude Code
A Claude Code **marketplace plugin** that gives any agent a second brain: recall + capture skills
backed by a git repo, powered by a bundled, **zero-dependency** MCP server with **anonymized
git-token handling**.
Extracted from the [Noah Almighty / avatar-chat](https://example.invalid) brain skills so the same
capability runs in vanilla Claude Code (or any MCP-capable agent), independent of that system's
in-process tools and multi-tenant trust model.
## What's inside
```
.claude-plugin/marketplace.json ← marketplace manifest (this repo is a marketplace)
plugins/second-brain/
├── .claude-plugin/plugin.json ← plugin manifest
├── .mcp.json ← launches the bundled MCP server
├── mcp/ ← the "brain" MCP server (Node, zero deps)
│ ├── index.mjs ← JSON-RPC/stdio loop + tool dispatch
│ ├── git.mjs ← token-anonymized git plumbing
│ ├── search.mjs ← keyword ranking over wiki/
│ └── vault.mjs ← path-guarded filesystem helpers
└── skills/ ← brain-search / ingest / reflect / lint / migrate
```
The vault itself is a separate git repo holding `wiki/` (curated notes) and `raw/` (captures) — you
point the plugin at it via env (below). One vault per install.
## Install
```
/plugin marketplace add jinyoung/brain-skills # or a local path to this repo
/plugin install second-brain
```
Requires `node` (≥18) and `git` on PATH. No `npm install` — the MCP server uses only Node built-ins,
so it runs the moment the plugin is cloned.
## Configure (env)
The plugin's `.mcp.json` reads these from your environment:
| Variable | Meaning |
| ----------------- | ----------------------------------------------------------------------------- |
| `BRAIN_DIR` | Local working-tree path for the vault. Default: `~/.second-brain`. |
| `BRAIN_REPO` | Remote vault: `owner/repo`, a GitHub URL, or any git URL. Omit for local-only. |
| `BRAIN_GIT_TOKEN` | Auth token for `BRAIN_REPO` (private repos / push). **Optional** — see auth below. |
| `BRAIN_GIT_HOST` | GitHub host for the `owner/repo` form. Default: `github.com`. |
- **With `BRAIN_REPO`:** the vault is cloned/synced on first use and `commit` pushes back.
- **Without it:** `BRAIN_DIR` is a purely local git repo; `commit` stays local.
## Authenticating to a remote vault
Two ways, in order of preference:
### 1. Credential helper (recommended — no token in the environment)
**Leave `BRAIN_GIT_TOKEN` unset.** With no token, the MCP server attaches no auth header and git
resolves credentials the normal way — from your OS keychain / git credential helper:
```
gh auth login # or git-credential-manager, osxkeychain, libsecret, …
# do NOT set BRAIN_GIT_TOKEN
```
Why this is safer: a PAT exported as an env var is inherited by **every** subprocess the agent
spawns — including its Bash tool — so the model could read it with `printenv` and it would land in
the model's context (i.e. leave your machine). Keeping the token in the OS keychain instead means it
never appears as an environment variable the agent can read. The server runs git non-interactively
(`GIT_TERMINAL_PROMPT=0`), so if the helper has no cached credential it fails fast rather than hanging.
### 2. `BRAIN_GIT_TOKEN` env var (fallback)
If you do use the env var, prefer a **fine-grained PAT scoped to just the vault repo**
(`contents: read/write`) to limit the blast radius if it leaks. The token is still **anonymized**
(below) on the git side — but the env-exposure caveat above applies.
## Git-token anonymization
The token is treated as a secret that must never be persisted or leak (the pattern ported from
avatar-chat's `marketplace.ts`):
1. **Never in the remote URL.** Embedding it would write it into `.git/config` on disk. Instead it
is supplied per-invocation as a `git -c http.extraHeader=Authorization: Basic <base64>` header,
which git uses for the transfer but never persists.
2. **Scrubbed from errors.** `execFile` rejections embed the full argv (including that header, whose
base64 decodes back to the token). Every git error is run through `scrubGitError`, which redacts
`Authorization: Basic …` → `[REDACTED]` before anything is returned to the model or logged.
3. **Env-only.** The token is read solely from `BRAIN_GIT_TOKEN`; it is never echoed by any tool.
Commit author/committer come from your own git config — identity is not overridden.
## Tools (surface as `mcp__brain__*`)
| Tool | Purpose |
| ------------ | ------------------------------------------------------------- |
| `search` | Ranked recall over `wiki/` (title/aliases > tags > body). |
| `get_note` | Read a full `wiki/…` note + a resolved graph footer (links → paths, dangling flags, backlinks). |
| `list_files` | List the working tree (optionally scoped to a subdir). |
| `read_file` | Read any vault file. |
| `write_file` | Create/overwrite a file (parent dirs created). |
| `commit` | Stage all, commit, and push (token anonymized). |
| `status` | Report remote / working tree / vault-layout / pending paths. |
You can also edit the working tree with Claude Code's built-in tools (find the path via `status`),
but always **persist with `mcp__brain__commit`** — only that path pushes with the token anonymized.
## Development
The MCP server still has **zero runtime dependencies** — `plugins/second-brain/package.json` only wires
up Node's built-in test runner (no `npm install` needed to *run* the plugin). Tests cover the pure
logic (frontmatter parsing, search ranking, path guards, token scrubbing, the wikilink graph) plus a
real local-git commit:
```
cd plugins/second-brain && npm test # node --test, no deps
```
## License
MIT