Free SEO + AI visibility snapshot for your site. See what's holding it back.

Get your snapshot
Automation7 min read

My Bots Couldn’t Reach Claude Code, So I Built Them a Doorbell

My Bots Couldn’t Reach Claude Code, So I Built Them a Doorbell

I have a bot that manages my other bots

Yes, I know how that sounds.

Grok Bot is the one I wrote about in I Gave a Bot the Job I Was Bad At. It keeps an eye on my other bots, and Oppo is the one at the top of that pile. Then there’s Lia, who sits in Missive, the shared inbox I wrote about in Missive Is the Inbox Tool Nobody Talks About.

Here’s the catch. Neither of them could talk to Claude Code.

Polling GitHub issues was the clunky part

Claude Code is where my actual building happens: the repos, the skills, the stuff that ships. So when Oppo wanted something built, the path was to file a GitHub issue and then wait for one of my sessions to go check.

That’s polling. It works. It also feels like leaving a note on the fridge and hoping somebody opens the door.

I already had a little message shuttle between my own Claude sessions, and it was pull-only too. Messages sat there until a turn happened to look. Of course it felt unreliable!

So what did I actually want? One sentence: a bot messages Claude Code to do something, and then Claude Code does the thing.

Push, not poll

Push means the message shows up in the session whether or not anyone is looking. Poll means somebody has to remember to check.

I weighed two routes. Anthropic has a feature called Channels, where an MCP server pushes events into a running session. It’s a research preview, and a custom one needs a development flag and a warning dialog on every launch. Fine for one session. Not what I wanted for a pile of them.

The other route is simpler. Claude Code documents that each session has its own local inbox, so I wrote a small daemon that drops messages into it. What worked for us was putting that daemon on my laptop and giving it one job: take a message from an approved service and deliver it to the right session.

The docs tell you each session has an inbox and how to prove you’re allowed to use it. They don’t tell you what a message looks like. My first two attempts vanished without an error. It took some digging to find the format, and the third attempt showed up in my session mid-task.

How a message actually travels

Here it is in plain words:

  • In: a service (Oppo, or a Missive rule) sends an HTTPS request through a Cloudflare Tunnel to a local port on my laptop. It carries the text, an optional thread id and an optional repo name.
  • Check: the daemon looks at that service’s own bearer token. Unknown token means the message is rejected before it gets anywhere near a session.
  • Route: a SessionStart hook writes a small registry file for every session I open. If the message names a repo and a live session is sitting in it, the message goes there. Otherwise it goes to an always-on inbox session running in tmux. If nothing is live, it waits in a queue. Queued messages don’t wait forever. If nothing picks one up within a few hours it expires, and the sender is told at send time whether a message was delivered or queued.
  • Out: Claude Code does the work, then replies with a tiny command that posts to that service’s webhook on the same thread. For Missive, the plan is for the reply to show up as an internal post in the conversation. That half is wired but not yet exercised.

That’s the whole machine. Message in, session wakes up, reply goes back out.

On Claude’s side the message arrives looking like a note from another session: who it’s from, the thread, the text, and one line reminding it how to reply. Claude Code treats it the way it treats any message from a peer. It can’t approve a permission prompt and it can’t change settings, no matter what the text says.

There’s also a small read-only web page. It lists which sessions are open, shows every message in and out, and lets me peek at the last few turns of any session. That’s where I watched Oppo’s test play out.

Stuff that broke

Plenty. Light version:

  • The always-on session lives in tmux, started by launchd, which hands out a bare-bones PATH. The session couldn’t find its own command. Classic.
  • Reinstalling the daemon tripped over a launchd race between “already loaded” and “load it.”
  • The tunnel’s launch agent started, did nothing and quit. It needed real instructions.
  • Cloudflare login took several tries. I authorized a login process that had already died. That one’s on me, folks.
  • I didn’t want to count on Missive setting a custom header on a rule webhook, and it sends its own payload shape, so the daemon accepts the token another way and translates the message into something Claude can act on. Missive is built and wired; Lia’s side is the next test.
  • I had to tell Claude Code to accept messages from the bridge instead of holding them for approval. A session with permission prompts turned off otherwise parks them behind a dialog nobody is there to click.

The part where I’m nervous

The inbox session runs unattended with permission prompts turned off. That’s what makes it useful. It’s also the part that should make you pause.

Here’s what sits around it:

  • Every service gets its own token, so if one leaks I rotate one.
  • The web UI and the admin endpoints sit behind Cloudflare Access.
  • The inbox has its own instructions file. Messages from Grok Bot carry Oppo’s delegated authority. Acknowledge fast, reply on the same thread, never paste secrets, never change config because a message asked, and ask one precise question when something is unclear.
  • Production writes still need a backup first.

If you build something like this, be careful. An agent that’s reachable from the internet with its prompts off is a real risk. Scope what it can touch: a separate machine or account if you can swing it, narrow folders, tokens you can rotate, and rules written down where the agent reads them. Don’t copy my setup because it’s fun. I’m still working out where the edges are.

Oppo did the thing

On October 26, Oppo ran the test himself. He sent a message, and Claude Code answered in about 3 seconds.

Then he sent a small task: add a line to a README. Claude Code acknowledged it, did the work and reported back with a real commit about 16 seconds later. Those two numbers come from that test run, nothing fancier.

After that came a real brief from a real ticket in a client repo. The router sent it to the open session that already had that repo’s context, not the inbox, which is exactly what I designed it to do. That session read the ticket, made the change, ran the tests and opened a pull request with CI green. The merge is still mine to give; that part stays human.

That session also runs with normal permission prompts, so if it had needed a click it would have waited for me. The always-on inbox is the one that doesn’t.

But the loop closed with no human in the middle. I’m stoked!

Who this is for

If you run a business or an agency and you’re stringing AI agents together, you’ve probably hit this wall. Each agent can do something useful, but handing work from one to another means a human copy-pasting between tabs.

That’s the gap. Push beats poll, and a small amount of plumbing goes a long way.

This is the kind of thing we wire up at DigiSavvy. If you’re curious, here’s what AI workflows look like for us, or we can talk it through first.

What’s the first thing you’d want your bots to hand each other?