Skip to main content

Device Context Protocol — Python reference implementation. A bridge between LLM agents and physical devices. Import as `dcp`.

Project description

DCP — Device Context Protocol

tests license: MIT spec: v0.3 draft

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 16 KB.

Complementary to MCP — a reference Bridge translates DCP ↔ MCP so any MCP host (Claude Desktop, Claude Code, IDE assistants) works zero-config.

Contents

Why DCP?

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:

  • compiles to a compact CBOR wire format
  • uses a static intent table (no runtime negotiation)
  • moves safety enforcement to a Bridge process

A reference Bridge translates DCP ↔ MCP, so any MCP-compatible LLM works out of the box. DCP is the last mile to physical hardware.

Design principles

  1. Intent, not register. set_brightness(50%), not write_pwm(pin=5, duty=128).
  2. Units in the protocol. Every number declares a unit. No ambiguity.
  3. Static intent table. Manifest known at compile time; runtime is pure binary.
  4. Safety lives in the Bridge. Devices trust the Bridge; LLMs never see raw GPIO.
  5. Idempotent by default. Non-idempotent intents must declare themselves.
  6. Transport-agnostic. UART, BLE, MQTT, USB-CDC, WebSocket — one frame.

Architecture

DCP architecture

LLM ── MCP ──▶ Bridge ── DCP wire ──▶ Device(s)
                 │
                 ├─ issues capability tokens
                 ├─ enforces rate limits, ranges
                 └─ logs, dry-runs, undo

The Bridge is the sole trust boundary. 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.

Validated on real hardware

As of v0.3 the reference firmware is measured-validated on an ESP32-WROOM-32 dev board over CH340 USB-Serial at 115 200 baud:

  • 10/10 round-trip tests pass (tools/test_uart_roundtrip.py)
  • 88/88 Python unit & conformance tests pass
  • Compiled firmware: 294 KB flash, 22.7 KB globals (Arduino-ESP32 core 3.3.8)
  • The pure DCP layer is approximately 14 KB over a baseline empty sketch (measurement script in docs/paper/figures/)

See docs/RATIONALE.md §7 for what the hardware validation does and does not prove.

Manifest

dcp: 0.1
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.read

intent_id = crc16(name) — manifests and firmware stay in sync without coordination.

Wire format

A single frame:

┌────────┬────────┬────────┬─────────────┬───────┐
│ ver:u8 │ kind:u8│ seq:u16│ intent_id:u16│ cbor  │
└────────┴────────┴────────┴─────────────┴───────┘
field meaning
ver 1 for v0.1
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.

Adding a feature

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.

Quickstart

pip install -e ".[mcp,serial,mqtt,ble,dev]"
python examples/lamp_demo.py              # in-process bridge ↔ fake lamp
pytest                                    # all tests
dcp inspect examples/lamp_manifest.yaml   # parsed manifest summary
dcp codegen examples/lamp_manifest.yaml -o /tmp/dcp_intents.h

Run as an MCP server

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-567812345678

Capability tokens (HMAC-SHA256)

For 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...sig

Tokens 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).

What's not in v0.1 (intentional)

  • Multi-device transactions
  • Firmware OTA
  • Mesh routing
  • LLM authentication (Bridge's problem)
  • Capability token signing (stubbed — see safety.py)

License

MIT.

Roadmap

  • Wire format + manifest parser
  • Reference Python Bridge with loopback transport
  • Lamp example
  • MCP server wrapper + CLI (dcp serve)
  • Generic in-process device simulator
  • UART transport (COBS framing + CRC-16)
  • ESP32 reference firmware (Arduino-compatible C++)
  • Design rationale (docs/RATIONALE.md)
  • CI (GitHub Actions, Linux + Windows, py 3.11–3.13)
  • MQTT transport
  • HMAC-SHA256 capability tokens (Bridge-side enforcement)
  • Manifest compiler: dcp codegen (YAML → C header)
  • Compile-time DCP_ID(name) macro in firmware
  • BLE GATT transport (bleak)
  • Release prep: CONTRIBUTING / CHANGELOG / CoC / SECURITY / issue templates
  • On-device HMAC verification (per-frame signatures, ESP32 firmware)
  • ESP32 BLE peripheral example (NimBLE-Arduino)
  • Conformance test suite (golden frames, language-neutral YAML)
  • Codegen --stubs: emits handler signatures + binding table
  • Quickstart video script (docs/QUICKSTART_VIDEO.md)
  • Real-hardware UART validation (waiting on ESP32+CH340 board)
  • Public launch under device-context-protocol GitHub org

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pydcp-0.3.0.tar.gz (167.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pydcp-0.3.0-py3-none-any.whl (29.3 kB view details)

Uploaded Python 3

File details

Details for the file pydcp-0.3.0.tar.gz.

File metadata

  • Download URL: pydcp-0.3.0.tar.gz
  • Upload date:
  • Size: 167.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pydcp-0.3.0.tar.gz
Algorithm Hash digest
SHA256 5c4b175da71812abada460277542402213b8ea14a714f52bad850ac7c2bf728c
MD5 ba2be95b144c8d222a0b29124e56adaf
BLAKE2b-256 3fb062997fcffa9818be2a5fce2b6ddaa9a0cbafbec7796b8c0e7c5896ac0e8f

See more details on using hashes here.

Provenance

The following attestation bundles were made for pydcp-0.3.0.tar.gz:

Publisher: publish.yml on device-context-protocol/dcp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pydcp-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: pydcp-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 29.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pydcp-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 cc1eeadff8e7681cbb8022799988b3fa5fa640d44f34478142438c7a22dfcc1d
MD5 4557cad47e90a4041ebb5440b6a5e71a
BLAKE2b-256 5cbc9e3f1dbc1e86616c9f8c71db82e5170e55b6160ef273f47ed58cfb206e6f

See more details on using hashes here.

Provenance

The following attestation bundles were made for pydcp-0.3.0-py3-none-any.whl:

Publisher: publish.yml on device-context-protocol/dcp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page