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.

EventLevelWhen
Device disconnectwarningA connected BLE device disconnects unexpectedly
Notification availableinfoFirst 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.