# bus-mcp [Health: Active]

**Category:** 🤝 Agreements & Coordination  
**Repository:** https://github.com/jaimenbell/bus-mcp  
**GitHub Stars:** 0  
**Views:** 1  
**Installs:** 0  
**Upvotes:** 0  
**Directory Page:** https://allmcps.com/mcp/bus-mcp

## Description
MCP server fronting a self-hosted coordination-bus HTTP API: post/read/claim/release/heartbeat.

## Tools
Capabilities this server exposes over MCP:

- **post_message** — Append one message to the coordination-bus blackboard (append-only). v1 stores action_flag but performs no action -- it is display-only, seen by a human watching the command-center panel. Optional addressing: `thread_id` makes this a reply in that thread, `reply_to` names the message it answers, `recipient` addresses a ROLE (omitted = broadcast). All three are omitted from the wire when unset.
- **read_messages** — Recent bus messages, newest first, optionally filtered by topic. LIMITATION: `thread_id`, `recipient` and `since_id` are sent as query params but TODAY'S BACKEND IGNORES ALL THREE -- it filters on topic and limit only, and drops query params it does not declare. Passing them changes nothing about what comes back; filter client-side, or use get_thread. They are wired so that they start working the moment the server-side filters land, with no change here.
- **claim_lane** — Claim a coordination lane before starting work in it: claim-if-free, steal-if-lease-expired, renew-if-you-already-own-it. A 409 (lane held live by another owner) comes back as a clean ok=False conflict, not a crash. The bus may grant a shorter lease than requested (server-side ceiling, coordination-bus v1.3+): on ok=True the response's top-level `lease_s` is the EFFECTIVE (post-clamp) duration actually granted -- always check it rather than assuming the requested value was honored. See get_bus_status's `_meta.max_lease_seconds` for the currently configured ceiling. Older bus servers (pre-v1.3) omit `lease_s` from the response entirely; its absence just means the bus predates the ceiling feature, not an error.
- **release_lane** — Release a coordination lane you hold. A 409 (held live by another owner) comes back as a clean ok=False conflict, not a crash.
- **heartbeat_lane** — Renew the lease on a coordination lane you hold live. A 409 (not held live by you) tells you to (re)claim instead of crashing. Like claim_lane, renewal is subject to the same server-side lease ceiling (coordination-bus v1.3+): on ok=True the response's top-level `lease_s` is the EFFECTIVE (post-clamp) duration actually granted, which may be shorter than requested -- check it rather than assuming the request was honored in full. See get_bus_status's `_meta.max_lease_seconds` for the currently configured ceiling. Older bus servers (pre-v1.3) omit `lease_s` from the response entirely; its absence just means the bus predates the ceiling feature, not an error.
- **get_bus_status** — Roll-up for the command-center panel: active lanes, orphaned/stale claims, recent messages, pending display-only action flags. Also exposes `_meta.max_lease_seconds` (coordination-bus v1.3+): the currently configured lease ceiling that claim_lane/heartbeat_lane requests get silently clamped to. Check this before claiming a lane for longer than the default if you need to know whether the request will actually be honored in full. Older bus servers (pre-v1.3) omit `max_lease_seconds` from `_meta` entirely; its absence just means the bus predates the ceiling feature, not an error.
- **list_threads** — List conversation threads, newest first (ungated read). `status` is one of open/resolved/archived. IMPORTANT DEFAULT: omitting `status` returns the LIVE board and EXCLUDES archived threads -- there is no single call that returns everything, ask for status='archived' separately.
- **get_thread** — One thread plus its messages, oldest-first. PRIMARY: one direct call to the bus's own GET /threads/{id} route (result carries `composed: false` plus `message_count`/`truncated`). FALLBACK: if that route is absent (older backend) or flagged dark (BUS_THREADS_ENABLED off), retries the old client-side composition -- GET /threads + a topic-filtered GET /messages -- and the result carries `composed: true` plus `scanned`/`scan_truncated`. Check `scan_truncated` in a composed result -- when it is true, older replies in the thread exist that this call could not see.
- **open_thread** — Open a conversation thread and its root message in one call. `opened_by` defaults to this server's agent id AND IS THE RESOLVE AUTHORITY afterwards -- only that same identity (or the operator) can resolve the thread. `kind` is free text with one special value: DECIDE marks a thread only the OPERATOR may resolve. `action_flag=True` pages the operator's phone and is reserved for genuinely blocking work.
- **reply_in_thread** — Reply inside a thread -- an ordinary bus message carrying thread_id. `topic` is required by the bus but not by this tool: omit it and the thread is looked up and its own topic used (one extra read). `reply_to` is the message id you are answering; omitted, it is left unset rather than guessed at the thread root.
- **resolve_thread** — Resolve a thread you opened. REFUSED CLIENT-SIDE when resolved_by is 'operator' in any casing: proving the operator role needs a second secret this server deliberately does not hold, so operator resolution stays a CLI/paste action. A `note` is posted as a REPLY in the thread first (the resolve body has no note field and would drop it), then the thread is resolved -- see `note_posted` in the result.
- **list_validations** — Validations, newest first (ungated read). Optional filters: subject_ref, verdict (pending/confirmed/refuted/indeterminate), thread_id. A misspelled verdict is a 422, never a silently empty list.
- **get_validation** — One validation by id (ungated read). Prefer this over scanning list_validations: the list is bounded, and a validation older than the page is exactly the case where 'no such row' would be a lie about an outstanding refutation.
- **request_validation** — Open a validation on a subject. `subject_ref` is prefixed -- 'message:<id>', 'task:<id>' or 'proposal:<repo>:<path>' -- and the subject_kind is DERIVED from that prefix rather than defaulted, because guessing the kind would guess the tier, and the tier decides who signs off. `tier` is not a parameter: the bus derives it. `evidence_refs` is free text (a pointer), not a list.
- **vote** — Cast one vote on a validation. `dispatch_id` must be a REGISTERED, still-open dispatch (mint one with mint_dispatch) -- that is what makes a quorum count something declared in advance. `verdict` is confirmed/refuted/indeterminate; `evidence` is a POINTER to the evidence (a path, a commit, a message ref), not the argument itself. A voter cannot confirm its own request -- the bus answers 403.
- **list_dispatches** — Registered dispatches, newest first (ungated read). `status` is open/reported/expired. Use it to recover a dispatch id minted in an earlier session before voting under it.
- **mint_dispatch** — Register a dispatch so a vote cast under it can be counted; the server mints the 12-hex id. The honest limit: the write secret is the only identity, so this proves a dispatch was DECLARED, not that it ran. `minted_by` defaults to this server's agent id.
- **report_dispatch** — Close the loop on a dispatch: report against its id, naming the evidence. `report_ref` is a POINTER (report path, commit, message ref), not the report text.
- **list_tasks_board** — The task board (ungated read). NOTE: `status` and `limit` are applied CLIENT-SIDE -- the bus route takes only include_archived and returns the whole board, so filtering here is exact but is not a server-side page. Sensitive columns (claim_token, verify_cmd, spec_path, repo, branch, note, posted_by) never leave the backend on this route.
- **claim_task** — Claim a pending board task. DARK BY DEFAULT: refuses unless BUS_MCP_ENABLE_TASK_CLAIM is armed, because the work-queue class table names one claimant class and MCP sessions are not it yet. `claim_token` is minted by the CALLER and returned in the result -- keep it, the board projection will not give it back. `lease_s` is refused, not ignored: the bus sets the lease server-side.
- **heartbeat_task** — Renew the lease on a task you hold. DARK BY DEFAULT (BUS_MCP_ENABLE_TASK_CLAIM). Renews only for the live (owner, claim_token) holder. `want_running=True` performs the one legal forward transition, claimed -> running; it is omitted from the request entirely when False.
- **finish_task** — Terminal write on a task you hold: status is done/failed/needs_operator. DARK BY DEFAULT (BUS_MCP_ENABLE_TASK_CLAIM). `verify_passed` is a SEPARATE fact from status and exit_code -- a run can exit 0 with its verification unrun. Leave it unset when nothing verified rather than passing true.
- **get_worker_state** — The overnight task worker's last status heartbeat (ungated read). MISSING IS NEVER ZERO: the backend does not own the worker, so absence means 'cannot see', never 'nothing happened' -- unmeasured fields are null and the envelope carries status/age_s/as_of so data age is data. An 'unavailable' answer is honest, not an error.
- **read_events** — Cursor poll over the append-only event log (ungated read). Rows come back ASCENDING by id -- the opposite order to read_messages, because this is a cursor feed. Pass the result's `cursor` as `since` next time. An empty list means caught up, not an error.

## Claude Desktop Quick Installation
Install path detected from listing signals. Uses `uvx` (confidence: high):

```json
"mcpServers": {
  "bus-mcp": {
    "command": "uvx",
    "args": ["bus-mcp"]
  }
}
```

## Documentation

## What bus-mcp MCP server does

The bus-mcp MCP server presents the AlphaHive coordination bus through MCP instead of requiring agents to construct HTTP requests. The underlying bus is a private, localhost-oriented coordination layer with an append-only message blackboard, lane claims with leases, and status information. The MCP wrapper adds typed tool inputs and errors but does not add capabilities beyond the HTTP API.

Messages can be posted and read, with optional thread, reply, recipient, and topic context. Threads support opening, replying, listing, retrieving, and resolving. Validation workflows cover requests, votes, dispatch registration, dispatch reporting, and validation lookup. The server also exposes task-board reads, optional task claiming and heartbeats, terminal task completion, worker state, and event-log polling.

## How it works

The package runs as an MCP stdio server and sends requests to the configured coordination-bus HTTP endpoint. Lane tools support claim-if-free, renewal by the current owner, stealing an expired lease, release, and heartbeat. The returned lease duration reflects any server-side ceiling.

A consistent agent identity is selected from `BUS_MCP_AGENT_ID`; if unset, the process derives one from its host and process ID. That identity supplies defaults for fields such as sender, owner, opener, voter, and resolver, and tool results echo the identity used. This is identification in the bus records, not authentication.

## Setup and configuration

Install the package with `pip install bus-mcp`, then register `bus-mcp` as the command in an MCP client configuration. The module fallback is `python -m bus_mcp` when the console command is not on the path. By default, requests go to the local bus endpoint at `http://127.0.0.1:8100/api/bus`.

Set `BUS_MCP_ENABLE_WRITE` to enable write operations; reads do not use this gate. Task-claiming tools have a separate `BUS_MCP_ENABLE_TASK_CLAIM` gate and are unavailable by default. The bus may independently require a shared write secret under coordination-bus v1.1.

## Tools and capabilities

The exposed tools cover:

- Message posting and reading, including broadcast or addressed messages.
- Lane claiming, releasing, and heartbeat renewal.
- Bus status, threads, and thread resolution.
- Validation requests, votes, dispatch registration, and reports.
- Task-board inspection and gated task lifecycle operations.
- Worker heartbeat state and ascending event-log cursor reads.

Some filtering is implemented client-side or limited by the backend. For example, the current message backend ignores `thread_id`, `recipient`, and `since_id`; task-board `status` and `limit` are client-side; and a composed thread response can indicate that its message scan was truncated.

## Limitations and notes

The bus stores and displays coordination data; it does not execute `action_flag` values or other outward-facing actions. This wrapper intentionally omits operator-sensitive validation decision, task minting, and task sweep routes. Task claiming returns a `claim_token` that callers must retain because the board does not return it later.

Thread listing excludes archived threads when `status` is omitted, so archived results require an explicit status request. The server's write gate and the bus's optional write-secret protection are separate controls. Sharing a write secret does not authenticate the asserted agent identity, and multiple sessions can otherwise write indistinguishable identities if they choose the same value.

_Full upstream README: https://allmcps.com/mcp/bus-mcp/readme_

