| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
Status: Draft v0.3 — May 2026 · Hardware-validated on ESP32-WROOM-32
A protocol that lets LLM agents safely control physical devices, down to dollar-class microcontrollers.
Intent-level, transport-agnostic, capability-scoped. Compact wire format (sub-50-byte frames). Self-contained firmware: under 1 KB of RAM, ~28 KB of flash.
Complementary to MCP — a reference Bridge translates DCP ↔ MCP so any MCP host (Claude Desktop, Claude Code, IDE assistants) works zero-config.
MCP is excellent for SaaS tools, but assumes JSON-RPC over WebSocket and runtime tool discovery. On an MCU with 32 KB of RAM, that's a non-starter.
DCP keeps MCP's mental model (manifest + tool calls) but:
A reference Bridge translates DCP ↔ MCP, so any MCP-compatible LLM works out of the box. DCP is the last mile to physical hardware.
Why this matters in one chart: the protocol's schema decides how many hallucinated or adversarial calls are stopped before any byte reaches a device. DCP catches all six categories at the wire layer; the others catch what their existing schema happens to cover.
The Bridge is the sole trust boundary. On every call it issues and verifies capability tokens, enforces range/type/unit checks from the manifest, and supports dry-run as a wire-format primitive. Devices remain simple enough to fit on commodity microcontrollers; everything the LLM is allowed to do is enforced before any byte traverses the device boundary.
As of v0.3 the reference firmware is measured-validated on two physical boards — an ESP32-WROOM-32 dev board over CH340 USB-Serial, and an ESP32-S3 (LILYGO T-Panel S3) over the S3's native USB-Serial/JTAG — both at 115 200 baud:
Static RAM is the scarce resource on an MCU. The DCP layer's measured 0.6 KB of RAM sits two orders of magnitude under IoT-MCP's reported 74 KB peak memory. DCP's flash cost (27.6 KB, measured) is not plotted — IoT-MCP does not report a comparable flash figure.
See docs/RATIONALE.md §7 for what the hardware validation does and does not prove.
The reference firmware is portable by design (Arduino Stream + a software SHA-256, no SoC-specific code paths in DCP.{h,cpp}). It cross-compiles for every current ESP32 variant and for ESP8266; two of those targets are also runtime-validated on real boards, the rest are build-validated pending hardware on the bench:
| Target | ISA | Flash (lamp+blink) | Globals | Status |
|---|---|---|---|---|
| ESP32-WROOM-32 | Xtensa LX6 (baseline) | 294 KB | 22.7 KB | runtime ✓ |
| ESP32-S3 (T-Panel) | Xtensa LX7 | 322 KB | 22.7 KB | runtime ✓ (native USB) |
| ESP32-C3 | RV32IMC | 289 KB | 13.4 KB | builds ✓ |
| ESP32-C6 | RV32IMAC + HW-crypto | 266 KB | 14.0 KB | builds ✓ |
| ESP32-H2 | RV32IMAC + 802.15.4 | 292 KB | 14.0 KB | builds ✓ |
| ESP32-P4 | RV32IMAFC dual-core | 326 KB | 22.0 KB | builds ✓ |
| ESP8266 NodeMCU | Xtensa LX106 (legacy) | 242 KB | 28.9 KB | builds ✓ |
All builds use Arduino-ESP32 core 3.3.8 / Arduino-ESP8266 core 3.x
arduino-cli compile --clean --fqbn esp32:esp32:esp32c3 \
--library firmware/esp32 firmware/esp32/examples/lamp
arduino-cli compile --clean --fqbn esp8266:esp8266:nodemcuv2 \
--library firmware/esp32 firmware/esp32/examples/lampdcp: 0.3
device:
id: lamp-kitchen-01
model: smart_lamp_v1
vendor: example.dev
intents:
- name: set_brightness
params:
level: { type: float, unit: percent, range: [0, 100] }
fade: { type: duration, unit: ms, default: 0 }
capability: lamp.write
idempotent: true
dry_run: true
- name: read_brightness
returns: { type: float, unit: percent }
capability: lamp.read
events:
- name: motion_detected
payload:
confidence: { type: float, unit: ratio, range: [0, 1] }
capability: lamp.readintent_id = crc16(name) — manifests and firmware stay in sync without coordination.
A frame is a 6-byte fixed header + CBOR payload + an optional 16-byte truncated HMAC-SHA256. Header fields:
| field | meaning |
|---|---|
| ver | 1 in v0.3 |
| kind | 0x01 call · 0x02 reply · 0x03 event · 0x04 error · 0x81 dry-run |
| seq | client-chosen, echoed in reply |
| intent_id | CRC-16/CCITT of intent name |
| cbor | CBOR map: params / return / event payload / error |
Reply status codes: ok, denied, range, busy, unknown_intent, capability_required.
A typical set_brightness(50) call is 19 bytes on the wire; the MCP JSON-RPC equivalent is approximately 180 bytes. The full normative spec lives at SPEC.md.
See docs/ADDING_FEATURES.md for the full 5-step loop with a worked blink(times, period) example. The short version: edit the manifest, add a C++ handler + binding, recompile, flash, restart the MCP server — the LLM picks up the new tool automatically. The Bridge needs no code change.
# As a user — install from PyPI:
pip install "pydcp[mcp,serial]" # or [mcp,serial,mqtt,ble] for all transports
dcp inspect examples/lamp_manifest.yaml # parsed manifest summary
dcp serve examples/lamp_manifest.yaml --simulator# As a contributor — editable install from source:
git clone https://github.com/device-context-protocol/dcp.git
cd dcp
pip install -e ".[mcp,serial,mqtt,ble,dev]"
pytest # all 88 tests
python examples/lamp_demo.py # in-process bridge ↔ fake lampThe PyPI package is named pydcp (the bare dcp is squatted by an unrelated package). The import name is dcp. The protocol name is DCP.
The reference Bridge ships an MCP server that exposes each DCP intent as an MCP tool. With --simulator it spins up an in-process fake device, so you can demo with no hardware.
dcp serve examples/lamp_manifest.yaml --simulator # no hardware
dcp serve examples/lamp_manifest.yaml --serial COM3 # real ESP32 over UART
dcp serve examples/lamp_manifest.yaml --mqtt broker.lan:1883 \ # MQTT
--mqtt-prefix dcp/lamp-kitchen
dcp serve examples/lamp_manifest.yaml --ble AA:BB:CC:DD:EE:FF \ # BLE
--ble-service 12345678-1234-5678-1234-567812345678For multi-tenant or scoped access, mint short-lived HMAC tokens and pass them to the Bridge:
export DCP_SECRET=$(dcp token keygen)
dcp token mint --caps lamp.write,lamp.read --ttl 3600
# eyJjYXBzIjpb...sigTokens are verified by the Bridge on every call. The device sees only already-authorized frames. Devices themselves do not verify signatures in v0.2 — that requires on-device HMAC, which is on the roadmap.
To wire it into Claude Desktop, add this to your claude_desktop_config.json:
{
"mcpServers": {
"smart-lamp": {
"command": "dcp",
"args": [
"serve",
"C:/path/to/protocol/examples/lamp_manifest.yaml",
"--simulator"
]
}
}
}Then ask Claude "set the lamp to 60% brightness". The call flow:
Claude ─MCP─▶ dcp serve ─Bridge─▶ Loopback ─DCP wire─▶ GenericSimulator
For production use, replace GenericSimulator with a real transport (UART / MQTT / BLE — coming next).
If you use DCP in academic work, please cite the arXiv preprint:
@misc{yang2026dcp,
title = {Device Context Protocol: A Compact, Safety-First Architecture
for LLM-Driven Control of Constrained Devices},
author = {Yang, Dongxu},
year = {2026},
eprint = {2605.26159},
archivePrefix= {arXiv},
primaryClass = {cs.NI},
url = {https://arxiv.org/abs/2605.26159},
}A machine-readable CITATION.cff is also provided — GitHub renders a "Cite this repository" button in the sidebar.
MIT.
| Back | FazBrowse Home | New Git URL |