@korso/shepherd 0.4.4 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,327 +1,336 @@
1
- # @korso/shepherd — Shepherd MCP Server
2
-
3
- Shepherd's stdio MCP server. Gives any MCP-capable agent (Claude Code, Codex, etc.) four advisory coordination tools backed by the shared hub: `work`, `done`, `announce`, and `sync`. The agent **joins the workspace automatically** on startup (no `join` tool), and the server ships standing instructions so the agent self-coordinates without the user prompting it.
4
-
5
- > **New here?** The [developer quickstart](https://github.com/Korsoai/shepherd/blob/main/docs/shepherd-mcp-quickstart.md) is the fastest path. TL;DR: `npx -y @korso/shepherd` with the env vars below.
6
-
7
- ---
8
-
9
- ## CRITICAL: WORKSPACE must match the hub exactly
10
-
11
- > **`WORKSPACE` defaults to `default`. If you override it, the value must equal the hub's `ALLOWED_WORKSPACE` env var exactly.**
12
-
13
- The server fires an automatic `join` call to the hub at startup. If the workspace it sends does not match the hub's `ALLOWED_WORKSPACE`, that call returns HTTP 400 and coordination degrades: every tool reports "session not ready … proceeding uncoordinated" instead of a landscape. The safe default is to **leave `WORKSPACE` unset** so it resolves to `default` — only set it when a maintainer points you at a different workspace. If your agent never sees teammates, check `WORKSPACE` (and `TEAM_TOKEN`) first.
14
-
15
- ---
16
-
17
- ## Install
18
-
19
- The server is published to npm and runs via `npx` — no clone or build required
20
- (Node 18+):
21
-
22
- ```sh
23
- npx -y @korso/shepherd
24
- ```
25
-
26
- You won't normally run that by hand; you put it in your MCP client config (below)
27
- with the required env vars. `npx` caches the package, so startup is fast after the
28
- first fetch, and `@korso/shepherd@latest` picks up updates automatically.
29
-
30
- > Hacking on the server itself? See **[Develop from source](#develop-from-source)**
31
- > at the bottom.
32
-
33
- ---
34
-
35
- ## 2. Environment variables
36
-
37
- **Only two are required:**
38
-
39
- | Variable | Description | Example |
40
- |---|---|---|
41
- | `HUB_URL` | Base URL of the deployed hub | `https://shepherd.example.com` |
42
- | `TEAM_TOKEN` | Shared bearer token accepted by the hub | `tok_abc123` |
43
-
44
- Missing either causes an immediate startup failure with a clear error on stderr
45
- listing which vars are absent. (No other var triggers this.)
46
-
47
- **Everything else is optional** — each identity field is resolved at startup as
48
- **env var → git detection → fallback**, so a plain `npx -y @korso/shepherd` with
49
- just the two required vars produces a valid, fully-identified session. Set an
50
- override only to replace what's detected:
51
-
52
- | Variable | If omitted | Example |
53
- |---|---|---|
54
- | `WORKSPACE` | defaults to `default` (**must match hub's `ALLOWED_WORKSPACE` if overridden**) | `shepherd` |
55
- | `REPO` | `git remote origin` → `owner/repo`, else repo folder name, else `unknown-repo` | `Korsoai/shepherd` |
56
- | `BRANCH` | `git rev-parse --abbrev-ref HEAD`, else `HEAD` | `main` |
57
- | `BASE_BRANCH` | `origin/HEAD`, else `origin/main` / `origin/master` (used for the change-awareness heads-up) | `origin/main` |
58
- | `HUMAN` | git `user.name`, else local-part of `user.email`, else a generated name | `daichi` |
59
- | `PROGRAM` | defaults to `claude-code` | `codex` |
60
- | `MODEL` | omitted — **never auto-detected**, so set it if you want it shown | `claude-sonnet-4-6` |
61
- | `HEARTBEAT_INTERVAL_SECONDS` | defaults to `60` | `30` |
62
- | `SHEPHERD_INBOX_DIR` | defaults to `~/.shepherd/inbox`. Override only to relocate the **announcement-push** inbox (see below); the background heartbeat writes incoming announcements here. If you set it, point your client hook/extension at the **same** dir | `~/.shepherd/inbox` |
63
-
64
- ---
65
-
66
- ## Announcement push (on by default)
67
-
68
- Announcements reach an agent **without it having to ask**. The background
69
- heartbeat pulls any pending announcements from the hub every beat and stages them
70
- in a local **inbox file** (per working directory, under `SHEPHERD_INBOX_DIR`,
71
- default `~/.shepherd/inbox`). That file is then drained by two paths:
72
-
73
- 1. **Universal drainer (always on, every client).** Whenever the agent calls any
74
- Shepherd tool (`work`/`sync`/`done`/`announce`), the result also includes
75
- anything sitting in the inbox. So even with no hook configured, no announcement
76
- is ever lost — the worst case is the old behaviour (delivered on the next
77
- Shepherd tool call), never silent drops.
78
- 2. **Passive client hook/extension (optional, per client).** To get announcements
79
- **without** waiting for a Shepherd tool call — surfaced on the agent's next
80
- action of any kind — wire up your client's hook below. This is the
81
- "a subagent finished" style of notification.
82
-
83
- Both paths read the **same** inbox file and de-duplicate by announcement id, so
84
- running both is safe (the hub hands each announcement to exactly one drain; the
85
- merge is just defensive). It's cheap: a **local file read — no network** (the
86
- heartbeat already did the fetch), and it only adds to the model's context when
87
- something is actually waiting.
88
-
89
- It delivers to an agent **while it's active**; an idle agent picks messages up the
90
- moment it next does anything. (Waking a fully-idle agent is out of scope — for
91
- Claude Code that needs Channels; Codex/Pi have no equivalent.)
92
-
93
- ### Claude Code — `PreToolUse` hook
94
-
95
- `PreToolUse` fires before every tool, giving the most frequent passive delivery.
96
- The hook needs no arguments — it resolves the same default inbox dir the server
97
- uses (override both with `SHEPHERD_INBOX_DIR` if you relocated it):
98
-
99
- ```json
100
- {
101
- "mcpServers": {
102
- "shepherd": {
103
- "command": "npx",
104
- "args": ["-y", "@korso/shepherd"],
105
- "env": {
106
- "HUB_URL": "https://shepherd.example.com",
107
- "TEAM_TOKEN": "tok_abc123"
108
- }
109
- }
110
- },
111
- "hooks": {
112
- "PreToolUse": [
113
- {
114
- "matcher": "*",
115
- "hooks": [
116
- { "type": "command", "command": "npx -y -p @korso/shepherd shepherd-inbox-hook" }
117
- ]
118
- }
119
- ]
120
- }
121
- }
122
- ```
123
-
124
- ### Codex — `UserPromptSubmit` hook
125
-
126
- Codex uses the **same** hook contract as Claude Code (JSON on stdin, a
127
- `hookSpecificOutput.additionalContext` reply), so the **same bin** serves it. Use
128
- `UserPromptSubmit` — Codex's `PreToolUse` only fires for Bash, not `apply_patch`
129
- or MCP calls. Hooks must be enabled with `features.hooks = true`. In
130
- `~/.codex/config.toml`:
131
-
132
- ```toml
133
- [features]
134
- hooks = true
135
-
136
- [[hooks.UserPromptSubmit]]
137
- command = ["npx", "-y", "-p", "@korso/shepherd", "shepherd-inbox-hook"]
138
- # On Windows use command_windows instead:
139
- # command_windows = ["cmd", "/c", "npx -y -p @korso/shepherd shepherd-inbox-hook"]
140
- ```
141
-
142
- ### Pi — extension
143
-
144
- Pi has no stdin/stdout hook; it loads in-process extensions. Ship the bundled
145
- extension into Pi's extensions dir:
146
-
147
- ```sh
148
- # global, applies everywhere:
149
- mkdir -p ~/.pi/agent/extensions
150
- cp "$(npm root -g)/@korso/shepherd/dist/inboxExtension.js" ~/.pi/agent/extensions/shepherd-inbox.js
151
- # …or per-project: copy into .pi/extensions/ in the repo root.
152
- ```
153
-
154
- It runs on every user turn (`before_agent_start`), drains the same inbox, and
155
- injects pending announcements. (Or load it ad hoc with
156
- `pi -e /abs/path/to/dist/inboxExtension.js`.)
157
-
158
- ### Notes
159
-
160
- Every path is **fail-open**: a missing dir, unreachable hub, or any error means
161
- nothing is surfaced and the tool call / turn proceeds normally — coordination
162
- never blocks the agent. The inbox is keyed per working directory; two sessions in
163
- the exact same directory share it (a benign edge — they're the same repo). If you
164
- override `SHEPHERD_INBOX_DIR` on the server, set it on the hook/extension to the
165
- same value (the Claude/Codex bin and the Pi extension both read
166
- `SHEPHERD_INBOX_DIR`, or you can pass the dir as the first CLI arg to the bin).
167
-
168
- ---
169
-
170
- ## 3. MCP client configuration
171
-
172
- ### Claude Code
173
-
174
- > **Do not use `~/.claude/mcp.json` — Claude Code does not read it** (a config
175
- > there loads silently into nothing). Use `claude mcp add` (user scope, applies
176
- > everywhere) or a project-root `.mcp.json`. Confirm with `claude mcp list`,
177
- > which should show `shepherd … ✔ Connected`.
178
-
179
- Recommended — register once at user scope. Written as a **single line** so it
180
- pastes cleanly into PowerShell, cmd, bash, and zsh (on PowerShell the bash `\`
181
- line-continuation does not work). Minimal: just the two required vars (identity
182
- is auto-detected from git):
183
-
184
- ```powershell
185
- claude mcp add shepherd -s user -e HUB_URL=https://shepherd.example.com -e TEAM_TOKEN=tok_abc123 -- npx -y @korso/shepherd
186
- ```
187
-
188
- Add any optional overrides from §2 with extra `-e` flags (e.g. `-e MODEL=claude-sonnet-4-6 -e HUMAN=daichi`).
189
-
190
- Alternative — a `.mcp.json` at the **root of the repo you're working in**
191
- (optional overrides shown commented-style; drop the ones you don't need):
192
-
193
- ```json
194
- {
195
- "mcpServers": {
196
- "shepherd": {
197
- "command": "npx",
198
- "args": ["-y", "@korso/shepherd"],
199
- "env": {
200
- "HUB_URL": "https://shepherd.example.com",
201
- "TEAM_TOKEN": "tok_abc123",
202
- "MODEL": "claude-sonnet-4-6"
203
- }
204
- }
205
- }
206
- }
207
- ```
208
-
209
- > Windows note: the server is a thin stdio client to the Linux-hosted hub, and
210
- > `npx` works the same on every OS — no file paths to escape. The hub itself runs
211
- > on Linux (Postgres), so the Windows-native durability concerns from the spike
212
- > don't apply to clients.
213
-
214
- ### Codex (`~/.codex/config.toml`)
215
-
216
- Codex uses the same MCP stdio protocol but configures it in **TOML**, not JSON —
217
- at `~/.codex/config.toml` (global) or `.codex/config.toml` in a trusted project.
218
- The table is `mcp_servers` with an **underscore** (`mcp-servers`/`mcpServers` are
219
- silently ignored). Either run `codex mcp add`:
220
-
221
- ```sh
222
- codex mcp add shepherd --env HUB_URL=https://shepherd.example.com --env TEAM_TOKEN=tok_abc123 --env PROGRAM=codex -- npx -y @korso/shepherd
223
- ```
224
-
225
- …or add the table directly:
226
-
227
- ```toml
228
- [mcp_servers.shepherd]
229
- command = "npx"
230
- args = ["-y", "@korso/shepherd"]
231
- env = { HUB_URL = "https://shepherd.example.com", TEAM_TOKEN = "tok_abc123", PROGRAM = "codex", MODEL = "o4-mini" }
232
- ```
233
-
234
- ### Pi (`~/.pi/agent/mcp.json` or `.pi/mcp.json`)
235
-
236
- Pi uses a JSON `mcpServers` block (project config overrides global):
237
-
238
- ```json
239
- {
240
- "mcpServers": {
241
- "shepherd": {
242
- "command": "npx",
243
- "args": ["-y", "@korso/shepherd"],
244
- "env": {
245
- "HUB_URL": "https://shepherd.example.com",
246
- "TEAM_TOKEN": "tok_abc123",
247
- "PROGRAM": "pi"
248
- }
249
- }
250
- }
251
- }
252
- ```
253
-
254
- ---
255
-
256
- ## 4. Verify the server starts (quick smoke test)
257
-
258
- Run with the two required vars set to confirm it connects and idles on stdin.
259
- PowerShell (set env vars, then run):
260
-
261
- ```powershell
262
- $env:HUB_URL = "https://shepherd.example.com"
263
- $env:TEAM_TOKEN = "tok_abc123"
264
- npx -y @korso/shepherd
265
- ```
266
-
267
- bash/zsh: `HUB_URL=https://shepherd.example.com TEAM_TOKEN=tok_abc123 npx -y @korso/shepherd`
268
-
269
- No stderr output and the process blocking on stdin = healthy. Press Ctrl+C to exit.
270
-
271
- **Missing env vars:** if you omit `HUB_URL` or `TEAM_TOKEN`, you will see:
272
-
273
- ```
274
- [shepherd] Configuration error — missing or invalid env vars:
275
- HUB_URL: HUB_URL is required
276
- TEAM_TOKEN: TEAM_TOKEN is required
277
- ```
278
-
279
- and the process exits 1 immediately. This is by design. The optional identity
280
- vars never cause this — they fall back to git detection / defaults.
281
-
282
- **Wrong WORKSPACE:** if you override `WORKSPACE` to a value the hub doesn't allow, the server starts and connects but the startup auto-join is rejected (400), so every tool call (`work`, `sync`, etc.) reports "session not ready … proceeding uncoordinated". Either leave `WORKSPACE` unset (resolves to `default`) or set it to exactly match the hub's `ALLOWED_WORKSPACE`, then restart.
283
-
284
- ---
285
-
286
- ## Develop from source
287
-
288
- Only needed if you're changing the MCP server itself. Clone the monorepo and
289
- point your client at a local build instead of npx:
290
-
291
- ```sh
292
- git clone https://github.com/Korsoai/shepherd.git
293
- cd shepherd
294
- npm install
295
- npm run build # tsc -b — compiles the workspace for dev + tests
296
- ```
297
-
298
- For an exact preview of the published artifact (a single self-contained bundle
299
- with `@shepherd/shared` inlined), build the package directly:
300
-
301
- ```sh
302
- npm run build --workspace=@korso/shepherd # runs tsup → packages/mcp-server/dist/index.js
303
- ```
304
-
305
- Then use `node /absolute/path/to/shepherd/packages/mcp-server/dist/index.js` as
306
- the `command` in your MCP config (Windows: escape backslashes in JSON).
307
-
308
- ### Publishing a new version
309
-
310
- ```sh
311
- # bump "version" in packages/mcp-server/package.json, then:
312
- npm publish --workspace=@korso/shepherd # prepublishOnly runs tsup automatically
313
- ```
314
-
315
- `publishConfig.access` is `public`, so the scoped package publishes publicly.
316
-
317
- ---
318
-
319
- ## Troubleshooting
320
-
321
- | Symptom | Likely cause | Fix |
322
- |---|---|---|
323
- | `Configuration error — missing or invalid env vars` | `HUB_URL` or `TEAM_TOKEN` is absent (only these two are required) | Add the missing var(s) to your client's `env` block |
324
- | Tools report "session not ready … proceeding uncoordinated" | Startup auto-join rejected — usually a stale `TEAM_TOKEN`, or a `WORKSPACE` override the hub doesn't allow | Re-check `TEAM_TOKEN`; leave `WORKSPACE` unset (→ `default`) or match the hub's `ALLOWED_WORKSPACE`; restart |
325
- | Agent shows up under a surprising name/repo/branch | Identity auto-detected from git | Override with `HUMAN`/`REPO`/`BRANCH`/`MODEL` env vars (§2) |
326
- | `npm error 404 … @korso/shepherd` | Package not published yet, or name typo | `npm view @korso/shepherd version` to confirm it's live |
327
- | Process exits immediately with no error | Rare; check for node version incompatibility | Requires Node 18+ (ESM support) |
1
+ # @korso/shepherd — Shepherd MCP Server
2
+
3
+ Shepherd's stdio MCP server. Gives any MCP-capable agent (Claude Code, Codex, etc.) four advisory coordination tools backed by the shared hub: `work`, `done`, `announce`, and `sync`. The agent **joins the workspace automatically** on startup (no `join` tool), and the server ships standing instructions so the agent self-coordinates without the user prompting it.
4
+
5
+ > **New here?** The [developer quickstart](https://github.com/Korsoai/shepherd/blob/main/docs/shepherd-mcp-quickstart.md) is the fastest path. TL;DR: `npx -y @korso/shepherd` with the env vars below.
6
+
7
+ ---
8
+
9
+ ## CRITICAL: WORKSPACE must match the hub exactly
10
+
11
+ > **`WORKSPACE` defaults to `default`. If you override it, the value must equal the hub's `ALLOWED_WORKSPACE` env var exactly.**
12
+
13
+ The server fires an automatic `join` call to the hub at startup. If the workspace it sends does not match the hub's `ALLOWED_WORKSPACE`, that call returns HTTP 400 and coordination degrades: every tool reports "session not ready … proceeding uncoordinated" instead of a landscape. The safe default is to **leave `WORKSPACE` unset** so it resolves to `default` — only set it when a maintainer points you at a different workspace. If your agent never sees teammates, check `WORKSPACE` (and `TEAM_TOKEN`) first.
14
+
15
+ ---
16
+
17
+ ## Install
18
+
19
+ The server is published to npm and runs via `npx` — no clone or build required
20
+ (Node 18+):
21
+
22
+ ```sh
23
+ npx -y @korso/shepherd
24
+ ```
25
+
26
+ You won't normally run that by hand; you put it in your MCP client config (below)
27
+ with the required env vars. `npx` caches the package, so startup is fast after the
28
+ first fetch, and `@korso/shepherd@latest` picks up updates automatically.
29
+
30
+ > Hacking on the server itself? See **[Develop from source](#develop-from-source)**
31
+ > at the bottom.
32
+
33
+ ---
34
+
35
+ ## 2. Environment variables
36
+
37
+ **Only two are required:**
38
+
39
+ | Variable | Description | Example |
40
+ |---|---|---|
41
+ | `HUB_URL` | Base URL of the deployed hub | `https://shepherd.example.com` |
42
+ | `TEAM_TOKEN` | Shared bearer token accepted by the hub | `tok_abc123` |
43
+
44
+ Missing either causes an immediate startup failure with a clear error on stderr
45
+ listing which vars are absent. (No other var triggers this.)
46
+
47
+ **Everything else is optional** — each identity field is resolved at startup as
48
+ **env var → git detection → fallback**, so a plain `npx -y @korso/shepherd` with
49
+ just the two required vars produces a valid, fully-identified session. Set an
50
+ override only to replace what's detected:
51
+
52
+ | Variable | If omitted | Example |
53
+ |---|---|---|
54
+ | `WORKSPACE` | defaults to `default` (**must match hub's `ALLOWED_WORKSPACE` if overridden**) | `shepherd` |
55
+ | `REPO` | `git remote origin` → `owner/repo`, else repo folder name, else `unknown-repo` | `Korsoai/shepherd` |
56
+ | `BRANCH` | `git rev-parse --abbrev-ref HEAD`, else `HEAD` | `main` |
57
+ | `BASE_BRANCH` | `origin/HEAD`, else `origin/main` / `origin/master` (used for the change-awareness heads-up) | `origin/main` |
58
+ | `HUMAN` | git `user.name`, else local-part of `user.email`, else this device's **cached** last-detected name, else a generated name | `daichi` |
59
+ | `PROGRAM` | defaults to `claude-code` | `codex` |
60
+ | `MODEL` | omitted — **never auto-detected**, so set it if you want it shown | `claude-sonnet-4-6` |
61
+ | `HEARTBEAT_INTERVAL_SECONDS` | defaults to `60` | `30` |
62
+ | `SHEPHERD_INBOX_DIR` | defaults to `~/.shepherd/inbox`. Override only to relocate the **announcement-push** inbox (see below); the background heartbeat writes incoming announcements here. If you set it, point your client hook/extension at the **same** dir | `~/.shepherd/inbox` |
63
+
64
+ **Device-identity cache.** Whenever `HUMAN` is unset and git **does** detect a
65
+ name, that name is cached for your OS user at `~/.shepherd/identity.json`. A
66
+ later launch from a directory where git can't be read (e.g. a multi-repo
67
+ workspace root) then reuses the cached name instead of inventing a fresh random
68
+ one each time. The cache refreshes automatically the next time git reports a
69
+ different name, and an explicit `HUMAN` override always wins and never touches
70
+ the cache. It is best-effort: if the file can't be read or written, resolution
71
+ just falls back to a generated name.
72
+
73
+ ---
74
+
75
+ ## Announcement push (on by default)
76
+
77
+ Announcements reach an agent **without it having to ask**. The background
78
+ heartbeat pulls any pending announcements from the hub every beat and stages them
79
+ in a local **inbox file** (per working directory, under `SHEPHERD_INBOX_DIR`,
80
+ default `~/.shepherd/inbox`). That file is then drained by two paths:
81
+
82
+ 1. **Universal drainer (always on, every client).** Whenever the agent calls any
83
+ Shepherd tool (`work`/`sync`/`done`/`announce`), the result also includes
84
+ anything sitting in the inbox. So even with no hook configured, no announcement
85
+ is ever lost — the worst case is the old behaviour (delivered on the next
86
+ Shepherd tool call), never silent drops.
87
+ 2. **Passive client hook/extension (optional, per client).** To get announcements
88
+ **without** waiting for a Shepherd tool call — surfaced on the agent's next
89
+ action of any kind — wire up your client's hook below. This is the
90
+ "a subagent finished" style of notification.
91
+
92
+ Both paths read the **same** inbox file and de-duplicate by announcement id, so
93
+ running both is safe (the hub hands each announcement to exactly one drain; the
94
+ merge is just defensive). It's cheap: a **local file read — no network** (the
95
+ heartbeat already did the fetch), and it only adds to the model's context when
96
+ something is actually waiting.
97
+
98
+ It delivers to an agent **while it's active**; an idle agent picks messages up the
99
+ moment it next does anything. (Waking a fully-idle agent is out of scope — for
100
+ Claude Code that needs Channels; Codex/Pi have no equivalent.)
101
+
102
+ ### Claude Code — `PreToolUse` hook
103
+
104
+ `PreToolUse` fires before every tool, giving the most frequent passive delivery.
105
+ The hook needs no arguments — it resolves the same default inbox dir the server
106
+ uses (override both with `SHEPHERD_INBOX_DIR` if you relocated it):
107
+
108
+ ```json
109
+ {
110
+ "mcpServers": {
111
+ "shepherd": {
112
+ "command": "npx",
113
+ "args": ["-y", "@korso/shepherd"],
114
+ "env": {
115
+ "HUB_URL": "https://shepherd.example.com",
116
+ "TEAM_TOKEN": "tok_abc123"
117
+ }
118
+ }
119
+ },
120
+ "hooks": {
121
+ "PreToolUse": [
122
+ {
123
+ "matcher": "*",
124
+ "hooks": [
125
+ { "type": "command", "command": "npx -y -p @korso/shepherd shepherd-inbox-hook" }
126
+ ]
127
+ }
128
+ ]
129
+ }
130
+ }
131
+ ```
132
+
133
+ ### Codex — `UserPromptSubmit` hook
134
+
135
+ Codex uses the **same** hook contract as Claude Code (JSON on stdin, a
136
+ `hookSpecificOutput.additionalContext` reply), so the **same bin** serves it. Use
137
+ `UserPromptSubmit` — Codex's `PreToolUse` only fires for Bash, not `apply_patch`
138
+ or MCP calls. Hooks must be enabled with `features.hooks = true`. In
139
+ `~/.codex/config.toml`:
140
+
141
+ ```toml
142
+ [features]
143
+ hooks = true
144
+
145
+ [[hooks.UserPromptSubmit]]
146
+ command = ["npx", "-y", "-p", "@korso/shepherd", "shepherd-inbox-hook"]
147
+ # On Windows use command_windows instead:
148
+ # command_windows = ["cmd", "/c", "npx -y -p @korso/shepherd shepherd-inbox-hook"]
149
+ ```
150
+
151
+ ### Pi — extension
152
+
153
+ Pi has no stdin/stdout hook; it loads in-process extensions. Ship the bundled
154
+ extension into Pi's extensions dir:
155
+
156
+ ```sh
157
+ # global, applies everywhere:
158
+ mkdir -p ~/.pi/agent/extensions
159
+ cp "$(npm root -g)/@korso/shepherd/dist/inboxExtension.js" ~/.pi/agent/extensions/shepherd-inbox.js
160
+ # …or per-project: copy into .pi/extensions/ in the repo root.
161
+ ```
162
+
163
+ It runs on every user turn (`before_agent_start`), drains the same inbox, and
164
+ injects pending announcements. (Or load it ad hoc with
165
+ `pi -e /abs/path/to/dist/inboxExtension.js`.)
166
+
167
+ ### Notes
168
+
169
+ Every path is **fail-open**: a missing dir, unreachable hub, or any error means
170
+ nothing is surfaced and the tool call / turn proceeds normally — coordination
171
+ never blocks the agent. The inbox is keyed per working directory; two sessions in
172
+ the exact same directory share it (a benign edge — they're the same repo). If you
173
+ override `SHEPHERD_INBOX_DIR` on the server, set it on the hook/extension to the
174
+ same value (the Claude/Codex bin and the Pi extension both read
175
+ `SHEPHERD_INBOX_DIR`, or you can pass the dir as the first CLI arg to the bin).
176
+
177
+ ---
178
+
179
+ ## 3. MCP client configuration
180
+
181
+ ### Claude Code
182
+
183
+ > **Do not use `~/.claude/mcp.json` — Claude Code does not read it** (a config
184
+ > there loads silently into nothing). Use `claude mcp add` (user scope, applies
185
+ > everywhere) or a project-root `.mcp.json`. Confirm with `claude mcp list`,
186
+ > which should show `shepherd … ✔ Connected`.
187
+
188
+ Recommended — register once at user scope. Written as a **single line** so it
189
+ pastes cleanly into PowerShell, cmd, bash, and zsh (on PowerShell the bash `\`
190
+ line-continuation does not work). Minimal: just the two required vars (identity
191
+ is auto-detected from git):
192
+
193
+ ```powershell
194
+ claude mcp add shepherd -s user -e HUB_URL=https://shepherd.example.com -e TEAM_TOKEN=tok_abc123 -- npx -y @korso/shepherd
195
+ ```
196
+
197
+ Add any optional overrides from §2 with extra `-e` flags (e.g. `-e MODEL=claude-sonnet-4-6 -e HUMAN=daichi`).
198
+
199
+ Alternative — a `.mcp.json` at the **root of the repo you're working in**
200
+ (optional overrides shown commented-style; drop the ones you don't need):
201
+
202
+ ```json
203
+ {
204
+ "mcpServers": {
205
+ "shepherd": {
206
+ "command": "npx",
207
+ "args": ["-y", "@korso/shepherd"],
208
+ "env": {
209
+ "HUB_URL": "https://shepherd.example.com",
210
+ "TEAM_TOKEN": "tok_abc123",
211
+ "MODEL": "claude-sonnet-4-6"
212
+ }
213
+ }
214
+ }
215
+ }
216
+ ```
217
+
218
+ > Windows note: the server is a thin stdio client to the Linux-hosted hub, and
219
+ > `npx` works the same on every OS — no file paths to escape. The hub itself runs
220
+ > on Linux (Postgres), so the Windows-native durability concerns from the spike
221
+ > don't apply to clients.
222
+
223
+ ### Codex (`~/.codex/config.toml`)
224
+
225
+ Codex uses the same MCP stdio protocol but configures it in **TOML**, not JSON —
226
+ at `~/.codex/config.toml` (global) or `.codex/config.toml` in a trusted project.
227
+ The table is `mcp_servers` with an **underscore** (`mcp-servers`/`mcpServers` are
228
+ silently ignored). Either run `codex mcp add`:
229
+
230
+ ```sh
231
+ codex mcp add shepherd --env HUB_URL=https://shepherd.example.com --env TEAM_TOKEN=tok_abc123 --env PROGRAM=codex -- npx -y @korso/shepherd
232
+ ```
233
+
234
+ …or add the table directly:
235
+
236
+ ```toml
237
+ [mcp_servers.shepherd]
238
+ command = "npx"
239
+ args = ["-y", "@korso/shepherd"]
240
+ env = { HUB_URL = "https://shepherd.example.com", TEAM_TOKEN = "tok_abc123", PROGRAM = "codex", MODEL = "o4-mini" }
241
+ ```
242
+
243
+ ### Pi (`~/.pi/agent/mcp.json` or `.pi/mcp.json`)
244
+
245
+ Pi uses a JSON `mcpServers` block (project config overrides global):
246
+
247
+ ```json
248
+ {
249
+ "mcpServers": {
250
+ "shepherd": {
251
+ "command": "npx",
252
+ "args": ["-y", "@korso/shepherd"],
253
+ "env": {
254
+ "HUB_URL": "https://shepherd.example.com",
255
+ "TEAM_TOKEN": "tok_abc123",
256
+ "PROGRAM": "pi"
257
+ }
258
+ }
259
+ }
260
+ }
261
+ ```
262
+
263
+ ---
264
+
265
+ ## 4. Verify the server starts (quick smoke test)
266
+
267
+ Run with the two required vars set to confirm it connects and idles on stdin.
268
+ PowerShell (set env vars, then run):
269
+
270
+ ```powershell
271
+ $env:HUB_URL = "https://shepherd.example.com"
272
+ $env:TEAM_TOKEN = "tok_abc123"
273
+ npx -y @korso/shepherd
274
+ ```
275
+
276
+ bash/zsh: `HUB_URL=https://shepherd.example.com TEAM_TOKEN=tok_abc123 npx -y @korso/shepherd`
277
+
278
+ No stderr output and the process blocking on stdin = healthy. Press Ctrl+C to exit.
279
+
280
+ **Missing env vars:** if you omit `HUB_URL` or `TEAM_TOKEN`, you will see:
281
+
282
+ ```
283
+ [shepherd] Configuration error — missing or invalid env vars:
284
+ HUB_URL: HUB_URL is required
285
+ TEAM_TOKEN: TEAM_TOKEN is required
286
+ ```
287
+
288
+ and the process exits 1 immediately. This is by design. The optional identity
289
+ vars never cause this — they fall back to git detection / defaults.
290
+
291
+ **Wrong WORKSPACE:** if you override `WORKSPACE` to a value the hub doesn't allow, the server starts and connects but the startup auto-join is rejected (400), so every tool call (`work`, `sync`, etc.) reports "session not ready … proceeding uncoordinated". Either leave `WORKSPACE` unset (resolves to `default`) or set it to exactly match the hub's `ALLOWED_WORKSPACE`, then restart.
292
+
293
+ ---
294
+
295
+ ## Develop from source
296
+
297
+ Only needed if you're changing the MCP server itself. Clone the monorepo and
298
+ point your client at a local build instead of npx:
299
+
300
+ ```sh
301
+ git clone https://github.com/Korsoai/shepherd.git
302
+ cd shepherd
303
+ npm install
304
+ npm run build # tsc -b — compiles the workspace for dev + tests
305
+ ```
306
+
307
+ For an exact preview of the published artifact (a single self-contained bundle
308
+ with `@shepherd/shared` inlined), build the package directly:
309
+
310
+ ```sh
311
+ npm run build --workspace=@korso/shepherd # runs tsup → packages/mcp-server/dist/index.js
312
+ ```
313
+
314
+ Then use `node /absolute/path/to/shepherd/packages/mcp-server/dist/index.js` as
315
+ the `command` in your MCP config (Windows: escape backslashes in JSON).
316
+
317
+ ### Publishing a new version
318
+
319
+ ```sh
320
+ # bump "version" in packages/mcp-server/package.json, then:
321
+ npm publish --workspace=@korso/shepherd # prepublishOnly runs tsup automatically
322
+ ```
323
+
324
+ `publishConfig.access` is `public`, so the scoped package publishes publicly.
325
+
326
+ ---
327
+
328
+ ## Troubleshooting
329
+
330
+ | Symptom | Likely cause | Fix |
331
+ |---|---|---|
332
+ | `Configuration error — missing or invalid env vars` | `HUB_URL` or `TEAM_TOKEN` is absent (only these two are required) | Add the missing var(s) to your client's `env` block |
333
+ | Tools report "session not ready … proceeding uncoordinated" | Startup auto-join rejected — usually a stale `TEAM_TOKEN`, or a `WORKSPACE` override the hub doesn't allow | Re-check `TEAM_TOKEN`; leave `WORKSPACE` unset (→ `default`) or match the hub's `ALLOWED_WORKSPACE`; restart |
334
+ | Agent shows up under a surprising name/repo/branch | Identity auto-detected from git, or reused from the device-identity cache when launched outside a git work tree | Override with `HUMAN`/`REPO`/`BRANCH`/`MODEL` env vars (§2); a correct git `user.name` on the next in-repo launch refreshes the cache, or delete `~/.shepherd/identity.json` to clear it |
335
+ | `npm error 404 … @korso/shepherd` | Package not published yet, or name typo | `npm view @korso/shepherd version` to confirm it's live |
336
+ | Process exits immediately with no error | Rare; check for node version incompatibility | Requires Node 18+ (ESM support) |