Tools Reference
March 27, 2026 · View on GitHub
All tools return structured JSON:
{ "ok": true, ... } on success,
{ "ok": false, "error": { "code": "...", "message": "..." } } on failure.
BLE Core
ble.scan_start
Start a background BLE scan. Returns a scan_id immediately. The scan runs for up to timeout_s seconds (auto-stops), or you can stop it early with ble.scan_stop.
{ "timeout_s": 10, "name_filter": "Arduino", "service_uuid": "180a" }
Returns { "ok": true, "scan_id": "a1b2c3" }.
ble.scan_get_results
Non-blocking: return the devices discovered so far by a running (or finished) scan.
{ "scan_id": "a1b2c3" }
Returns:
{
"ok": true,
"active": true,
"devices": [{
"name": "Arduino",
"address": "AA:BB:CC:DD:EE:FF",
"rssi": -55,
"tx_power": -12,
"service_uuids": ["0000180a-0000-1000-8000-00805f9b34fb"],
"manufacturer_data": { "76": "0215..." },
"service_data": { "0000180a-...": "0a1b" }
}]
}
Fields tx_power, service_uuids, manufacturer_data, and service_data are included when advertised by the device. manufacturer_data keys are company IDs; values are hex-encoded. service_data keys are UUIDs; values are hex-encoded.
ble.scan_stop
Stop a running scan early and return the final device list. Safe to call on an already-finished scan.
{ "scan_id": "a1b2c3" }
Returns { "ok": true, "devices": [...], "active": false }.
ble.connect
Connect to a device by address.
{ "address": "AA:BB:CC:DD:EE:FF", "timeout_s": 10, "pair": true }
Set pair to true to bond during connection. Pairing works on Linux (BlueZ) and Windows (WinRT). On macOS, the OS pairs automatically when you access a secured characteristic, so this flag is not needed.
Returns { "ok": true, "connection_id": "abc123", "address": "..." }.
ble.disconnect
{ "connection_id": "abc123" }
ble.connection_status
Check whether a connection is still alive. If the device disconnected unexpectedly, the server detects it automatically and returns a clear status instead of letting subsequent calls fail with timeouts.
{ "connection_id": "abc123" }
Returns { "ok": true, "connected": true, "address": "AA:BB:CC:DD:EE:FF" } or { "ok": true, "connected": false, "address": "...", "disconnect_ts": 1700000000.0 }.
ble.discover
List services and characteristics (cached per connection).
{ "connection_id": "abc123" }
Returns { "ok": true, "services": [{ "uuid": "...", "characteristics": [{ "uuid": "...", "handle": 3, "properties": ["read", "notify"], "descriptors": [{ "uuid": "...", "handle": 5 }] }] }] }.
ble.mtu
Return the negotiated MTU for a connection. The effective max write payload per packet is mtu - 3 bytes (ATT header overhead).
{ "connection_id": "abc123" }
Returns { "ok": true, "mtu": 517, "max_write_payload": 514 }.
ble.read
Read a characteristic value.
{ "connection_id": "abc123", "char_uuid": "2a00" }
Returns { "ok": true, "value_b64": "...", "value_hex": "...", "value_len": 4 }.
ble.write
Write to a characteristic (requires BLE_MCP_ALLOW_WRITES=true).
{ "connection_id": "abc123", "char_uuid": "2a00", "value_hex": "0102", "with_response": true }
ble.read_descriptor
Read a GATT descriptor by handle. Handles are returned by ble.discover.
{ "connection_id": "abc123", "handle": 5 }
Returns { "ok": true, "value_b64": "...", "value_hex": "...", "value_len": 2 }.
ble.write_descriptor
Write to a GATT descriptor by handle (requires BLE_MCP_ALLOW_WRITES=true). Rarely needed directly — bleak handles CCCD for notify/indicate automatically.
{ "connection_id": "abc123", "handle": 5, "value_hex": "0100" }
ble.subscribe
Subscribe to notifications on a characteristic.
{ "connection_id": "abc123", "char_uuid": "2a37" }
Returns { "ok": true, "subscription_id": "sub456" }.
ble.unsubscribe
{ "connection_id": "abc123", "subscription_id": "sub456" }
ble.wait_notification
Block until the next single notification arrives, or timeout. For bursty/bulk flows prefer ble.drain_notifications.
{ "connection_id": "abc123", "subscription_id": "sub456", "timeout_s": 10 }
Returns { "ok": true, "notification": { "value_b64": "...", "value_hex": "...", "ts": 1700000000.0 } } or { "ok": true, "notification": null } on timeout.
ble.poll_notifications
Non-blocking: return up to max_items buffered notifications immediately (or an empty list).
{ "connection_id": "abc123", "subscription_id": "sub456", "max_items": 50 }
Returns { "ok": true, "notifications": [...], "dropped": 0 }.
ble.drain_notifications
Batch-collect: waits up to timeout_s for the first notification, then keeps collecting until idle_timeout_s passes with no new data, max_items is reached, or timeout_s expires. Ideal for bursty flows like downloading a log file or dataset over BLE notifications.
{ "connection_id": "abc123", "subscription_id": "sub456", "timeout_s": 2, "idle_timeout_s": 0.25, "max_items": 200 }
Returns { "ok": true, "notifications": [...], "dropped": 0 }.
Introspection
ble.connections.list
List all tracked connections with their status, address, name, timestamps, and subscription count. Useful for recovering connection IDs after context loss.
{}
Returns:
{
"ok": true,
"connections": [{
"connection_id": "abc123",
"address": "AA:BB:CC:DD:EE:FF",
"name": "Arduino",
"connected": true,
"created_ts": 1700000000.0,
"last_seen_ts": 1700000050.0,
"subscription_count": 1,
"spec": { "spec_id": "a1b2c3", "name": "MyDevice Protocol" }
}],
"count": 1
}
Disconnected entries include disconnect_ts. The spec field is null if no spec is attached.
ble.subscriptions.list
List all active subscriptions with their status, queue depth, and dropped count. Optionally filter by connection_id.
{ "connection_id": "abc123" }
Returns:
{
"ok": true,
"subscriptions": [{
"subscription_id": "sub456",
"connection_id": "abc123",
"char_uuid": "00002a37-0000-1000-8000-00805f9b34fb",
"active": true,
"queue_depth": 3,
"dropped": 0,
"created_ts": 1700000010.0
}],
"count": 1
}
ble.scans.list
List all tracked scans with their status, filters, timestamps, and device count. Does not dump full device lists — use ble.scan_get_results for that.
{}
Returns:
{
"ok": true,
"scans": [{
"scan_id": "a1b2c3",
"active": true,
"started_ts": 1700000000.0,
"timeout_s": 10.0,
"num_devices_seen": 5,
"filters": { "name_filter": "Arduino", "service_uuid": null }
}],
"count": 1
}
The filters field is null when no filters were applied.
ble.tasks.list
List all registered background tasks (from plugins) with their status.
{}
Returns:
{
"ok": true,
"tasks": [{
"task_id": "d4e5f6",
"name": "scanner_monitor",
"running": true,
"started_ts": 1700000000.0,
"error": null
}],
"count": 1
}
ble.tasks.cancel
Cancel a running background task by task_id.
{ "task_id": "d4e5f6" }
Returns { "ok": true, "task_id": "d4e5f6", "cancelled": true }.
Protocol Specs
Tools for managing BLE device protocol specs. Specs are markdown files with YAML front-matter stored in .ble_mcp/specs/.
ble.spec.template
Return a markdown template for a new BLE protocol spec.
{ "device_name": "MyDevice" }
Returns { "ok": true, "template": "---\nkind: ble-protocol\n...", "suggested_path": ".ble_mcp/specs/mydevice.md" }.
ble.spec.register
Register a spec file in the index. Validates YAML front-matter (requires kind: ble-protocol and name). The path must be inside the project directory (where .ble_mcp/ lives).
{ "path": ".ble_mcp/specs/mydevice.md" }
Returns { "ok": true, "spec_id": "a1b2c3d4e5f67890", "name": "MyDevice Protocol", ... }.
ble.spec.list
List all registered specs with their metadata.
{}
Returns { "ok": true, "specs": [...], "count": 2 }.
ble.spec.attach
Attach a registered spec to a connection session (in-memory only). The spec will be available via ble.spec.get for the duration of the connection.
{ "connection_id": "abc123", "spec_id": "a1b2c3d4e5f67890" }
ble.spec.get
Get the attached spec for a connection (returns null if none attached).
{ "connection_id": "abc123" }
ble.spec.read
Read full spec content, file path, and metadata by spec_id.
{ "spec_id": "a1b2c3d4e5f67890" }
ble.spec.search
Full-text search over a spec's content. Returns matching snippets with line numbers and context.
{ "spec_id": "a1b2c3d4e5f67890", "query": "sensor read", "k": 10 }
Tracing
Tools for inspecting the JSONL trace log. Tracing is enabled by default and records every tool call.
ble.trace.status
Return tracing config and event count.
{}
Returns { "ok": true, "enabled": true, "event_count": 42, "file_path": ".ble_mcp/traces/trace.jsonl", "payloads_logged": false, "max_payload_bytes": 16384 }.
ble.trace.tail
Return last N trace events (default 50).
{ "n": 20 }
Returns { "ok": true, "events": [{ "ts": "...", "event": "tool_call_start", "tool": "ble.read", ... }, ...] }.
Plugins
Tools for managing user plugins. Plugins live in .ble_mcp/plugins/ and can add device-specific tools without modifying the core server. Requires BLE_MCP_PLUGINS env var to be set.
ble.plugin.template
Return a Python plugin template. Optionally pre-fill with a device name.
{ "device_name": "SensorTag" }
Returns { "ok": true, "template": "\"\"\"Plugin for SensorTag...", "suggested_path": ".ble_mcp/plugins/sensortag.py" }.
ble.plugin.list
List loaded plugins with their tool names and metadata.
{}
Returns:
{
"ok": true,
"plugins": [{
"name": "sensortag",
"path": "/path/to/.ble_mcp/plugins/sensortag.py",
"tools": ["sensortag.read_temp"],
"meta": {
"description": "SensorTag plugin",
"service_uuids": ["f000aa00-0451-4000-b000-000000000000"],
"device_name_contains": "SensorTag"
}
}],
"count": 1,
"plugins_dir": "/path/to/.ble_mcp/plugins",
"enabled": true,
"policy": "*"
}
The meta field is plugin-defined (optional). Common keys: description, service_uuids, device_name_contains.
ble.plugin.reload
Hot-reload a plugin by name. Re-imports the module and refreshes tools.
{ "name": "sensortag" }
Returns { "ok": true, "name": "sensortag", "tools": ["sensortag.read_temp"], "notified": true }.
ble.plugin.load
Load a new plugin from a file or directory path. The path must be inside .ble_mcp/plugins/.
{ "path": ".ble_mcp/plugins/sensortag.py" }
Returns { "ok": true, "name": "sensortag", "tools": ["sensortag.read_temp"], "notified": true, "hint": "Plugin loaded on the server. The client may need a restart to call the new tools." }.
MCP log notifications
The server sends MCP notifications/message log events to proactively alert the client. These are not tool calls — they are asynchronous messages sent outside the request/response cycle.
| Event | Level | When |
|---|---|---|
| Device disconnect | warning | A connected BLE device disconnects unexpectedly |
| Notification available | info | First GATT notification arrives on a subscription since the last ble.drain_notifications, ble.poll_notifications, or ble.wait_notification |
These are best-effort and fire-and-forget. The server's internal state (connection status, notification queue) is always the source of truth. Not all MCP clients surface log notifications — Claude Code currently ignores them. The MCP Inspector shows them in real time and is useful for verifying the behavior.