Skip to content

Repository files navigation

AgentGATT

CI License: MIT

An open, vendor-neutral protocol that carries MCP (Model Context Protocol) messages over BLE GATT — so traditional Bluetooth-only embedded devices (ESP32, nRF52, …) can be used directly by AI agents.

AgentGATT turns a BLE Peripheral (the embedded device) into an MCP server, and a BLE Central (phone / PC / SBC gateway) into an MCP client bridge.

[ embedded device ]  -- BLE GATT -->  [ host/gateway ]  -->  [ LLM / Agent ]
   (Peripheral)    AgentGATT Service    (Central)             MCP client

Status

This repository contains the public protocol specification and a reference implementation in C (chip-agnostic, with a NimBLE peripheral example and a BlueZ host bridge). It is intentionally small and portable.

  • ✅ Protocol spec (v1.0 draft) — docs/PROTOCOL.md
  • ✅ Identifier registry (UUIDs + error codes + validator) — registry/
  • ✅ C core library (framing + MCP dispatch) — src/agentgatt_core.c
  • ✅ HAL abstraction — include/agentgatt/hal.h
  • ✅ NimBLE peripheral HAL — src/nimble_hal.c, examples/example_peripheral.c
  • ✅ Zephyr native BT peripheral HAL — src/zephyr_hal.c
  • ✅ RT-Thread (NimBLE package) boot glue — examples/example_rtt_startup.c
  • ✅ Host-side Central bridge — src/central_bridge.c
  • ✅ BlueZ GDBus Central backend (discovery/connect/GATT/subscribe) — src/host/bluez_hal.c
  • ✅ Smoke tests + conformance golden-byte vectors — tests/
  • ✅ Device-side HAL porting guide + template — docs/HAL_PORTING.md, src/hal_template.c

Implemented MCP methods

MCP method Direction Status
initialize host → device ✅ (advertises tools + resources)
tools/list / tools/call host → device ✅ (dotted names + group + O(1) lookup)
resources/list / resources/read host → device ✅ (read-only state)
notifications/cancelled host → device ✅ (cooperative async cancel)
notifications/message device → host ✅ (unsolicited)
notifications/initialized host → device ✅ (no response)

Quick start

Understanding the protocol

Read docs/PROTOCOL.md. The essentials:

Item Value
Service E2C56DB5-DFFB-48D2-B060-D0F5A71096E0
Meta (read+notify) …96E1 — protocol info + limits
Request (write) …96E2 — host → device (JSON-RPC)
Response (notify) …96E3 — device → host (JSON-RPC)
Framing uint16 LE length + UTF-8 JSON

Build the core (host, self-contained)

The portable core compiles without any BLE stack (useful for CI/sanity checks):

cmake -S . -B build -DAGENTGATT_BUILD_TESTS=ON
cmake --build build
ctest --test-dir build

On a device (NimBLE)

Add these sources to your NimBLE target:

  • src/agentgatt_core.c
  • src/nimble_hal.c
  • examples/example_peripheral.c

call agentgatt_example_init() after nimble_port_init() + controller init, then start the NimBLE host task loop.

On the host (Central, BlueZ)

cmake -S . -B build -DAGENTGATT_BUILD_BRIDGE=ON
cmake --build build
# MCP stdio <-> GATT bridge over BlueZ (Linux)
./build/agentgatt-bridge

The bridge speaks MCP on stdin/stdout (MCP stdio transport) and GATT over BLE: it scans for the AgentGATT service UUID, connects, subscribes to notifications, and forwards JSON-RPC in both directions. Requires a Linux system with BlueZ and pkg-config for gio-2.0. It can be plugged directly into an MCP client by registering it as an MCP stdio server.

CI / sanity

python3 tools/validate_registry.py   # validates identifier registry
cmake -S . -B build -DAGENTGATT_BUILD_TESTS=ON
cmake --build build
ctest --test-dir build               # smoke + conformance tests

Repository layout

.
├── docs/
│   ├── PROTOCOL.md              # public protocol specification
│   └── HAL_PORTING.md           # device-side HAL porting guide
├── registry/                    # identifier registry (UUIDs + validator)
│   ├── README.md
│   ├── schema/registry-schema.json
│   └── entries/agentgatt-v1.json
├── tools/
│   └── validate_registry.py     # registry JSON-Schema + byte-order check
├── include/agentgatt/
│   ├── protocol.h               # UUIDs, version, framing, limits
│   ├── hal.h                    # BLE HAL abstraction (library boundary)
│   ├── core.h                   # device-side service core API
│   └── central_hal.h            # host-side bridge HAL
├── src/
│   ├── agentgatt_core.c         # framed JSON + MCP method dispatch
│   ├── nimble_hal.c             # NimBLE peripheral HAL (NimBLE/ESP-IDF/RTT)
│   ├── zephyr_hal.c             # Zephyr native BT peripheral HAL
│   ├── hal_template.c           # copy-to-start porting template (any stack)
│   ├── central_bridge.c         # host-side MCP stdio <-> GATT bridge
│   └── host/
│       └── bluez_hal.c          # BlueZ Central HAL (GDBus, complete)
├── examples/
│   ├── example_peripheral.c     # full device-side example
│   └── example_rtt_startup.c    # RT-Thread NimBLE boot glue
├── tests/
│   ├── smoke.c                  # framing + dispatch + async + escape tests
│   ├── conformance.c            # golden-byte wire-format vectors
│   └── conformance/README.md    # vector format + usage
└── CMakeLists.txt

Design notes

  • Framing — every message is uint16 LE length + UTF-8 JSON; a frame is assembled atomically before the HAL sends it, so a torn frame (prefix without payload) can never reach the wire.
  • Tool lookup — the core indexes tools in an FNV-1a open-addressing hash table, so tools/call resolution is O(1) regardless of tool count.
  • Grouping — tools use dotted names (sensor.temp.read) and an optional group field; read-only state is modeled as MCP resources, not tools.
  • Async + cancel — a tool may return AGENTGATT_TOOL_PENDING and finish later via agentgatt_respond(); notifications/cancelled triggers the tool's optional cancel hook (cooperative, single-threaded).
  • Threading — the core is single-threaded and lock-free. invoke/cancel/ agentgatt_respond must run on the same context as agentgatt_on_request.

Contributing a new BLE stack

Start from docs/HAL_PORTING.md (step-by-step guide) and src/hal_template.c (copy-to-start skeleton). Implement the two interfaces:

  • agentgatt_hal_api + agentgatt_hal_callbacks (device side) — include/agentgatt/hal.h
  • central_hal_* (host side) — include/agentgatt/central_hal.h

Then wire GATT events into agentgatt_on_request / agentgatt_on_subscribed / agentgatt_on_unsubscribed / agentgatt_on_disconnected (device), and drive central_hal_write_request / notification-forwarding (host).


License

MIT. See LICENSE.

The protocol itself is an open specification: implement it freely in any product, commercial or otherwise, without licensing fees.

About

AgentGATT: an open, vendor-neutral BLE GATT transport for MCP (Model Context Protocol) — let Bluetooth-only embedded/RTOS devices act as MCP servers.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages