Foreman verified by 'strings' on the claude binary that the sandbox-isolation env var is CLAUDE_CONFIG_DIR, not CLAUDE_HOME (2026-05-06 sandbox spike). identity.ts now resolves in this order: 1. PING_AGENT_IDENTITY 2. $CLAUDE_CONFIG_DIR/ping-agent (preferred — what Claude Code reads) 3. $CLAUDE_HOME/ping-agent (compat for the previously-documented var) 4. ~/.ping-agent Two new tests cover the precedence (CLAUDE_CONFIG_DIR > CLAUDE_HOME) and the fallback path (CLAUDE_HOME still works when CLAUDE_CONFIG_DIR is unset). Existing tests updated to use CLAUDE_CONFIG_DIR. 37 tests pass. README + install.sh: sandbox launch examples updated to set CLAUDE_CONFIG_DIR. Note: agent-ping's identity-resolution PR (#1, merged) has the same bug and should also be patched. Filing a follow-up.
98 lines
3.2 KiB
Markdown
98 lines
3.2 KiB
Markdown
# agent-watcher-mcp (Layer 2)
|
|
|
|
The MCP Watcher: a Claude Code stdio MCP server that surfaces ping-inbox
|
|
events into your Claude Code session via Channels, and exposes reply tools
|
|
(`ack` / `respond` / `mark_handled`) so Claude can react.
|
|
|
|
Spec: `../spec/agent-watcher.md` §4.
|
|
|
|
## What it does
|
|
|
|
1. Resolves identity (env / `$CLAUDE_HOME/ping-agent` / `~/.ping-agent`).
|
|
2. Writes `pings/.<agent>.watcher-active` so the existing agent-ping
|
|
`UserPromptSubmit` hook stands down on this host.
|
|
3. Watches `pings/<agent>.inbox` via inotify (chokidar).
|
|
4. On change, drains unread pings (since HWM), applies sentinel
|
|
deferral (warn-after-3), and emits each as a
|
|
`notifications/claude/channel` event.
|
|
5. Exposes three MCP tools so Claude can ack, respond, or mark a ping
|
|
handled.
|
|
6. Removes the sentinel on graceful shutdown.
|
|
|
|
## Install
|
|
|
|
Per CLAUDE.md rule #2, the agent does not install on itself. A human
|
|
runs:
|
|
|
|
```bash
|
|
cd /path/to/agent-watcher/mcp-watcher
|
|
./install.sh
|
|
```
|
|
|
|
This installs deps, builds, symlinks the binary into `~/.local/bin/`,
|
|
adds `.stignore` patterns, and prints the `mcp.json` snippet to paste
|
|
into Claude Code's config.
|
|
|
|
## Launch
|
|
|
|
Channels is in research preview. Start Claude Code with:
|
|
|
|
```bash
|
|
claude --dangerously-load-development-channels server:agent-watcher
|
|
```
|
|
|
|
## Sandbox testing
|
|
|
|
To run a separate Claude Code session with a different identity (so it
|
|
won't compete with the default `~/.ping-agent` for inbox reads):
|
|
|
|
```bash
|
|
mkdir -p ~/.claude-sandbox
|
|
echo sandbox > ~/.claude-sandbox/ping-agent
|
|
CLAUDE_CONFIG_DIR=~/.claude-sandbox \
|
|
claude --dangerously-load-development-channels server:agent-watcher
|
|
```
|
|
|
|
The sandbox session reads `pings/sandbox.inbox` and writes
|
|
`pings/.sandbox.watcher-active` — fully isolated from prod.
|
|
|
|
## How it interacts with agent-ping
|
|
|
|
The `agent-ping` UserPromptSubmit hook checks for the sentinel file
|
|
`pings/.<agent>.watcher-active` at startup. If the file is present and
|
|
the PID inside is alive, the hook stands down — the watcher is the
|
|
delivery primitive. If the watcher exits (graceful or crash with stale
|
|
sentinel cleared by the hook's age check), the hook resumes
|
|
async-on-next-prompt delivery.
|
|
|
|
This means **you can run with the watcher OR the hook OR both
|
|
configured** — the sentinel arbitrates.
|
|
|
|
## Tests
|
|
|
|
```bash
|
|
npm test # unit tests via vitest (35 currently passing)
|
|
npm run typecheck # tsc --noEmit
|
|
npm run build # tsc → dist/
|
|
```
|
|
|
|
## Observability
|
|
|
|
- Logs to stderr (visible via `claude --debug` or the Claude Code MCP
|
|
debug log at `~/.claude/debug/<session-id>.txt`).
|
|
- Sentinel file content includes PID + start time for the hook's
|
|
age/liveness check.
|
|
|
|
## Limitations / v2
|
|
|
|
- Sentinel hook coexistence requires the agent-ping hook to know how
|
|
to read the sentinel. PR pending against `agent-ping` to add the
|
|
check.
|
|
- No reconnection / restart on Claude Code session restart — Claude
|
|
Code spawns the subprocess anew each session, so the watcher
|
|
re-drains from HWM cleanly.
|
|
- One watcher process per agent identity. Two sessions with the same
|
|
identity contending on the same inbox is undefined behaviour
|
|
(use `CLAUDE_HOME` to scope identities).
|
|
- Channels is research preview; if the API changes, expect to update
|
|
the meta-key sanitization or notification shape.
|