bearier-mcp
by kumabear917
README.md
# 🐻 Bearier MCP
> **Multi-Agent Desktop Toolkit for Everyone**
[English](README.md) | [简体中文](README.zh-CN.md)
Bearier MCP lets two desktop AI agents collaborate asynchronously through a shared local task queue — **no API keys, no IDE, no cloud**. Just one SQLite file bridging agents that otherwise can't talk to each other.
Pair a **brain** agent (plans, dispatches, reviews) with a **hands** agent (executes, reports). They coordinate through Bearier without either one needing an open API, an SDK, or a single line of glue code.
```
Brain agent Hands agent
(plans, dispatches, (claims, executes,
reviews results) reports back)
│ │
│ MCP stdio │ MCP stdio
▼ ▼
┌─────────────────────────────────────────┐
│ Bearier MCP server (stdio) │
│ │
│ shared SQLite (local) │
└─────────────────────────────────────────┘
```
## Why
Most multi-agent tooling assumes you're a developer who lives in an IDE, configures API keys, and wires up coding agents inside VS Code. **Bearier is built for everyone else** — people who use desktop AI apps that have no open API, no SDK, and no extension surface beyond an MCP config file and natural-language conversation.
If your agent can load an MCP server over stdio, it can join Bearier. That's the only requirement.
Bearier itself is just a **task post office**: it reliably relays tasks, persists state and result pointers, and gets out of the way. It never reasons, never executes commands, and never bypasses either agent's own permissions.
## Features
- **6-state machine with terminal-state protection** — a finished task can never be re-claimed by another worker
- **Atomic task claiming** — `UPDATE ... WHERE status='pending'` inside a transaction; the database guarantees a single winner even under concurrent `fetch_pending_tasks`
- **Heartbeat lease renewal + dual-layer timeout** — pending tasks that nobody claims expire to `failed`; running tasks whose lease lapses are reaped to `failed/TIMEOUT`
- **Approval gating** — tasks flagged `approval_required` or `destructive` are reported as `needs_approval` and never auto-executed
- **Path safety** — `working_directory`, `context_path`, `result_path` are validated by realpath against an allowlist; symlink traversal is rejected
- **Zero heavy dependencies** — Python stdlib `sqlite3` + `mcp`; no Node, no Rust, no Docker
- **Cross-platform** — runs on macOS, Windows, and Linux; paths adapt via `os.pathsep`
## Quick start
### 1. Install Python dependencies
Bearier needs Python 3.13+. Create a venv and install:
```bash
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
```
> **macOS note (no Rust):** `mcp==1.28.1` pulls `pyjwt[crypto]` → `cryptography`, which needs Rust to build. Since stdio never uses RS256 JWT, install in two steps to skip it:
> ```bash
> pip install --no-deps mcp==1.28.1
> pip install --only-binary :all httpx-sse==0.4.3 jsonschema==4.26.0 \
> python-multipart==0.0.32 sse-starlette==3.4.6 starlette \
> typing-inspection uvicorn pyjwt==2.13.0
> ```
Verify: `python -c "from mcp.server.fastmcp import FastMCP; print('ok')"`
### 2. Configure both agents
Both agents must point at the **same `BRIDGE_DB_PATH`** — that's how they see each other's writes.
**WorkBuddy** — `~/.workbuddy/mcp.json`:
```json
{
"mcpServers": {
"bearier": {
"command": "/path/to/python",
"args": ["/path/to/bearier-mcp/src/server.py"],
"env": {
"PYTHONPATH": "/path/to/bearier-mcp/src",
"BRIDGE_DB_PATH": "/path/to/bridge.db",
"ALLOWED_WORKSPACES": "/path/to/your/workspace",
"SHARED_DIR": "/path/to/your/shared"
}
}
}
}
```
**Codex desktop** — `~/.codex/config.toml` (TOML, not JSON):
```toml
[mcp_servers.bearier]
type = "stdio"
command = "/path/to/python"
args = ["/path/to/bearier-mcp/src/server.py"]
startup_timeout_sec = 30
[mcp_servers.bearier.env]
PYTHONPATH = "/path/to/bearier-mcp/src"
BRIDGE_DB_PATH = "/path/to/bridge.db"
ALLOWED_WORKSPACES = "/path/to/your/workspace"
SHARED_DIR = "/path/to/your/shared"
```
Restart both apps after writing. In WorkBuddy, also click **Trust** on the connector management page.
### 3. Verify connectivity
Ask each agent to call `ping(caller="codex" / "workbuddy")`, then `list_ping_log(limit=5)`. If you see both pings, the bridge is live.
## Tools (7)
| Tool | Called by | Purpose |
|---|---|---|
| `ping(caller, note)` | both | connectivity check, writes `ping_log` |
| `list_ping_log(limit)` | both | view ping history |
| `db_info()` | both | database path + stats (debugging) |
| `assign_task(...)` | brain | dispatch a self-contained task |
| `fetch_pending_tasks(worker_id, limit, claim)` | hands | pull work; `claim=true` atomically transitions `pending→running` |
| `report_result(task_id, worker_id, status, ...)` | hands | report `done` / `failed` / `needs_approval` / `cancelled` / `running` (heartbeat) |
| `get_task_result(task_id)` | brain | fetch result + full event timeline |
## State machine
```
pending → running claim
pending → failed claim timeout (no worker)
running → running heartbeat / lease renewal
running → done success
running → failed failure / lease timeout (TIMEOUT)
running → needs_approval requests human approval
running → cancelled cancelled
```
`done` / `failed` / `needs_approval` / `cancelled` are **terminal** — re-submitting the same terminal state is idempotent and returns the stored result. Only `pending→running` and `running→running` change task ownership; all other transitions are reported by the holding worker.
## Automation (hands agent auto-pull)
Schedule the hands agent to periodically call `fetch_pending_tasks` so dispatched work gets picked up without human prompting. Suggested automation prompt:
> Call `fetch_pending_tasks(worker_id="workbuddy-default", limit=1, claim=true)`. If `count=0`, return silently. If a task is claimed: execute per `instruction` inside `working_directory`. On success call `report_result(status="done", task_id=..., worker_id="workbuddy-default", summary="...")`. On failure call `report_result(status="failed", task_id=..., worker_id="workbuddy-default", error_code="EXEC_ERROR", error_message="...")`. Tasks marked `destructive` or `approval_required=true` are reported as `needs_approval` and not executed.
For real-time collaboration, just tell the hands agent "pull pending tasks" — it responds in seconds. Automation is the offline backstop.
## Dashboard
Visualize the full collaboration timeline in a browser:
```bash
PYTHONPATH=src python src/dashboard.py
```
Open `http://127.0.0.1:8765`. Bound to loopback only — never exposed publicly. Each task card shows title, status badge, worker, timestamps, error (if any), and the event timeline (`created → claimed → completed`).
## Security
- **stdio only** — no network ports (dashboard binds `127.0.0.1` exclusively)
- **Never expose the database or dashboard to the public internet**
- Database lives on the local system disk — **not on ExFAT/removable drives** (multi-process SQLite locking is unreliable there)
- `working_directory` must fall inside `ALLOWED_WORKSPACES`
- `context_path` / `result_path` must fall inside `working_directory` or `SHARED_DIR` (realpath-validated, anti-traversal)
- `summary` ≤ 4 KB, `metadata` ≤ 16 KB — larger payloads are forced to disk via `result_path`
- `destructive` / `approval_required` tasks are never auto-executed by automation
## Error codes
`VALIDATION_ERROR` / `TASK_NOT_FOUND` / `INVALID_STATE` / `TASK_ALREADY_CLAIMED` / `WORKER_MISMATCH` / `TIMEOUT` / `PERMISSION_DENIED` / `PATH_VIOLATION` / `RESULT_TOO_LARGE` / `EXEC_ERROR` / `DATABASE_BUSY` / `UNKNOWN`
Uniform response shape: `{ok: bool, data: dict|null, error: {code, message, retryable}|null}`
## How it compares
Bearier occupies a lane most multi-agent tools don't:
| | Bearier MCP | agent-orchestration | beads-village |
|---|---|---|---|
| Target user | everyone (non-coders) | developers in IDEs | developers in IDEs |
| Target agent | desktop apps, no API needed | IDE coding agents (Cursor, Copilot) | IDE coding agents |
| Agent discovery | by `worker_id`, no shared `cwd` | requires same `cwd` | team-based, same project |
| Transport | stdio | stdio | stdio |
| Task timeout | dual-layer (pending + lease) | agent-level only | none |
| Approval gating | yes | research gate | no |
| Dependencies | Python stdlib only | Node 18+ | Node + Python + optional Go |
If you live in an IDE and want rich in-editor coordination, those projects fit better. If you have two desktop AI apps that only speak MCP and want them to hand off tasks — Bearier is the bridge.
## Project structure
```
bearier-mcp/
├── README.md this file (English)
├── README.zh-CN.md 中文版
├── requirements.txt pinned dependencies
├── src/
│ ├── server.py MCP server (7 tools)
│ ├── config.py config loading + path boundary checks
│ ├── errors.py 12 error codes + uniform response
│ ├── models.py 6-state machine + Task data structure
│ ├── db.py SQLite connection + schema init
│ ├── repository.py task repo (CRUD + atomic claim + timeout sweep + path validation)
│ └── dashboard.py collaboration timeline HTTP server (127.0.0.1:8765)
├── tests/
│ ├── test_ping_client.py connectivity test
│ ├── test_repository.py data layer (29 cases)
│ └── test_mcp_tools.py MCP tool end-to-end (22 cases)
└── config-examples/
├── workbuddy.mcp.json WorkBuddy config example
└── codex-desktop.config.toml Codex desktop config example
```
## Testing
```bash
PYTHONPATH=src python tests/test_repository.py # 29 cases
PYTHONPATH=src python tests/test_mcp_tools.py # 22 cases
PYTHONPATH=src python tests/test_ping_client.py # connectivity
```
## License
MIT
This server cannot be deployed
Maintenance
ActivityStale
ResponsivenessNo issues