Architecture
import { Aside } from ‘@astrojs/starlight/components’;
Understanding mcbluetooth’s architecture helps you troubleshoot issues and extend functionality.
System Overview
Section titled “System Overview”┌─────────────────────────────────────────────────────────────┐│ MCP Client (Claude, etc.) │└─────────────────────────────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────────┐│ mcbluetooth ││ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ ││ │ MCP Tools │ │ MCP Resources│ │ Pairing Agent │ ││ └─────────────┘ └─────────────┘ └─────────────────────┘ ││ ┌─────────────────────────────────────────────────────────┐││ │ D-Bus Clients (async) │││ │ ┌──────────────┐ ┌──────────────────────┐ │││ │ │ BlueZClient │ │ ObexClient │ │││ │ │ (system bus) │ │ (session bus) │ │││ │ └──────────────┘ └──────────────────────┘ │││ └─────────────────────────────────────────────────────────┘│└─────────────────────────────────────────────────────────────┘ │ ┌───────────────┼───────────────┐ ▼ ▼ ▼┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐│ BlueZ │ │ obexd │ │ PipeWire/ ││ (bluetoothd) │ │ (session) │ │ PulseAudio │└─────────────────┘ └─────────────────┘ └─────────────────┘ │ │ │ └───────────────┼───────────────┘ ▼┌─────────────────────────────────────────────────────────────┐│ Linux Kernel ││ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ ││ │ Bluetooth │ │ btusb │ │ HCI driver │ ││ │ subsystem │ │ module │ │ │ ││ └──────────────┘ └──────────────┘ └──────────────────┘ │└─────────────────────────────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────────┐│ Bluetooth Hardware ││ (USB dongle, built-in) │└─────────────────────────────────────────────────────────────┘Component Responsibilities
Section titled “Component Responsibilities”mcbluetooth (MCP Server)
Section titled “mcbluetooth (MCP Server)”The MCP server layer that exposes Bluetooth functionality:
| Component | Purpose |
|---|---|
| MCP Tools | 69 tools for Bluetooth operations |
| MCP Resources | Live state queries via URIs |
| Pairing Agent | Handles PIN/passkey negotiation |
| D-Bus Clients | Async communication with BlueZ/obexd |
BlueZ (bluetoothd)
Section titled “BlueZ (bluetoothd)”The official Linux Bluetooth stack daemon:
- Manages adapter hardware
- Handles device discovery and pairing
- Implements Bluetooth profiles (A2DP, HFP, etc.)
- Manages custom profile registrations (HFP AG via ProfileManager1)
- Exposes D-Bus API on system bus
OBEX protocol daemon for file transfer:
- Runs per-user (session daemon)
- Implements OPP, FTP, PBAP, MAP profiles
- Exposes D-Bus API on session bus
PipeWire/PulseAudio
Section titled “PipeWire/PulseAudio”Audio server integration:
- Receives audio streams from BlueZ
- Provides volume, mute, routing controls
- Handles codec negotiation
D-Bus Architecture
Section titled “D-Bus Architecture”mcbluetooth communicates with two D-Bus buses:
System Bus (BlueZ)
Section titled “System Bus (BlueZ)”Service: org.bluezPaths: /org/bluez /org/bluez/hci0 /org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF
Interfaces: org.bluez.Adapter1 - Adapter control org.bluez.Device1 - Device management org.bluez.GattService1 - BLE services org.bluez.GattCharacteristic1 - BLE characteristics org.bluez.AgentManager1 - Pairing agent registration org.bluez.ProfileManager1 - Custom profile registration (HFP AG)Session Bus (OBEX)
Section titled “Session Bus (OBEX)”Service: org.bluez.obexPaths: /org/bluez/obex /org/bluez/obex/client/session0 /org/bluez/obex/client/session0/transfer0
Interfaces: org.bluez.obex.Client1 - Session management org.bluez.obex.Session1 - Session properties org.bluez.obex.ObjectPush1 - OPP file sending org.bluez.obex.FileTransfer1 - FTP operations org.bluez.obex.PhonebookAccess1 - PBAP org.bluez.obex.MessageAccess1 - MAPAsync Design
Section titled “Async Design”mcbluetooth uses dbus-fast for non-blocking D-Bus communication:
# Singleton pattern ensures one connectionclass BlueZClient: _instance = None
@classmethod async def get_instance(cls): if cls._instance is None: cls._instance = cls() await cls._instance._connect() return cls._instanceBenefits:
- Multiple concurrent operations
- No blocking the MCP event loop
- Efficient resource usage
Tool Registration
Section titled “Tool Registration”Tools are organized by functional area:
from mcbluetooth.tools import adapter, audio, ble, device, hfp, monitor, obex
def create_server(): mcp = FastMCP("mcbluetooth")
adapter.register_tools(mcp) device.register_tools(mcp) audio.register_tools(mcp) hfp.register_tools(mcp) ble.register_tools(mcp) monitor.register_tools(mcp) obex.register_tools(mcp)
return mcpEach module follows the pattern:
def register_tools(mcp: FastMCP) -> None: @mcp.tool() async def bt_connect(adapter: str, address: str) -> dict: """Connect to a paired device.""" client = await BlueZClient.get_instance() return await client.connect_device(adapter, address)Error Handling
Section titled “Error Handling”D-Bus errors are translated to user-friendly messages:
| D-Bus Error | mcbluetooth Response |
|---|---|
org.bluez.Error.Failed | {"error": "Operation failed", ...} |
org.bluez.Error.NotReady | {"error": "Device not ready", ...} |
org.bluez.Error.AuthenticationFailed | {"error": "Authentication failed", ...} |
org.freedesktop.DBus.Error.ServiceUnknown | {"error": "BlueZ not running", ...} |
State Management
Section titled “State Management”BlueZ State
Section titled “BlueZ State”mcbluetooth doesn’t cache BlueZ state — each query goes to D-Bus:
- Ensures fresh data
- Avoids stale state issues
- BlueZ handles the caching
OBEX Sessions
Section titled “OBEX Sessions”OBEX sessions are tracked locally:
_active_sessions: dict[str, dict] = {}# session_id -> {path, address, target, created}
def generate_session_id(address: str, target: str) -> str: """Generate friendly ID: ftp_C87B235568E8""" clean_addr = address.replace(":", "") return f"{target}_{clean_addr}"Pairing Agent
Section titled “Pairing Agent”The pairing agent registers with BlueZ and handles callbacks:
1. Agent registers with AgentManager12. BlueZ calls agent methods for pairing events3. Agent handles PIN/passkey based on pairing_mode4. Results returned to BlueZPerformance Considerations
Section titled “Performance Considerations”D-Bus Call Overhead
Section titled “D-Bus Call Overhead”Each tool call involves:
- MCP request parsing
- D-Bus method call (async)
- Response serialization
Typical latency: 1-10ms per call.
Scanning
Section titled “Scanning”Discovery is resource-intensive:
bt_scanstarts/stops discovery explicitly- Avoids continuous scanning
- Timeout prevents runaway scans
Large Transfers
Section titled “Large Transfers”OBEX transfers use polling for progress:
while True: status = await get_transfer_status(transfer_path) if status["status"] in ("complete", "error"): return status await asyncio.sleep(0.5)Security Model
Section titled “Security Model”| Layer | Security |
|---|---|
| MCP | Trust boundary at MCP client |
| D-Bus | PolicyKit for privileged ops |
| BlueZ | Pairing provides encryption |
| OBEX | Session-based access |