desktop-agent-bus
# desktop-agent-bus
`desktop-agent-bus` lets multiple AI desktop applications on the **same
machine** exchange MCP messages without Redis, a database, or a hosted service.
Each application starts the same MCP server with its own role name. Every
server instance appends to and reads from one shared JSONL room file.
```text
Desktop client A Desktop client B Desktop client C
| | |
+------ desktop-agent-bus ------+
|
v
~/.desktop_agent_bus/room.jsonl
```
## Scope
- Designed for 2-5 local AI desktop clients and low-frequency collaboration.
- Uses only the Python standard library at runtime.
- Supports MCP over stdio and local HTTP.
- Routes work to named roles while retaining broadcast compatibility.
- Keeps reset rooms and audit-session exports available for later review.
- Uses POSIX file locks for cross-process write safety.
It is **not** a distributed queue, not a cross-machine transport, and not a
replacement for Redis, A2A, or a durable workflow system.
## Requirements
- Python 3.10+
- macOS or Linux for cross-process file locking
- MCP-capable desktop clients that can launch a local command or call a local
HTTP endpoint
## Quick start
1. Clone the source and install it locally:
```bash
git clone https://github.com/hemajack57-collab/desktop-agent-bus.git
cd desktop-agent-bus
python3 -m pip install .
```
2. Choose one shared room path, for example
`${HOME}/.desktop_agent_bus/room.jsonl`.
3. Configure every MCP client to launch the script with a distinct `--name`
and the exact same `AGENT_BUS_FILE`.
4. Restart clients, call `bus_who`, then call `bus_read` with `since: 0`.
Example stdio MCP configuration:
```json
{
"mcpServers": {
"desktop-agent-bus": {
"command": "desktop-agent-bus",
"args": [
"--name",
"reviewer"
],
"env": {
"AGENT_BUS_FILE": "/absolute/path/to/room.jsonl"
}
}
}
}
```
See [docs/INTEGRATIONS.md](docs/INTEGRATIONS.md) for configuration guidance and
[docs/SECURITY.md](docs/SECURITY.md) for local-file safety boundaries.
## MCP tools
| Tool | Purpose |
| --- | --- |
| `bus_send` | Send a tagged message to specific roles or broadcast. |
| `bus_read` | Read newer messages; optionally filter by recipient, sender, or tag. |
| `bus_wait` | Long-poll for a matching message, addressed to the caller by default. |
| `bus_history` | List archived rooms with metadata, or replay one by its listed name. |
| `bus_search` | Search active messages by text, sender, or tag; optionally include archives. |
| `bus_export` | Export the current or most recently closed audit session as Markdown. |
| `bus_who` | Show live roles and MCP process counts. |
| `bus_open` | Open an audit session after stating its measurable ceiling. |
| `bus_close` | Close the audit session and record an outcome. |
| `bus_reset` | Archive the current room and begin a new conversation. |
## Addressing and filtering
Leave `to` empty and a message is broadcast, exactly as in earlier releases. Set
it and only the named roles receive it when they use `bus_wait` or opt in to
recipient filtering with `bus_read`:
```json
{"content": "run the backtest", "to": ["executor"], "tags": ["task"]}
```
`bus_wait` defaults to `for_me: true`, so each role is woken only by broadcasts
and by messages addressed to it. Pass `for_me: false` to observe all traffic, or
`from_sender` / `tags` to narrow further. `bus_read` remains an all-room read by
default for compatibility; pass `for_me: true` when polling it as a role.
`from_sender` matches one exact role name. Supplying one or more `tags` returns
messages carrying any of those tags. Records written before 1.4 carry no `to`
field and are always treated as broadcasts.
```json
{"since": 41, "for_me": true, "from_sender": "executor", "tags": ["result"]}
```
Addressing is routing, not authorization: clients that share the room file can
still call `bus_read` with `for_me: false`. Use filesystem permissions and a
separate room for data that must not be visible to another local client.
## Review and archives
`bus_reset` archives a room rather than deleting it, and the archive stays
readable:
```text
bus_history list archives with counts and time spans
bus_history {"archive": "room.20260812-120000-000000.jsonl"}
replay one listed archive
bus_search {"query": "drawdown", "include_archives": true}
search active and archived rooms
bus_export Markdown record of the current or last session
```
`bus_export` uses the session's `base_msg_id` watermark, so it captures every
message from `bus_open` onward together with the stated ceiling and the recorded
outcome — a self-contained review artifact. Search text is case-insensitive and
matches message content or sender; it also accepts `from_sender`, `tags`, and a
maximum `limit`.
`bus_history` accepts only an archive name returned by its listing; it does not
accept arbitrary paths. Archive names and search results are local room data, so
do not paste untrusted room contents into a shell command.
## Efficient waits
`bus_wait` compares the room file's timestamp and size before parsing JSONL.
When no writer has changed the file, an idle poll performs only a filesystem
metadata check instead of re-reading the transcript. This keeps multiple waiting
roles inexpensive as collaboration history grows.
## Local HTTP adapter
For a client that connects to MCP through HTTP:
```bash
python3 -m desktop_agent_bus \
--http \
--host 127.0.0.1 \
--port 8765 \
--name reviewer
```
The endpoint is `http://127.0.0.1:8765/mcp`. HTTP is only an MCP adapter; it
does not make the shared room file available to other machines.
The adapter rejects all requests with an `Origin` header and rejects non-loopback
`Host` headers, so browser pages cannot call it. It also limits request bodies to
1 MiB by default. Set `AGENT_BUS_TOKEN` to require a constant-time checked
`Authorization: Bearer <token>` header from non-browser HTTP clients. Do not put
that token in a repository or a client configuration shared with untrusted users.
## Safe configuration helper
`desktop-agent-bus-install` previews a Claude Desktop MCP entry by default. When
installed, it configures the `desktop-agent-bus` console command; a source
checkout falls back to its local script:
```bash
desktop-agent-bus-install --name reviewer
```
It writes a backup and applies the change only with explicit confirmation:
```bash
desktop-agent-bus-install --name reviewer --apply
```
## Development
```bash
python3 -m unittest discover -s tests -v
python3 -m pip wheel --no-deps .
```
## License
[MIT](LICENSE)
TDQS
Scored across 10 tools
Each tool targets a distinct action: searching, reading, waiting, sending, session management, room reset, history, and role discovery. The overlapping message retrieval tools (read, wait, search) are clearly differentiated by their behavior (pull, long-poll, search).
All tools share the bus_ prefix with a short action verb. bus_who and bus_history are less verb-like but still follow the concise command-style pattern, and no style mixing (all lowercase, snake_case). Slight inconsistency in verb vs. noun, but overall predictable.
10 tools for a room-based messaging and audit system is well-scoped. Each tool covers a distinct capability with no redundant duplicates.
The set covers the full messaging lifecycle (send, read, wait), search, history, audit session lifecycle (open, close, export), room reset, and participant discovery. No obvious dead ends.