{
  "markdown": "# BLEA\n\n<!-- mcp-name: io.github.Nitmi/blea -->\n\n[![Agent Plugins 1.0.0](https://img.shields.io/badge/Agent_Plugins-1.0.0-0F766E)](https://agent-plugins.org/)\n[![Agent Skill](https://img.shields.io/badge/Agent-Skill-2563EB)](skills/ble/SKILL.md)\n[![ClawHub Skill](https://img.shields.io/badge/ClawHub-Skill-D97706)](https://clawhub.ai/nitmi/skills/blea)\n[![CLI](https://img.shields.io/badge/Interface-CLI-374151)](#cli)\n[![MCP](https://img.shields.io/badge/Protocol-MCP-C2410C)](#mcp-and-agent-plugin)\n[![PyPI](https://img.shields.io/pypi/v/blea)](https://pypi.org/project/blea/)\n[![CI](https://github.com/Nitmi/blea/actions/workflows/ci.yml/badge.svg)](https://github.com/Nitmi/blea/actions/workflows/ci.yml)\n\nBLEA — **Bluetooth Low Energy Automation** — is a universal Agent Plugin for safe, deterministic\nBLE work. It gives AI agents a portable Skill, a machine-readable CLI, a local MCP server, guarded\nautomation, and offline evidence capture, diff, and replay.\n\nThe project is designed for the BLE work agents repeatedly need: checking OS Bluetooth access,\nfinding the right nearby device, discovering GATT services, preserving and comparing raw byte\nevidence, reading and subscribing before changing state, and making writes only after explicit\nauthorization.\n\n| Surface | Role |\n| --- | --- |\n| Agent Plugin 1.0.0 | Packages the portable identity, Skill, and MCP declaration. |\n| Agent Skill | Teaches agents the BLE workflow, safety boundaries, and recovery rules. |\n| CLI | Emits deterministic JSON/JSONL for scripts, terminals, and any agent with shell access. |\n| MCP | Exposes the same runtime as local, structured tools with managed BLE sessions. |\n\nThe `ble` executable is the shared runtime. Regular subcommands use the CLI surface; `ble mcp`\nstarts the stdio MCP server. The MCP protocol is therefore a first-class interface, not a second\ntool hidden inside an unrelated CLI product.\n\n## Install\n\nBLEA requires Python 3.10 or newer. Bluetooth access is provided by the operating system through\n[Bleak](https://github.com/hbldh/bleak).\n\n### Quick install with an Agent prompt\n\nGive this prompt to an Agent with shell access:\n\n```text\nRead https://github.com/Nitmi/blea and follow its README to install BLEA for me.\n```\n\nFor manual installation, use the detailed paths below.\n\nInstall the latest published Python runtime from PyPI:\n\n```shell\nuv tool install blea\nble --help\n```\n\nFor Codex, install the public Git marketplace package after the runtime:\n\n```shell\ncodex plugin marketplace add Nitmi/blea\ncodex plugin add blea@blea\n```\n\nTo install only the portable Agent Skill with the Skills CLI:\n\n```shell\nnpx skills add Nitmi/blea --skill ble\n```\n\nFor OpenClaw, install the same portable Skill from its public ClawHub listing:\n\n```shell\nopenclaw skills install @nitmi/blea\n# Or use the registry CLI directly:\nclawhub install @nitmi/blea\n```\n\nThe Skill teaches the workflow and safety policy; it does not install the Python runtime. Install\n`blea` from PyPI as shown above before asking an agent to access live Bluetooth hardware or start\nthe local MCP server. The source Skill is [`skills/ble/SKILL.md`](skills/ble/SKILL.md), and the\npublic ClawHub release is [`@nitmi/blea`](https://clawhub.ai/nitmi/skills/blea).\n\nStart a new Agent task after installation. The marketplace package supplies the BLE Skill and MCP\ndeclaration; it does not install the Python runtime or operating-system Bluetooth permissions. See\n[`docs/installation.md`](docs/installation.md) for the release-tag, legacy `0.6.0` bridge, and update\nprocedure.\n\nFrom a checkout, install the command runtime directly from the working tree:\n\n```shell\nuv tool install --editable .\nble --version\n```\n\nFor repository development:\n\n```shell\nuv sync --extra dev\nuv run ble --help\n```\n\nThe Python runtime and Agent Plugin are two related installation units: PyPI installs the `ble`\nexecutable, while an Agent Plugins 1.0.0 client loads this repository directory to discover\n`plugin.json`, `mcp.json`, and `skills/ble`. Install the runtime first so the plugin's local stdio\nMCP server can find `ble` on `PATH`. See [`docs/installation.md`](docs/installation.md) for source,\nplugin, update, permissions, and verification paths.\n\n## CLI\n\n```shell\nble doctor --json\nble scan --timeout 8 --json\nble inspect --device \"id:AA:BB:CC:DD:EE:FF\" --json\nble probe --device \"id:AA:BB:CC:DD:EE:FF\" --max-reads 16 --json\nble probe --device \"id:AA:BB:CC:DD:EE:FF\" --max-reads 16 --read-offset 16 --json\nble read --device \"id:AA:BB:CC:DD:EE:FF\" --characteristic 2a19 --json\nble subscribe --device \"id:AA:BB:CC:DD:EE:FF\" --characteristic 2a37 --duration 15 --jsonl\nble observe --device \"id:AA:BB:CC:DD:EE:FF\" --duration 10 --jsonl\nble diff before.blea.jsonl after.blea.jsonl --json\nble replay capture.blea.jsonl inspect --json\nble replay capture.blea.jsonl read --characteristic 2a19 --json\nble exchange \\\n  --device \"id:AA:BB:CC:DD:EE:FF\" \\\n  --write-characteristic 12345678-1234-1234-1234-1234567890ab \\\n  --notify-characteristic 87654321-4321-4321-4321-ba0987654321 \\\n  --text ping --duration 5 --allow-write \\\n  --confirm-device \"AA:BB:CC:DD:EE:FF\" --jsonl\n```\n\nDevice names are accepted only when exactly one observed device has that name. Prefer the\nplatform identifier returned by `scan`; on macOS this is normally a UUID rather than a MAC address.\n\nEvery binary value includes `length`, `hex`, `base64`, and replacement-safe UTF-8 representations.\nJSON errors use stable reasons and exit codes suitable for agent recovery.\n\n`probe` is paginated. Follow `next_read_offset` until it is `null`. Page counts and\ncharacteristic-level failures live under `read_page`; `ok=true` means the page executed, while\n`read_page.has_failures` tells you whether any characteristic failed. Every result includes a small\n`profile_summary`. The CLI includes the full GATT profile by default and supports\n`--no-include-profile`; the MCP tool omits it by default to keep repeated pages compact.\n\nConnection commands report `timeout_scope=per_backend_operation`. Their timeout applies separately\nto discovery, connection, and each GATT operation, rather than bounding the total command time.\nGATT entries include `uuid_namespace`. BLEA reports library descriptions only for canonical\nBluetooth Base UUIDs, avoiding false standard names inferred from the leading bytes of custom\n128-bit UUIDs.\n\n`observe` discovers notify/indicate characteristics and watches them over one connection for a\nbounded duration. Omit `--characteristic` to select all event-capable traits, or repeat it for\nexplicit selection. The result separates subscription failures, cleanup failures, and notification\nevents; a quiet window is evidence only for that sample window.\n\nSave a complete read-only evidence package after diagnosis:\n\n```shell\nble capture \\\n  --device \"id:AA:BB:CC:DD:EE:FF\" \\\n  --output capture.blea.jsonl \\\n  --max-reads 128 \\\n  --observe-duration 10 \\\n  --redact-identifiers \\\n  --json\n```\n\n`capture` records the advertisement, normalized GATT profile, each readable-characteristic result,\nbounded notifications, operation errors, and a validated final summary. It performs no writes,\npairing, or configuration changes and replaces the destination only after an atomic close. The\nJSONL file is the authoritative artifact; the command's JSON result is a compact summary. The\nEvidence Format v1 contract and deterministic validator live in\n[`docs/evidence-format-v1.md`](docs/evidence-format-v1.md).\n\nCompare two complete evidence packages without a Bluetooth adapter or device:\n\n```shell\nble diff before.blea.jsonl after.blea.jsonl --json\nble diff before.blea.jsonl after.blea.jsonl --fail-on-change --json\n```\n\n`diff` validates both Evidence Format v1 inputs, projects them into stable BLE semantics, and emits\nsorted JSON Pointer changes. It ignores capture IDs, timestamps, runtime metadata, sampling\nduration, and RSSI movement within 5 dBm by default. Use `--strict-rssi` for exact RSSI comparison.\nDevice identifiers must match unless an intentional cross-device comparison uses\n`--allow-different-devices`. Binary payloads are atomic changes, so one changed value does not\nproduce separate Hex, Base64, and UTF-8 noise. A normal difference exits successfully;\n`--fail-on-change` returns code `3` after printing the complete result for CI. The contract lives in\n[`docs/diff-format-v1.md`](docs/diff-format-v1.md).\n\nReplay a complete evidence package without a Bluetooth adapter or physical device:\n\n```shell\nble replay capture.blea.jsonl inspect --json\nble replay capture.blea.jsonl probe --max-reads 32 --json\nble replay capture.blea.jsonl read --characteristic 2a19 --json\nble replay capture.blea.jsonl observe --duration 10 --jsonl\nble replay capture.blea.jsonl run examples/replay-read-only.yaml --json\n```\n\nReplay reconstructs advertisements, GATT, reads, captured failures, subscription outcomes, and\nthe notification timeline through the same read-only backend interfaces used for live devices.\nThe default `--speed 0` mode is immediate and deterministic; place a positive `--speed` before the\noperation to preserve recorded notification gaps at that multiplier. Missing observations return\nthe stable `replay_miss` reason instead of an invented value or success. Replay never accesses a\nreal adapter and never sends or simulates writes or exchanges. The full contract lives in\n[`docs/replay-format-v1.md`](docs/replay-format-v1.md).\n\n## Guarded writes\n\nA write requires both `--allow-write` and an exact confirmation of the resolved identifier:\n\n```shell\nble write \\\n  --device \"id:AA:BB:CC:DD:EE:FF\" \\\n  --characteristic 12345678-1234-1234-1234-1234567890ab \\\n  --hex 01 \\\n  --allow-write \\\n  --confirm-device \"AA:BB:CC:DD:EE:FF\" \\\n  --read-back \\\n  --json\n```\n\nYAML writes and exchanges add two more guards: the workflow must enable writes and each state-changing\nstep must declare `dangerous: true` plus successful prerequisite steps. The policy must also carry an\nexact `confirm_device` identifier. See `examples/guarded-write.yaml` and\n`examples/esp32-burst-exchange.yaml`.\n\nFor protocols where a write triggers notifications, use `exchange`. It enables the notification\nsubscription before performing the guarded write, then collects events for a bounded duration.\nThe write and notify characteristics may be the same or different. This avoids the race created by\nlaunching standalone session subscribe and write operations concurrently.\n\nFor repeatable checks, an `exchange` step can assert the exact notification count, UTF-8 or Hex\ncontent, the final notification, and subscription cleanup. Run a guarded YAML workflow with the\nindependent invocation gate:\n\n```shell\nble run examples/esp32-burst-exchange.yaml --allow-write --json\n```\n\nReplace the example's device identifier and confirmation with the exact value returned by a fresh\nscan before running it.\n\n## MCP and Agent Plugin\n\nStart the local stdio server directly:\n\n```shell\nble mcp\n```\n\nThe MCP surface includes one-shot tools, the offline `ble_diff` comparator, the offline\n`ble_replay` runner, and stateful session tools. Sessions let an agent connect\nonce, inspect, read, observe, subscribe, perform a guarded request/notification exchange or write,\nand then disconnect. `ble_exchange` and `ble_session_exchange` atomically subscribe before writing.\n`ble_session_list` exposes active leases and `ble_session_close_all` provides explicit recovery.\nThe server disconnects all sessions when the MCP client exits and reaps an inactive session after\n120 seconds by default. Set `BLEA_SESSION_IDLE_SECONDS` to another positive duration, or `0` to\ndisable idle reaping while retaining shutdown cleanup.\n\nClose a known session with `ble_session_close`. Reserve `ble_session_close_all` for recovering an\nunknown session ID, a failed explicit close, or confirmed leaked state.\n\nTo expose the normal MCP BLE tools against one capture instead of hardware, launch a dedicated\nreplay server:\n\n```shell\nble replay capture.blea.jsonl mcp\n```\n\nEvery replay-backed BLE operation result identifies the evidence and carries\n`replay.read_only=true`. Even if a client invokes an existing write or exchange tool with\nlive-device authorization fields, the ReplayBackend rejects it.\n\nThis repository is itself an [Agent Plugins 1.0.0](https://agent-plugins.org/) package:\n\n```text\nblea/\n├── plugin.json                        portable plugin identity\n├── mcp.json                           portable local stdio MCP declaration\n├── skills/ble/SKILL.md                portable Agent Skill\n├── .codex-plugin/plugin.json          root ChatGPT/Codex package metadata\n├── .mcp.json                          root ChatGPT/Codex MCP declaration\n├── .agents/plugins/marketplace.json   public Git marketplace catalog\n└── plugins/blea/                      installable Codex distribution mirror\n```\n\nThe plugin configuration expects the `ble` executable to be installed on `PATH`. The portable\nspecification distributes Skill and MCP metadata; native Bluetooth runtime installation and OS\npermissions remain platform responsibilities.\n\nThe repository also carries [`server.json`](server.json), the official MCP Registry package\nmetadata for the PyPI distribution. It declares a local stdio server and the `mcp` package\nargument; it does not advertise a hosted or remote BLE service. Releases publish this metadata to\nthe [official MCP Registry](https://registry.modelcontextprotocol.io/) with short-lived GitHub OIDC\ncredentials after the matching PyPI version succeeds. The namespace preserves the GitHub login's\ncanonical casing because Registry authorization and package ownership markers are case-sensitive.\n\nThe root [`Dockerfile`](Dockerfile) is a registry sandbox for MCP protocol introspection and\nadapter-free tools. It builds the version-aligned release checkout and starts `ble mcp`, which lets\nrelease-candidate CI run before that version exists on PyPI. It does not claim that a container can\naccess the host's Bluetooth adapter; use the native host installation above for live BLE work.\n\nThe OpenAI Plugins Directory submission uses the portable Skill only. BLEA's MCP server stays local\nbecause live BLE requires the user's native host and adapter; it is not represented as a public\nremote MCP endpoint. Submission copy, limitations, starter prompts, and review cases are recorded\nin [`docs/openai-plugin-submission.md`](docs/openai-plugin-submission.md).\n\nThe public Codex distribution is registered at the immutable Git ref shown in the install section\nand installed with `codex plugin add blea@blea`. A pinned ref does not advance to another release\nwhen a marketplace snapshot is refreshed. To change releases, remove the configured marketplace,\nadd it again at the new `v<version>` tag, reinstall the Plugin, and start a new Agent task. Do not\ntreat installing the Python package alone as installing the Agent Skill or MCP declaration.\n\n## Platform status\n\nWindows 11 is hardware-verified with the ESP32-S3 BLEA test server, including discovery, GATT,\nnotifications, guarded exchange, capture, and replay. macOS and Linux currently pass the unit suite\nand replay the same real-device evidence in CI, but their native Bluetooth adapter paths have not\nbeen tested on physical hosts. Treat those two platforms as `CI + replay verified; native BLE\nhardware unverified` until a published acceptance report says otherwise.\n\nSee [`docs/platform-acceptance.md`](docs/platform-acceptance.md) for the exact support tiers,\nprivacy requirements, hardware acceptance procedure, and current evidence matrix.\n\n## Development\n\n```shell\nuv sync --extra dev\nuv run python scripts/sync_codex_plugin.py --check\nuv run python scripts/check_agent_package.py\nuv run ruff check .\nuv run ruff format --check .\nuv run pytest\nuv build --clear --no-create-gitignore\nuv run python scripts/check_distribution.py dist\n```\n\nUnit tests use a fake BLE backend and do not require nearby hardware. CI runs on Windows, macOS,\nand Linux. Each CI job also runs `examples/replay-read-only.yaml` against the checked-in complete\nevidence fixture, providing an end-to-end CLI and Workflow smoke test with no adapter.\n\nThe root `.codex-plugin`, `.mcp.json`, and `skills` paths are the Codex Plugin source of truth. After\nchanging them, run `uv run python scripts/sync_codex_plugin.py`; CI uses `--check` to reject drift.\nThe repository-local Agent package checker validates both Codex Plugin roots, their MCP declaration,\nSkill metadata, and `agents/openai.yaml` without relying on a maintainer's personal Skill directory.\nThe distribution checker also rejects missing marketplace files in the sdist and Plugin metadata in\nthe Python wheel.\n\nReal adapter support is tracked separately from CI; CI success is not a native hardware support\nclaim.\n\nRelease history lives in [`CHANGELOG.md`](CHANGELOG.md). Maintainers should follow\n[`docs/releasing.md`](docs/releasing.md) for artifact, Trusted Publishing, Plugin, and\npost-publication gates.\n\n## Companion project\n\nFor serial, UART, COM-port, USB-to-TTL, and firmware-console workflows, see\n[`baud-cli`](https://github.com/Nitmi/baud-cli). It provides an agent-friendly `baud` CLI and\nportable Skill with guarded YAML automation, structured output, and archived raw-byte evidence.\n",
  "bytes": 17186,
  "sha": "b3e0bd21638498d9d25c6a9fafd7ca3e35be1736bd326a61ecd7ec7723355d55",
  "repo_slug": "nitmi/blea",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_nitmi_blea_e8d1e804/readme"
}