Skip to main content
Glama

AgentBus

A durable coordination bus for AI agent fleets — with a human approval gate in front of anything irreversible.

Agents are increasingly allowed to deploy, send, delete, and spend. Two things break at that point, and neither is an LLM problem:

  1. Handoffs get lost. An agent finishes, hands work to the next one, and the process dies. Nothing retries, nobody notices.

  2. There is no choke point. Approval lives inside a prompt — "ask me first" — which is a suggestion, not a control, and leaves no record of who allowed what.

AgentBus is the boring infrastructure underneath: a durable queue with at-least-once delivery, a blocking human-approval gate, and an append-only audit log of every message and decision.

                    ┌──────────────────────────────┐
   agents  ────────▶│  topics · groups · leases    │────────▶  workers
   (MCP/HTTP/CLI)   │  retries · dead letters      │
                    ├──────────────────────────────┤
   agent  ─ ask ───▶│  approval gate  ── blocks ───│──▶ human (phone/dashboard)
                    ├──────────────────────────────┤
                    │  append-only audit log       │
                    └──────────────────────────────┘
                         one SQLite file

Zero runtime dependencies. No Redis, no Postgres, no broker, no cloud account. Node 24+ and a file.


Quick start

npx agentbus serve                 # http://127.0.0.1:7801 — API + dashboard

Gate a risky action behind a human

agentbus ask "Deploy api build 118 to production" \
  --action deploy.prod --risk high \
  --detail "3 commits, including a migration that drops a column." \
  --payload '{"service":"api","build":118}' \
  --wait && ./deploy.sh

ask --wait blocks and prints a link. A person opens it on their phone, approves or denies, and the command exits 0 approved · 10 denied · 11 expired — so && does exactly the right thing, and the decision is recorded against their name.

Durable work handoff

# Producer — survives a crash, deduplicated by key
agentbus pub job.render '{"file":"a.png"}' --key render-a

# Consumer — any shell command becomes a durable worker.
# Payload on stdin; exit 0 acks, non-zero retries with backoff, then dead-letters.
agentbus sub 'job.>' --group renderers --exec './render.sh'

Unacked work returns to the queue when the lease expires, so a worker that gets killed mid-job loses nothing.


Related MCP server: gotoHuman MCP Server

Giving an agent access (MCP)

// .mcp.json  — or claude_desktop_config.json
{
  "mcpServers": {
    "agentbus": {
      "command": "npx",
      "args": ["-y", "agentbus", "mcp"],
      "env": { "AGENTBUS_URL": "http://127.0.0.1:7801", "AGENTBUS_AGENT": "deploy-agent" }
    }
  }
}

The agent gets request_approval, check_approval, publish, pull, ack, nack, and stats. request_approval blocks until a human decides, and returns explicit guidance:

{
  "state": "denied",
  "approved": false,
  "decidedBy": "sami",
  "reason": "not without a backup",
  "guidance": "Not approved (denied). Do NOT proceed. Tell the user and stop."
}

A timeout returns pending, never approved — silence is never consent.


Concepts

Topics are dot-separated: deploy.prod.api. Patterns use * for one segment and > for the rest — deploy.>, *.prod.api, >.

Consumer groups each get their own copy of every matching message. Within a group, a message goes to exactly one worker at a time. A new group starts from now by default; --earliest replays the whole backlog.

Leases give a worker a visibility timeout (30s default, extendable). Miss it and the message is redelivered. After max_attempts (5) it is dead-lettered, visible on the dashboard, and replayable with one click.

Approvals carry a title, machine-readable action, free-text detail, structured payload, risk level, and optional labelled choices ("canary 10%" vs "full rollout"). They expire rather than hang forever. Every request and decision — who, when, why — lands in the audit log.

Ordering is by a SQLite sequence, not by timestamp: two messages published in the same millisecond still have a well-defined order.


HTTP API

Method

Path

POST

/v1/publish

{topic, payload, idempotencyKey?, delayMs?}

POST

/v1/pull

{group, patterns?, max?, leaseMs?, waitMs?} — long-polls

POST

/v1/ack · /v1/nack · /v1/extend

settle or extend a delivery

GET

/v1/stats · /v1/dead · /v1/audit

observability

POST

/v1/replay

requeue a dead letter

POST

/v1/approvals

create; returns a signed shareable URL

GET

/v1/approvals/:id/wait

long-poll until decided

POST

/v1/approvals/:id/decide

{decision, by, reason?, choice?}

GET

/v1/stream

SSE tail of messages and approvals

GET

/ · /a/:id

dashboard · single-approval page

Set AGENTBUS_TOKEN to require Authorization: Bearer …. Approval links carry their own HMAC token, so you can send one to a phone without handing over the API key.

Environment: AGENTBUS_URL, AGENTBUS_TOKEN, AGENTBUS_DB, AGENTBUS_SECRET, AGENTBUS_AGENT, AGENTBUS_ALLOWED_ORIGINS.

Browser-origin policy

The default is an unauthenticated server on loopback, which means a webpage you happen to have open would otherwise be able to reach it. Three rules stop that, and they apply whether or not a token is set:

  • A request carrying an Origin header is refused unless that origin is the server's own, the host in AGENTBUS_URL, or listed in AGENTBUS_ALLOWED_ORIGINS. Refused means 403 — not merely a missing CORS header, which hides the response but still performs the write.

  • POST requires content-type: application/json. Form and text/plain bodies are CORS "simple requests" that a browser sends with no preflight at all, so accepting them would reopen the hole.

  • An unexpected Host header is refused, which blocks DNS rebinding.

Non-browser clients — the CLI, the MCP server, curl, your own scripts — send no Origin and are unaffected. If you serve the dashboard on a LAN address or behind a proxy so approvals reach a phone, set AGENTBUS_URL (or --url) to that public origin; it is what approval links are built from anyway.


Development

npm test          # 66 tests, no network, no fixtures
npm run typecheck # strict, noUncheckedIndexedAccess, erasableSyntaxOnly

TypeScript runs directly on Node 24 via native type stripping — there is no build step and no compiler in the runtime path.


License

MIT

Maintenance

ActivityMaintained
ResponsivenessSyncing

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/selimam2/agentbus'

If you have feedback or need assistance with the MCP directory API, please join our Discord server