Claude Desktop ↔ Claude Code MCP Bridge
by Gowindude
README.md
# Claude Desktop ↔ Claude Code MCP Bridge
## What this is
A bidirectional [MCP](https://modelcontextprotocol.io) bridge that lets **Claude Desktop**
and **Claude Code** collaborate on a build with no human in the execution loop. Desktop
writes a plan; Code picks it up, implements it, streams progress, and submits a completion
summary; Desktop approves or rejects (with a reason); Code revises on rejection or finalizes
on approval. The two sides never talk directly — they communicate through a shared SQLite
database and a small set of markdown files under `~/.claude-bridge/`. Two stdio FastMCP
servers expose the tools each side calls.
```
~/.claude-bridge/
├── bridge.db # SQLite: projects, updates, completions, approvals
├── plans/ # Desktop writes <project_id>.md here
└── status/ # Code writes <project_id>_status.md here
claude_desktop_mcp/
├── desktop_server.py # FastMCP server registered with Claude Desktop
├── code_server.py # FastMCP server registered with Claude Code (CLI)
├── db.py # All SQLite schema + access (async, aiosqlite)
├── models.py # Shared Pydantic v2 models (rows + tool inputs)
├── config.py # Paths, server names, status constants
└── requirements.txt
```
Both servers import from `db.py` and `models.py`; no DB logic is duplicated between them.
## Install
A **dedicated virtual environment is strongly recommended** — both servers are spawned by
the host app (Claude Desktop / Claude Code), and a venv with a concrete interpreter path is
the most reliable target (especially on Windows, where the Microsoft Store "App execution
alias" Python can fail when launched by a packaged app).
```bash
# from the project directory
python -m venv .venv
# Windows
.venv\Scripts\python.exe -m pip install -r requirements.txt
# macOS / Linux
.venv/bin/python -m pip install -r requirements.txt
```
The interpreter to reference in the config snippets is then:
* **Windows:** `<project>\.venv\Scripts\python.exe`
* **macOS / Linux:** `<project>/.venv/bin/python`
(You can skip the venv and `pip install -r requirements.txt` into any Python 3.11+, but then
make sure the `command` in the configs points at *that* interpreter.)
Requires Python 3.11+. Dependencies: `mcp[cli]`, `fastmcp`, `aiosqlite`, `aiofiles`,
`pydantic>=2.0`.
Initialize the shared state directory and database (also runs automatically on the first
tool call):
```bash
# Windows
.venv\Scripts\python.exe db.py
# macOS / Linux
.venv/bin/python db.py
```
This creates `~/.claude-bridge/` with `plans/`, `status/`, and `bridge.db`.
## How to register
Both servers run over **stdio** and are launched by the host (Desktop / Code) via its MCP
config. Use the ready-made snippets in this folder:
* `claude_desktop_config.snippet.json` → Claude Desktop
* `claude_code_config.snippet.json` → Claude Code
### Claude Desktop
Add the `claude_bridge_desktop` entry to your `claude_desktop_config.json`:
```json
{
"mcpServers": {
"claude_bridge_desktop": {
"command": "C:\\Users\\quack\\documents\\projects\\claude_desktop_mcp\\.venv\\Scripts\\python.exe",
"args": ["C:\\Users\\quack\\documents\\projects\\claude_desktop_mcp\\desktop_server.py"],
"env": {}
}
}
}
```
Config file location:
| OS | Path |
|---|---|
| **Windows (standard installer)** | `%APPDATA%\Claude\claude_desktop_config.json` (e.g. `C:\Users\<you>\AppData\Roaming\Claude\claude_desktop_config.json`) |
| **Windows (Microsoft Store / packaged install)** | `%LOCALAPPDATA%\Packages\Claude_<id>\LocalCache\Roaming\Claude\claude_desktop_config.json` (e.g. `...\Packages\Claude_pzs8sxrjxfjjc\LocalCache\Roaming\Claude\...`) |
| **macOS** | `~/Library/Application Support/Claude/claude_desktop_config.json` |
| **Linux** | `~/.config/Claude/claude_desktop_config.json` |
> If Claude Desktop was installed from the Microsoft Store, it uses a *virtualized* Roaming
> folder under `%LOCALAPPDATA%\Packages\Claude_<id>\LocalCache\Roaming\Claude` — editing the
> standard `%APPDATA%\Claude` path will have no effect. Merge into the existing file (it may
> already contain other `mcpServers`); don't overwrite it.
Restart (fully quit and reopen) Claude Desktop after editing — the config is read at launch.
### Claude Code (CLI)
The simplest way is the CLI, which writes the right config for you:
```bash
claude mcp add claude_bridge_code --scope user -- "C:\Users\quack\documents\projects\claude_desktop_mcp\.venv\Scripts\python.exe" "C:\Users\quack\documents\projects\claude_desktop_mcp\code_server.py"
```
`--scope user` makes the bridge available in every project. Restart Claude Code (or start a
new session) afterward so it loads the server.
Or add the `claude_bridge_code` entry manually to the appropriate MCP config (e.g.
`.mcp.json` in your project root, or your user-level Claude Code config):
```json
{
"mcpServers": {
"claude_bridge_code": {
"command": "C:\\Users\\quack\\documents\\projects\\claude_desktop_mcp\\.venv\\Scripts\\python.exe",
"args": ["C:\\Users\\quack\\documents\\projects\\claude_desktop_mcp\\code_server.py"],
"env": {}
}
}
}
```
> **Path note:** the `command` points at the project's venv interpreter
> (`.venv\Scripts\python.exe` on Windows, `.venv/bin/python` on macOS/Linux) — the one you
> installed the dependencies into. Avoid the bare Microsoft Store `python.exe` alias here;
> packaged host apps may fail to launch it. JSON requires escaped backslashes (`\\`) in
> Windows paths.
## Tool argument shape
> **Important:** every tool whose table lists parameters takes a **single object
> argument named `params`** — the tool input is one Pydantic model, so the JSON the MCP
> client sends is `{"params": { ... }}`, not flat top-level fields. Tools that take no
> parameters (`bridge_get_approval_queue`, `bridge_list_projects`,
> `bridge_get_pending_plan`) are called with `{}`.
For example, `bridge_send_plan` is invoked as:
```json
{ "params": { "title": "Add CSV export", "plan_markdown": "# Goal\n..." } }
```
In practice you just tell Claude what to do ("send this plan to Code") and it fills in the
`params` object; the shape above is what travels over the wire.
## Typical workflow
The examples below show each tool with its `params` payload.
1. **Desktop registers a plan.** In Claude Desktop, call `bridge_send_plan`:
```
bridge_send_plan params={"title": "Add CSV export", "plan_markdown": "# Goal\n..."}
→ { "project_id": "ab12cd34ef56", "status": "pending", "plan_path": "...", ... }
```
2. **Code picks it up.** In Claude Code:
```
bridge_get_pending_plan (no params) → returns project_id, title, full plan markdown
bridge_claim_project params={"project_id": "ab12cd34ef56"} → status: in_progress
```
3. **Code streams progress** while implementing:
```
bridge_send_update params={"project_id": "ab12cd34ef56", "message": "Wrote exporter module"}
bridge_send_update params={"project_id": "ab12cd34ef56", "message": "Added tests"}
```
4. **Code submits completion:**
```
bridge_send_completion params={
"project_id": "ab12cd34ef56",
"files_created": ["export.py", "test_export.py"],
"files_modified": ["cli.py"],
"summary": "CSV export end to end",
"caveats": null} → status: awaiting_approval
```
5. **Desktop reviews:**
```
bridge_get_approval_queue (no params) → project + completion summary
bridge_get_project_updates params={"project_id": "ab12cd34ef56"}
```
6. **Desktop decides:**
```
bridge_approve params={"project_id": "ab12cd34ef56"} → status: approved
# or
bridge_reject params={"project_id": "ab12cd34ef56", "reason": "Handle empty rows"} → status: rejected
```
7. **Code learns the outcome** by polling:
```
bridge_get_approval_status params={"project_id": "ab12cd34ef56"}
```
* **Approved** → the project is finalized to `complete`.
* **Rejected** → the response includes the rejection `reason`. Code calls
`bridge_reset_for_revision` with `params={"project_id": "ab12cd34ef56"}` (status →
`in_progress`), revises,
and submits a new completion. The loop repeats until approved.
### Status lifecycle
```
pending → in_progress → awaiting_approval → approved → complete
▲ │
└──────── rejected ◄───────────┘ (bridge_reset_for_revision)
```
Each tool validates the current status before transitioning and returns a clear
`ERROR: ...` string if the transition is invalid.
### Tool reference
**Desktop server (`claude_bridge_desktop`)**
| Tool | Purpose |
|---|---|
| `bridge_send_plan` | Register a new project + plan (writes `plans/<id>.md`) |
| `bridge_get_approval_queue` | List projects awaiting approval, with completion summaries |
| `bridge_get_project_updates` | All progress updates for a project |
| `bridge_approve` | Approve completed work |
| `bridge_reject` | Reject with a reason; sends work back |
| `bridge_get_project_status` | Full project state (status, updates, completion, decision) |
| `bridge_list_projects` | All projects, newest first |
**Code server (`claude_bridge_code`)**
| Tool | Purpose |
|---|---|
| `bridge_get_pending_plan` | Retrieve the oldest pending plan + markdown |
| `bridge_claim_project` | Mark a pending project `in_progress` |
| `bridge_send_update` | Append a progress message (also writes `status/<id>_status.md`) |
| `bridge_send_completion` | Submit finished-work summary → `awaiting_approval` |
| `bridge_get_approval_status` | Poll for approval/rejection (finalizes `approved` → `complete`) |
| `bridge_reset_for_revision` | Re-enter `in_progress` after a rejection |
## State directory
`~/.claude-bridge/` (on Windows, `C:\Users\<you>\.claude-bridge\`):
* **`bridge.db`** — SQLite with four tables: `projects`, `updates`, `completions`,
`approvals`. Foreign keys link `updates`/`completions`/`approvals` back to `projects`.
* **`plans/<project_id>.md`** — the exact plan markdown Desktop submitted.
* **`status/<project_id>_status.md`** — a running log: one timestamped line per progress
update, plus a structured `## Completion` block each time Code submits a completion.
You can safely inspect these files by hand. To reset everything, delete `bridge.db` and the
contents of `plans/` and `status/`, then run `python db.py` again.
## HTTP API Server
A lightweight FastAPI server (`api_server.py`) exposes the bridge database over `http://localhost:7823`. A browser-based dashboard artifact in Claude Desktop can poll it for live project status without any MCP or stdio involvement.
### How to start
**Option A — visible terminal:**
```
start_api.bat
```
**Option B — hidden background process:**
Double-click `start_api.vbs` (no terminal window appears). It also runs automatically on login via the Windows startup folder.
### Endpoints
| Method | Path | Description |
|---|---|---|
| `GET` | `/health` | Server health + DB path |
| `GET` | `/projects` | All projects, newest first |
| `GET` | `/projects/{id}` | Full project detail: project + updates + completion + approval |
| `POST` | `/projects/{id}/approve` | Approve a project (must be `awaiting_approval`) |
| `POST` | `/projects/{id}/reject` | Reject a project; body: `{"reason": "..."}` |
### Example curl calls
```bash
curl http://localhost:7823/health
curl http://localhost:7823/projects
curl http://localhost:7823/projects/abc123def456
curl -X POST http://localhost:7823/projects/abc123def456/approve
curl -X POST http://localhost:7823/projects/abc123def456/reject -H "Content-Type: application/json" -d "{\"reason\": \"needs better error handling\"}"
```
### Auto-start on login
`start_api.vbs` is copied to `%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\` so the API server starts automatically every time you log in alongside the bridge loop. To disable, delete `start_api.vbs` from that folder.
## Running the Persistent Loop
The bridge loop keeps Claude Code running in the background so Claude Desktop can push plans at any time and Code picks them up automatically — no manual intervention needed on the Code side.
### Files
| File | Purpose |
|---|---|
| `run_loop.bat` | Activates the venv, launches Claude Code with the bridge prompt, and auto-restarts on exit |
| `start_bridge.vbs` | Launches `run_loop.bat` as a hidden background window (no terminal stays open) |
| `stop_bridge.bat` | Kills the running Claude Code process |
| `bridge_status.bat` | Shows running Claude processes and the last 10 lines of `loop.log` |
### How to start
**Option A — visible terminal (useful for debugging):**
```
run_loop.bat
```
**Option B — hidden background process:**
Double-click `start_bridge.vbs` (or invoke it via `wscript.exe start_bridge.vbs`). No terminal window appears.
### How to stop
Run `stop_bridge.bat` or:
```
taskkill /F /IM claude.exe /T
```
### How to check status
Run `bridge_status.bat` — it lists active `claude.exe` processes and prints the last 10 lines of the log file.
### Startup registration
`start_bridge.vbs` is copied into `%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\` so the loop starts automatically every time you log in. To disable autostart, delete `start_bridge.vbs` from that folder.
### Log file
Every time the loop restarts, a timestamped line is appended to:
```
C:\Users\quack\.claude-bridge\loop.log
```
This lets you see how often Claude Code was restarted and when each session began.
## Troubleshooting
* **`ERROR: cannot <action> project in status '<x>'; expected '<y>'.`** — A status
transition was attempted out of order (e.g. completing a project that was never claimed,
or approving one that isn't `awaiting_approval`). Check the current state with
`bridge_get_project_status` / `bridge_list_projects` and follow the lifecycle above.
* **`ERROR: project not found: <id>`** — Wrong/typo'd `project_id`. Use
`bridge_list_projects` to find the correct id.
* **`ERROR: a non-empty 'reason' is required to reject a project.`** — `bridge_reject`
needs a `reason`; supply one.
* **`ERROR: plan file missing for project <id>: <path>`** — The `plans/<id>.md` file was
deleted out from under the DB. Re-send the plan with `bridge_send_plan` (creates a new
project) or restore the file.
* **Database is locked.** SQLite serializes writers; the bridge uses short-lived async
connections so this is rare. If it happens, ensure you don't have an external tool holding
a long transaction on `bridge.db`, then retry — the offending tool will simply return an
`ERROR: ...` string and can be called again.
* **Server not found / tools don't appear.** Confirm the `command` path points at the
interpreter where dependencies are installed (`pip show mcp`), that the `args` path to the
`*_server.py` file is correct and absolute, and that you restarted the host (Desktop) or
re-ran `claude mcp add` (Code). On Windows, JSON requires escaped backslashes (`\\`) in
paths.
* **Nothing happens after `bridge_send_plan`.** Code only acts when *you* ask it to call
`bridge_get_pending_plan` (and the rest). The bridge is pull-based polling, not a push
notification system.
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues