// BLOG

I Was the Message Bus

I Was the Message Bus

The awkward part of using several coding agents was not opening several terminals. It was me sitting between them, copying one answer into another window and trying to remember which session had seen which piece of the problem.

That became particularly silly while working on Ashes of the Unbound. A rendering investigation could involve Unreal code, captured evidence, a long issue note and several competing explanations. One session might be good at reading the shader path, another at challenging the conclusion, and a third at making the change. I was still the only thing connecting them.

On 5 May I started a small desktop client called cross-mcp chat. The directory still has that name. The application does not.

The first IRC-style client

The first version was deliberately modest. Another tool already gave Claude and Codex instances a shared SQLite message bus, so I built an IRC-like client around it with Tauri, Rust and Vue.

Channels lived on the left. The conversation occupied the middle. Participants appeared on the right. Messages could contain Markdown, code, diagrams and replies without turning the terminal into a wall of escaped JSON.

It did not run agents. It did not own routing. It opened the existing database read-write and promised not to alter its schema. Deleting a message left a tombstone so replies did not lose their parent.

Mostly, it gave me somewhere to see the work as one conversation instead of reconstructing it from terminal scrollback.

What the chat view solved

The work was already divided into roles even though the software did not know that yet.

An investigator needed the issue record and the evidence. A coder needed a bounded change. A reviewer needed the proposed patch and the original claim. A test runner needed the exact state to reproduce. Giving every session the entire history made each turn expensive and muddy. Giving them too little meant I had to fill the gaps by hand.

Channels offered a rough first boundary. They let the game investigation, implementation work and review discussion remain visible without becoming one prompt. The shared record also made direct replies much less fragile than pasting excerpts between unrelated sessions.

This was still only a nicer view of the same substrate. Presence came from whatever another process had recently written. A channel row did not mean an agent had actually received anything. The client could display a message, but it could not tell me whether that message was queued, delivered, being answered or stranded behind a dead process.

Those missing states did not matter much while I was the router. They became impossible to ignore once the app began launching agents itself.

What the first version exposed

There is a temptation to make the first version sound more deliberate than it was. It was not a grand multi-agent architecture. It was a chat window I built because manually relaying technical work had become irritating.

That narrow beginning was useful. It separated the immediate problem—seeing and sending messages—from the much larger one of owning a working session. I could use the client, notice exactly where I was still intervening, and turn each intervention into a requirement rather than inventing a workflow on a whiteboard.

Within the next couple of weeks the application could spawn Claude, Codex and OpenCode sessions, keep their configurations, route explicit requests and show partial output. That version got its first serious workout on the unresolved first-connection SkyLight investigation in Ashes.

It worked well enough to prove the workflow and badly enough to make the next rewrite obvious.

The next entry follows that dogfood run: Running a Multi-Agent Investigation in SkyeEngine Code.