Skip to main content
Glama
README.md
# agent-bus-mcp

MCP-обёртка над `~/.claude/dev-config/scripts/agent-msg.sh` — file-based conductor/sub-orchestrator
message bus для multi-agent Claude-сетапов (`~/.claude/dev-config/rules/multi-agent.md`).

Логика подписи (HMAC), хранения сообщений и path-резолва bus-директории остаётся в bash-скрипте —
сервер только шеллит его. Цель: дать доступ к bus'у любому MCP-совместимому клиенту (не только
Claude Code CLI), сохранив тот же формат сообщений и тот же ключевой материал.

## Identity binding

Один MCP-сервер = один агент. Личность (`AGENT_MSG_AS`) задаётся **только** через env сервера
при старте, не через tool-параметр — так подмена `--from` невозможна на уровне схемы, не только
проверкой (как в CLI, где расхождение `--from` с `$AGENT_MSG_AS` — отказ, но сама возможность
попытки существует).

Сервер без `AGENT_MSG_AS` в env стартовать отказывается.

## Tools

| Tool | Соответствует CLI | Заметка |
|---|---|---|
| `bus_send` | `send --from=<bound>` | `from` не параметр — берётся из bind. Получатель сверяется со списком известных имён (ключи отправителей ∪ реестр ротации ∪ admin-имена); незнакомое имя — отказ, обходится `force: true` |
| `bus_inbox` | `inbox --as=<bound>` | |
| `bus_read` | `read <id> [--archive]` | |
| `bus_archive` | `archive <id>` | |
| `bus_log` | `log` | read-only, identity не требуется |
| `bus_pending_approval` | `pending-approval` | только для identity ∈ `AGENT_BUS_ADMIN_NAMES` (default `conductor,user`) |
| `bus_verify` | `verify [id]` | |
| `bus_wait` | замена `watch` | bounded polling (default timeout 60s, max 300s) вместо бесконечного процесса — MCP tool-call не может висеть вечно как `watch` + Monitor. Курсор "уже виденного" снимается при СТАРТЕ сервера (не при первом вызове — иначе всё пришедшее между стартом и первым `bus_wait` теряется навсегда) и не переживает рестарт процесса. `include_existing: true` выдаёт и то, что лежало к старту |

`bus_read` возвращает `isError: true`, если подпись не сошлась, и ставит предупреждение ПЕРЕД
текстом сообщения. `agent-msg.sh` в этом случае пишет в stderr и завершается кодом 0, поэтому
без этой обработки подделка приезжает как обычный успешный ответ, а сноска про неё — уже после
подменённого тела.

## Setup

```bash
cd ~/Dev/mcp/agent-bus
npm install
```

В проекте, где нужен bus (например `~/Dev/Astra/Astra_2.0`), добавить `.mcp.json`:

```json
{
  "mcpServers": {
    "agent-bus": {
      "command": "node",
      "args": ["/home/oitc/Dev/mcp/agent-bus/src/index.js"],
      "env": {
        "AGENT_MSG_AS": "backend-orch",
        "AGENT_BUS_PROJECT_DIR": "/home/oitc/Dev/Astra/Astra_2.0"
      }
    }
  }
}
```

`AGENT_BUS_PROJECT_DIR` — откуда резолвится bus-директория (git common dir → project slug, та же
логика, что в `agent-msg.sh`). Нужен явно, если сервер запускается не из корня проекта (например
он всегда лежит в `~/Dev/mcp/agent-bus`, а не в самом проекте).

**Резолв делает сервер, один раз, и передаёт готовый путь в `agent-msg.sh` через `AGENT_MSG_BUS`.**
Иначе резолвов два — JS в сервере и bash в скрипте, — они расходятся на симлинках (`path.resolve`
не разворачивает, `realpath` разворачивает), и ни один не старший.

**Fail-closed на несуществующей шине.** `agent-msg.sh` делает `mkdir -p` безусловно: неверно
зарезолвленный путь не даёт ошибки, он молча заводит ВТОРУЮ шину, и все команды возвращают 0.
Так уже случилось до всякого MCP — в `~/.claude/projects/-tmp/agent-bus` лежали семь сообщений
переклички от 26.07, которых никто не получил. Поэтому сервер отказывается стартовать, если
каталога `<bus>/messages` не существует; завести шину по новому адресу осознанно —
`AGENT_BUS_ALLOW_CREATE=1`.

**Identity не кладётся в `.mcp.json` репозитория.** Файл попадает в git и разъезжается по всем
worktree — каждый sub-orch получил бы чужой `AGENT_MSG_AS` и отправлял бы под чужим именем, то
есть ровно то, против чего заведена подпись. Ставить в local scope:

```bash
claude mcp add agent-bus --scope local \
  --env AGENT_MSG_AS=<твоё-имя> \
  --env AGENT_BUS_PROJECT_DIR=/path/to/project \
  -- node /home/oitc/Dev/mcp/agent-bus/src/index.js
```

Для conductor'а (admin-роль) — `AGENT_MSG_AS=conductor`, без доп. настроек: он уже в default
`AGENT_BUS_ADMIN_NAMES`.

## Push-уведомления — через Monitor, не через MCP

`bus_wait` — bounded request/response (агент сам решает, когда ждать, и ждёт максимум 300s).
Настоящий push (событие прилетает в сессию само, без явного вызова) для Claude Code сессий уже
закрыт существующим механизмом и MCP-слой тут не нужен и не задействован:

```
Monitor(command: "agent-msg watch --as=<your-name>", persistent: true)
```

Это тот же `agent-msg.sh`, что и обёрнутые tools, но команда `watch` — бесконечный процесс
(по конструкции не ложится в MCP tool-call), а `Monitor` умеет его именно так и держать. Полное
описание — `~/.claude/dev-config/rules/multi-agent.md` §7 "PUSH через Monitor".

Итого роли не пересекаются: `bus_wait` — для MCP-клиентов без Monitor (или для одноразового
ожидания внутри более длинного tool-вызова); `watch`+`Monitor` — стандартный push-канал для
Claude Code сессий, как и раньше.

## Env vars

| Var | Default | Что |
|---|---|---|
| `AGENT_MSG_AS` | — (обязателен) | identity этого сервера |
| `AGENT_BUS_PROJECT_DIR` | `process.cwd()` | откуда резолвится bus dir (git common dir) |
| `AGENT_BUS_SCRIPT` | `~/.claude/dev-config/scripts/agent-msg.sh` | путь к обёртываемому CLI |
| `AGENT_BUS_ADMIN_NAMES` | `conductor,user` | кому доступен `bus_pending_approval`; эти же имена считаются известными получателями |
| `AGENT_BUS_ALLOW_CREATE` | — | `1` разрешает завести шину по адресу, которого ещё нет (иначе отказ при старте) |
| `AGENT_MSG_KEYS` | `~/.claude/agent-keys` | откуда берётся список известных имён для проверки получателя |
| `AGENT_MSG_BUS` / `ASTRA_AGENT_BUS` | — | прямой override bus-директории (проброс в скрипт как есть) |

## Что НЕ покрыто (сознательно)

- `keygen` — управление ключами остаётся ручной CLI-операцией, не tool (нет причины дёргать
  генерацию ключа из агентского вызова).
- Push через `watch` — не воспроизведён 1:1. `bus_wait` — bounded-poll аналог; постоянный push
  (как `agent-msg watch` под `Monitor`) для MCP-клиентов, умеющих сами держать долгоживущий
  polling-цикл (аналогично Monitor у Claude Code), достаточен.
- Иерархия / кто кому имеет право писать — не в протоколе. `AGENT_BUS_ADMIN_NAMES` даёт только
  read-доступ к approval-очереди; полный список прав (кто чью зону не трогает) остаётся
  конвенцией `multi-agent.md`, не enforced сервером.