Skip to content

Kongnitive/codex-buddy-bridge

Repository files navigation

Codex Buddy Bridge

Codex Buddy Bridge connects Codex Desktop to a small ESP32 hardware buddy. It keeps the device updated with the current Codex work state, such as idle, busy, waiting for user input, approval prompts, recent activity, and token counters.

The project is made of two local pieces:

  • a Codex MCP plugin that exposes buddy_* tools and writes the latest state
  • a Python BLE bridge that sends that state to compatible ESP32 hardware

The hardware side follows the Claude Desktop Buddy Nordic UART protocol, so a device or firmware that works with Claude Desktop Buddy can be used for testing this bridge.

Start here: QUICKSTART.md

中文文档:README.zh-CN.md

Architecture

flowchart LR
  subgraph Codex["Codex Desktop"]
    S1["Session A"]
    S2["Session B"]
    MCP["MCP tools"]
    Hooks["Plugin hooks"]
  end

  subgraph Files["Shared bridge files"]
    State["bridge/state.json"]
    Activity["bridge/activity.json"]
    Command["bridge/command.json"]
    Usage["bridge/usage_state.json"]
  end

  subgraph Bridge["Single persistent bridge.py process"]
    Heartbeat["Heartbeat loop"]
    Commands["Command loop"]
    Tokens["Token tracker"]
    BLE["Nordic UART BLE client"]
  end

  Hardware["Claude Code Buddy compatible ESP32 hardware"]
  CodexDB["%USERPROFILE%/.codex/state_5.sqlite"]

  S1 --> MCP
  S2 --> MCP
  MCP --> State
  MCP --> Command
  Hooks --> Activity
  CodexDB --> Tokens
  Activity --> Heartbeat
  State --> Heartbeat
  Command --> Commands
  Tokens --> Usage
  Tokens --> State
  Heartbeat --> BLE
  Commands --> BLE
  BLE <-->|"NUS: RX write / TX notify"| Hardware
Loading

The bridge sends newline-delimited JSON heartbeat snapshots over BLE every 10 seconds. The ESP32 firmware can use those snapshots to show idle, busy, attention, approval prompt, and transcript states.

What this is for

Use this project when you want a physical desktop indicator for Codex:

  • show whether Codex is currently working, idle, blocked, or waiting for you
  • surface short task summaries without keeping the Codex window in focus
  • forward approval prompts to hardware buttons and read approve/deny responses
  • display recent tool activity and token usage while a bridge process runs

It is intentionally local-first. The MCP server updates JSON files in this repo, and the bridge reads those files and pushes compact heartbeat snapshots over BLE. No cloud service is required by this project.

Hardware testing

For hardware-side testing, use a device or firmware compatible with Claude Desktop Buddy. Codex Buddy Bridge scans for a BLE device whose name starts with Claude by default and talks to it through the Nordic UART Service.

If you already have Claude Desktop Buddy hardware working, you can usually test this project directly by starting the bridge and checking device status:

buddy_start_bridge
buddy_get_bridge_status
buddy_get_device_status

Layout

.codex-plugin/plugin.json  Codex plugin manifest
.mcp.json                  MCP server registration
mcp/server.mjs             Node MCP tool server
bridge/bridge.py           Python BLE Nordic UART bridge
bridge/config.json         Local bridge config
bridge/state.json          Current heartbeat snapshot
bridge/usage_state.json    Token tracking cursor and counters
bridge/activity.json       Latest low-level tool activity from hooks

Setup

Install the Node MCP dependency once:

npm install

Python dependencies are expected to already be available in the configured Python environment. This project uses bleak for BLE.

MCP Tools

  • buddy_start_bridge: start the BLE bridge in the background.
  • buddy_stop_bridge: stop the bridge process.
  • buddy_get_bridge_status: read process, BLE, and current snapshot status.
  • buddy_set_snapshot: update the heartbeat snapshot.
  • buddy_task_start: mark the current Codex request as busy.
  • buddy_task_attention: mark Codex as waiting for user attention.
  • buddy_task_done: mark Codex as idle after completing a request.
  • buddy_task_error: mark Codex as needing attention after an error.
  • buddy_set_prompt: show a permission prompt on the buddy.
  • buddy_clear_prompt: remove the current prompt.
  • buddy_get_permission_response: read the latest approval decision from the buddy.
  • buddy_clear: return to idle state.
  • buddy_get_device_status: send {"cmd":"status"} through BLE and wait for the ack.

Efficient status sync

The bridge is intended to be started manually once and left running. MCP tools only update bridge/state.json; they do not implicitly start the bridge.

While connected, the bridge runs independent loops for heartbeat, command acks, and token tracking. Heartbeats are still sent every heartbeatSeconds seconds (10 by default), and state or hook activity changes are sent immediately.

Token tracking reads Codex's local state database in read-only mode (%USERPROFILE%\.codex\state_5.sqlite by default). It tracks positive deltas from the latest active thread's tokens_used value and stores its cursor in bridge/usage_state.json so bridge restarts do not re-count old tokens. Token updates are throttled before they are written to bridge/state.json: by default the bridge flushes at most 5,000 tokens per update, after either 5,000 pending tokens or 60 seconds. This avoids flooding the device with rapid level-up updates during long coding sessions.

Plugin hooks write low-level tool activity to bridge/activity.json. The bridge merges that activity into the next heartbeat; hooks never touch BLE and never start the bridge.

Project-local status sync

This repo includes AGENTS.md instructions that tell Codex to update the Buddy state during work in this project:

  • user request starts: buddy_task_start
  • waiting for confirmation: buddy_task_attention or buddy_set_prompt
  • request completes: buddy_task_done
  • request fails or needs intervention: buddy_task_error

Short summaries are enabled by default and are sent as the latest entries items in the heartbeat snapshot. This is project-local behavior; to make it global, copy the same rules into your global Codex instructions after testing.

When the hardware sends a permission decision such as approve or deny, the bridge records it in bridge/permission_response.json and clears the matching prompt from bridge/state.json so the device returns to idle instead of staying on the approval sending screen.

BLE Defaults

The bridge scans for the first BLE device whose name starts with Claude and uses the Nordic UART Service:

  • Service: 6e400001-b5a3-f393-e0a9-e50e24dcca9e
  • RX write: 6e400002-b5a3-f393-e0a9-e50e24dcca9e
  • TX notify: 6e400003-b5a3-f393-e0a9-e50e24dcca9e

Edit bridge/config.json if your device name prefix or Python path differs.

Validation

node --check mcp/server.mjs
python -m py_compile bridge/bridge.py
Get-Content .codex-plugin/plugin.json | ConvertFrom-Json
Get-Content .mcp.json | ConvertFrom-Json
Get-Content hooks.json | ConvertFrom-Json

About

Codex Buddy is a local Codex plugin and BLE bridge for ESP32 desktop companions, fully compatible with Claude Code Buddy hardware. It syncs task state, tool activity, permission prompts, heartbeats, and usage telemetry through one persistent bridge process.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors