ble-fleet-mcp
MCP server for managing large fleets of BLE devices with connection pooling, scheduling, retries, and partial-failure handling. Enables AI agents to run fleet-level reads/writes without worrying about radio connection limits.
README
ble-fleet-mcp
An MCP server that lets an AI agent manage a fleet of constrained-connection devices — 10, 100, or 1000+ of them — without ever having to know or reason about how many the underlying radio can actually hold open at once.
BLE radios typically support somewhere between 3 and 7 simultaneous connections.
Existing BLE-to-MCP bridges expose scan / connect / read / write / subscribe
as direct tools and leave connection management to the agent. That breaks down the
moment a task involves more devices than the radio can hold open: "check the
temperature on all 40 sensors in the warehouse" turns into forty manual
connect/read/disconnect cycles, with the agent doing the bookkeeping itself — wasted
tokens, wasted latency, and a real failure mode when it gets the bookkeeping wrong.
ble-fleet-mcp hides all of that. The agent asks for fleet-level outcomes — "read
all sensors", "set brightness on every light in Zone 2" — and the server handles
connection pooling, scheduling, retries, and partial failures underneath, inside a
hard concurrency cap it enforces itself.
Agent ──MCP──▶ fleet_read("warehouse", "temperature_c")
│
▼
[ scheduler batches 40 devices behind a 4-connection pool,
retries failures with backoff, circuit-breaks the
unresponsive ones, evicts idle connections to make room ]
│
▼
{ "SIM:0001": 21.4, "SIM:0002": "unreachable", ... }
Safety
- Read-only by default.
fleet_writerefuses to run unless the server is started withFLEET_ALLOW_WRITES=1— same posture asble-mcp-server. - Verify-after-write. Every write is followed by a readback; the result reports
both
acknowledged(the device accepted the write) andconverged(the readback actually matches what was requested) rather than trusting the ack alone. - Risk tiering. Devices registered as
safety_critical(e.g. a lock, in a fleet that also has light bulbs) are excluded fromfleet_writebatches by default and reported asconfirmation_required, unless their address is explicitly listed inconfirm_addresseson that call. A bulk write to "everything in Zone 2" can never silently include a lock. - Nothing is silently dropped. Every device in a fleet operation resolves to
success, a specific error (unreachable,timeout,write_rejected), orstill_queued(pollable viafleet_operation_status) — never just missing. - One bad device can't take down the fleet. Per-device timeouts and a circuit breaker isolate unresponsive devices; see docs/architecture.md.
Quick start
pip install ble-fleet-mcp
Or with uv:
uv add ble-fleet-mcp
Run it directly to sanity-check your BLE setup:
fleet-mcp
It speaks MCP over stdio, so in practice it's launched by your MCP client (see below), not run standalone. A minimal session, once connected from an agent:
fleet_register(name="warehouse", name_pattern="sensor")
-> {"fleet": "warehouse", "device_count": 40, "addresses": [...]}
fleet_read(fleet="warehouse", resource="temperature_c")
-> {"operation_id": "…", "status": "completed", "total": 40, "completed": 40,
"results": {"AA:BB:...:01": {"status": "success", "value": 21.4}, ...}}
No BLE hardware handy? examples/simulated_fleet/ runs
the exact same tool layer against a simulated fleet of virtual peripherals:
uv run python examples/simulated_fleet/demo.py --devices 50 --cap 4
Registering 50 simulated sensors (radio cap: 4)...
Reading temperature_c across the whole fleet...
Done in 1.64s over 50 devices with only 4 connections.
Status breakdown: {
"success": 50
}
Adding it to your client
ble-fleet-mcp speaks standard MCP over stdio, so it works with any MCP-compatible
client. Writes stay off unless you explicitly set FLEET_ALLOW_WRITES=1.
Claude Code
claude mcp add fleet-mcp -- fleet-mcp
Or add it to .mcp.json directly:
{
"mcpServers": {
"fleet-mcp": {
"command": "fleet-mcp",
"env": {
"FLEET_MAX_CONNECTIONS": "4",
"FLEET_ALLOW_WRITES": "0"
}
}
}
}
Claude Desktop
Add to your claude_desktop_config.json (Settings → Developer → Edit Config):
{
"mcpServers": {
"fleet-mcp": {
"command": "fleet-mcp",
"env": {
"FLEET_MAX_CONNECTIONS": "4",
"FLEET_ALLOW_WRITES": "0"
}
}
}
}
Cursor
Add to .cursor/mcp.json in your project (or the global ~/.cursor/mcp.json):
{
"mcpServers": {
"fleet-mcp": {
"command": "fleet-mcp",
"env": {
"FLEET_MAX_CONNECTIONS": "4",
"FLEET_ALLOW_WRITES": "0"
}
}
}
}
VS Code (Copilot)
Add to .vscode/mcp.json:
{
"servers": {
"fleet-mcp": {
"type": "stdio",
"command": "fleet-mcp",
"env": {
"FLEET_MAX_CONNECTIONS": "4",
"FLEET_ALLOW_WRITES": "0"
}
}
}
}
Tools
| Tool | Purpose |
|---|---|
fleet_register |
Register a device or group (address list, name pattern, or service UUIDs) into a named fleet. |
fleet_scan |
Discover devices matching a filter, without connecting or registering. |
fleet_read |
Read one resource across a fleet (or subset). Per-device results, not all-or-nothing. |
fleet_write |
Write one resource across a fleet. Requires FLEET_ALLOW_WRITES=1; verify-after-write on every device. |
fleet_watch |
Subscribe to / unsubscribe from / poll buffered, debounced notifications for a resource across a fleet. |
fleet_status |
Per-device health: healthy/unhealthy, consecutive failures, connected. |
fleet_pool_status |
Connection pool telemetry: active/idle/evicting counts, queue depth, per-device wait times. |
fleet_operation_status |
Poll a fleet_read/fleet_write batch by operation_id for partial or complete results. |
Full input/output schemas: docs/tools.md. Design rationale for the pool manager, scheduler, and circuit breaker: docs/architecture.md.
Environment variables
| Variable | Default | Meaning |
|---|---|---|
FLEET_MAX_CONNECTIONS |
4 |
Hard cap on simultaneous BLE connections. Match this to what you measured for your adapter — see docs/hardware-validation.md. |
FLEET_ALLOW_WRITES |
0 |
Set to 1 to allow fleet_write. Off by default. |
FLEET_REQUIRE_CONFIRMATION_SAFETY_CRITICAL |
1 |
Whether safety_critical-tiered devices require explicit confirm_addresses on writes. |
FLEET_DEVICE_TIMEOUT_S |
10.0 |
Per-device timeout (connect + operation) before it's treated as a failure. |
FLEET_OPERATION_TIMEOUT_S |
60.0 |
Default overall timeout for a fleet_read/fleet_write batch, after which unresolved devices report timeout and the rest of the batch is returned. |
FLEET_BACKOFF_INITIAL_S |
0.5 |
Initial per-device retry backoff. |
FLEET_BACKOFF_MAX_S |
30.0 |
Cap on per-device retry backoff. |
FLEET_BACKOFF_MULTIPLIER |
2.0 |
Exponential backoff multiplier. |
FLEET_CIRCUIT_BREAKER_THRESHOLD |
3 |
Consecutive failures before a device is marked unhealthy and stops being retried. |
FLEET_CIRCUIT_BREAKER_COOLDOWN_S |
60.0 |
How long an unhealthy device is skipped before a single probe attempt is allowed through. |
FLEET_TRACE_ENABLED |
1 |
Structured JSONL tracing of pool/scheduler events. |
FLEET_TRACE_PATH |
.fleet_mcp/traces/trace.jsonl |
Where trace events are written. |
FLEET_LOG_LEVEL |
INFO |
Log level; logs always go to stderr so stdout stays clean for the MCP stdio transport. |
FLEET_DASHBOARD_PORT |
unset (disabled) | Set to a port number to serve the web dashboard on that port. Off by default — see below. |
FLEET_DASHBOARD_HOST |
127.0.0.1 |
Dashboard bind address. Localhost only unless you explicitly widen it — there's no authentication. |
Web dashboard
A minimal, read-only dashboard over the same telemetry fleet_pool_status/fleet_status
already expose: live connection pool gauges, per-device health, recent operations, and
active watches. It's a stdlib http.server running in a background thread — no extra
dependency, and it reads straight from the running server's in-memory state, so it's
always exactly in sync.
Off by default. Enable it by setting FLEET_DASHBOARD_PORT:
FLEET_DASHBOARD_PORT=8765 fleet-mcp
# open http://127.0.0.1:8765
No hardware or MCP client needed to see it in action —
examples/simulated_fleet/dashboard_demo.py
runs the dashboard against a lively simulated fleet:
uv run python examples/simulated_fleet/dashboard_demo.py --devices 30 --cap 4
# open http://127.0.0.1:8765
Transports
| Transport | Status |
|---|---|
BLE (via bleak) |
v1, shipped |
| Zigbee coordinator | Roadmap, not started |
| Thread border router | Roadmap, not started |
The pool manager, scheduler, and MCP tool layer never import anything transport-specific — a new transport is a plugin, not a rewrite. See CONTRIBUTING.md.
Roadmap (not blocking 1.0.0)
- A second transport plugin (Zigbee or Thread) to prove the plugin interface generalizes beyond BLE.
- A "fleet template" registry for shareable configs of common device populations.
Development
git clone https://github.com/JephinJose/ble-fleet-mcp.git
cd ble-fleet-mcp
uv sync --extra dev
uv run pre-commit install
uv run pytest tests/unit
See CONTRIBUTING.md for the full workflow and how to add a transport plugin.
License
MIT — see LICENSE.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。