bluespy-mcp 1.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. bluespy_mcp-1.2.0/.claude/settings.local.json +11 -0
  2. bluespy_mcp-1.2.0/.gitignore +10 -0
  3. bluespy_mcp-1.2.0/.mcp.json +12 -0
  4. bluespy_mcp-1.2.0/CLAUDE.md +55 -0
  5. bluespy_mcp-1.2.0/LICENSE +21 -0
  6. bluespy_mcp-1.2.0/PKG-INFO +390 -0
  7. bluespy_mcp-1.2.0/README.md +363 -0
  8. bluespy_mcp-1.2.0/docs/demo-prompts.md +138 -0
  9. bluespy_mcp-1.2.0/docs/hardware-lifecycle.md +240 -0
  10. bluespy_mcp-1.2.0/pyproject.toml +54 -0
  11. bluespy_mcp-1.2.0/scripts/test.sh +57 -0
  12. bluespy_mcp-1.2.0/src/bluespy_mcp/__init__.py +3 -0
  13. bluespy_mcp-1.2.0/src/bluespy_mcp/__main__.py +5 -0
  14. bluespy_mcp-1.2.0/src/bluespy_mcp/_vendor/__init__.py +0 -0
  15. bluespy_mcp-1.2.0/src/bluespy_mcp/_vendor/bluespy.py +1345 -0
  16. bluespy_mcp-1.2.0/src/bluespy_mcp/analysis_core.py +731 -0
  17. bluespy_mcp-1.2.0/src/bluespy_mcp/analyzer.py +208 -0
  18. bluespy_mcp-1.2.0/src/bluespy_mcp/capture.py +290 -0
  19. bluespy_mcp-1.2.0/src/bluespy_mcp/hardware.py +547 -0
  20. bluespy_mcp-1.2.0/src/bluespy_mcp/loader.py +161 -0
  21. bluespy_mcp-1.2.0/src/bluespy_mcp/packet_cache.py +214 -0
  22. bluespy_mcp-1.2.0/src/bluespy_mcp/server.py +565 -0
  23. bluespy_mcp-1.2.0/src/bluespy_mcp/worker.py +388 -0
  24. bluespy_mcp-1.2.0/tests/__init__.py +0 -0
  25. bluespy_mcp-1.2.0/tests/conftest.py +129 -0
  26. bluespy_mcp-1.2.0/tests/e2e/__init__.py +1 -0
  27. bluespy_mcp-1.2.0/tests/e2e/conftest.py +141 -0
  28. bluespy_mcp-1.2.0/tests/e2e/fixtures/__init__.py +1 -0
  29. bluespy_mcp-1.2.0/tests/e2e/fixtures/prompts.py +7 -0
  30. bluespy_mcp-1.2.0/tests/e2e/results/.gitignore +2 -0
  31. bluespy_mcp-1.2.0/tests/e2e/results/20260301T021703Z_Load_the_capture_file_at__nonexistent_pa.json +10 -0
  32. bluespy_mcp-1.2.0/tests/e2e/results/20260301T021952Z_Load_the_capture_file_at__nonexistent_pa.json +10 -0
  33. bluespy_mcp-1.2.0/tests/e2e/results/20260301T022106Z_Load_the_capture_file_at__nonexistent_pa.json +20 -0
  34. bluespy_mcp-1.2.0/tests/e2e/results/20260301T022145Z_Load_the_capture_file_at__nonexistent_pa.json +18 -0
  35. bluespy_mcp-1.2.0/tests/e2e/results/20260301T022221Z_Use_the_load_capture_tool_to_load_the_ca.json +10 -0
  36. bluespy_mcp-1.2.0/tests/e2e/results/20260301T022310Z_Use_the_load_capture_tool_to_load_the_ca.json +20 -0
  37. bluespy_mcp-1.2.0/tests/e2e/results/20260301T022344Z_Use_the_load_capture_tool_to_load_the_ca.json +20 -0
  38. bluespy_mcp-1.2.0/tests/e2e/results/20260301T022411Z_Load_the_capture_file_at__Users_mafaneh.json +27 -0
  39. bluespy_mcp-1.2.0/tests/e2e/results/20260301T022505Z_Load__Users_mafaneh_Projects_bluespy_mcp.json +142 -0
  40. bluespy_mcp-1.2.0/tests/e2e/results/20260301T022539Z_Load__Users_mafaneh_Projects_bluespy_mcp.json +34 -0
  41. bluespy_mcp-1.2.0/tests/e2e/results/20260301T022625Z_Do_a_complete_analysis_of__Users_mafaneh.json +66 -0
  42. bluespy_mcp-1.2.0/tests/e2e/results/20260301T022803Z_Use_the_load_capture_tool_to_load_the_ca.json +20 -0
  43. bluespy_mcp-1.2.0/tests/e2e/results/20260301T023028Z_Use_the_load_capture_tool_to_load_the_ca.json +20 -0
  44. bluespy_mcp-1.2.0/tests/e2e/results/20260301T023057Z_Load_the_capture_file_at__Users_mafaneh.json +27 -0
  45. bluespy_mcp-1.2.0/tests/e2e/results/20260301T023132Z_Load__Users_mafaneh_Projects_bluespy_mcp.json +79 -0
  46. bluespy_mcp-1.2.0/tests/e2e/results/20260301T023202Z_Load__Users_mafaneh_Projects_bluespy_mcp.json +36 -0
  47. bluespy_mcp-1.2.0/tests/e2e/results/20260301T023345Z_Do_a_complete_analysis_of__Users_mafaneh.json +220 -0
  48. bluespy_mcp-1.2.0/tests/e2e/results/20260301T023618Z_Use_the_load_capture_tool_to_load_the_ca.json +20 -0
  49. bluespy_mcp-1.2.0/tests/e2e/results/20260301T023646Z_Load_the_capture_file_at__Users_mafaneh.json +27 -0
  50. bluespy_mcp-1.2.0/tests/e2e/results/20260301T023723Z_Load__Users_mafaneh_Projects_bluespy_mcp.json +99 -0
  51. bluespy_mcp-1.2.0/tests/e2e/results/20260301T023801Z_Load__Users_mafaneh_Projects_bluespy_mcp.json +36 -0
  52. bluespy_mcp-1.2.0/tests/e2e/results/20260301T023832Z_Do_a_complete_analysis_of__Users_mafaneh.json +57 -0
  53. bluespy_mcp-1.2.0/tests/e2e/results/20260301T024120Z_Connect_to_the_BlueSPY_sniffer.json +18 -0
  54. bluespy_mcp-1.2.0/tests/e2e/results/20260301T024229Z_Check_the_hardware_status__If_anything_i.json +18 -0
  55. bluespy_mcp-1.2.0/tests/e2e/results/20260301T024515Z_Check_the_hardware_status__If_anything_i.json +18 -0
  56. bluespy_mcp-1.2.0/tests/e2e/results/20260301T025726Z_Connect_to_the_BlueSPY_sniffer.json +20 -0
  57. bluespy_mcp-1.2.0/tests/e2e/results/20260301T025758Z_Check_the_hardware_status__If_anything_i.json +18 -0
  58. bluespy_mcp-1.2.0/tests/e2e/results/20260301T025845Z_Connect_to_the_sniffer__capture_for_5_se.json +50 -0
  59. bluespy_mcp-1.2.0/tests/e2e/results/20260301T025931Z_Connect_to_the_sniffer__start_a_continuo.json +60 -0
  60. bluespy_mcp-1.2.0/tests/e2e/results/20260301T030514Z_Check_the_hardware_status__If_anything_i.json +18 -0
  61. bluespy_mcp-1.2.0/tests/e2e/results/20260301T030605Z_Connect_to_the_sniffer__capture_for_5_se.json +50 -0
  62. bluespy_mcp-1.2.0/tests/e2e/results/20260301T030641Z_Connect_to_the_sniffer__start_a_continuo.json +50 -0
  63. bluespy_mcp-1.2.0/tests/e2e/results/20260301T030739Z_Connect_to_the_sniffer__start_capturing.json +80 -0
  64. bluespy_mcp-1.2.0/tests/e2e/results/20260301T030823Z_Connect_to_the_sniffer__capture_for_5_se.json +66 -0
  65. bluespy_mcp-1.2.0/tests/e2e/results/20260301T031338Z_Check_the_hardware_status__If_anything_i.json +18 -0
  66. bluespy_mcp-1.2.0/tests/e2e/results/20260301T031437Z_Connect_to_the_sniffer__capture_for_5_se.json +57 -0
  67. bluespy_mcp-1.2.0/tests/e2e/results/20260301T031518Z_Connect_to_the_sniffer__start_a_continuo.json +46 -0
  68. bluespy_mcp-1.2.0/tests/e2e/results/20260301T031625Z_Connect_to_the_sniffer__start_capturing.json +102 -0
  69. bluespy_mcp-1.2.0/tests/e2e/results/20260301T031710Z_Connect_to_the_sniffer__capture_for_5_se.json +69 -0
  70. bluespy_mcp-1.2.0/tests/e2e/results/20260301T032151Z_Check_the_hardware_status__If_anything_i.json +18 -0
  71. bluespy_mcp-1.2.0/tests/e2e/results/20260301T032247Z_Connect_to_the_sniffer__capture_for_5_se.json +58 -0
  72. bluespy_mcp-1.2.0/tests/e2e/results/20260301T032347Z_Check_the_hardware_status__If_anything_i.json +18 -0
  73. bluespy_mcp-1.2.0/tests/e2e/results/20260301T032435Z_Connect_to_the_sniffer__capture_for_5_se.json +76 -0
  74. bluespy_mcp-1.2.0/tests/e2e/results/20260301T032512Z_Connect_to_the_sniffer__start_a_continuo.json +48 -0
  75. bluespy_mcp-1.2.0/tests/e2e/results/20260301T032626Z_Connect_to_the_sniffer__start_capturing.json +121 -0
  76. bluespy_mcp-1.2.0/tests/e2e/results/20260301T032717Z_Connect_to_the_sniffer__capture_for_5_se.json +68 -0
  77. bluespy_mcp-1.2.0/tests/e2e/results/20260301T035142Z_Check_the_hardware_status__If_anything_i.json +18 -0
  78. bluespy_mcp-1.2.0/tests/e2e/results/20260301T035332Z_Connect_to_the_sniffer__capture_for_5_se.json +71 -0
  79. bluespy_mcp-1.2.0/tests/e2e/results/20260301T035408Z_Connect_to_the_sniffer__start_a_continuo.json +48 -0
  80. bluespy_mcp-1.2.0/tests/e2e/results/20260301T035515Z_Connect_to_the_sniffer__start_capturing.json +117 -0
  81. bluespy_mcp-1.2.0/tests/e2e/results/20260301T035606Z_Connect_to_the_sniffer__capture_for_5_se.json +75 -0
  82. bluespy_mcp-1.2.0/tests/e2e/results/20260301T035647Z_Connect_to_the_sniffer__start_a_continuo.json +62 -0
  83. bluespy_mcp-1.2.0/tests/e2e/results/20260301T035744Z_Connect_to_the_sniffer__start_a_continuo.json +55 -0
  84. bluespy_mcp-1.2.0/tests/e2e/results/20260302T074314Z_Check_the_hardware_status__If_anything_i.json +18 -0
  85. bluespy_mcp-1.2.0/tests/e2e/results/20260302T074403Z_Connect_to_the_sniffer__capture_for_5_se.json +53 -0
  86. bluespy_mcp-1.2.0/tests/e2e/results/20260302T074444Z_Connect_to_the_sniffer__start_a_continuo.json +46 -0
  87. bluespy_mcp-1.2.0/tests/e2e/results/20260302T074526Z_Connect_to_the_sniffer__start_capturing.json +70 -0
  88. bluespy_mcp-1.2.0/tests/e2e/results/20260302T074614Z_Connect_to_the_sniffer__capture_for_5_se.json +68 -0
  89. bluespy_mcp-1.2.0/tests/e2e/results/20260302T074652Z_Connect_to_the_sniffer__start_a_continuo.json +57 -0
  90. bluespy_mcp-1.2.0/tests/e2e/results/20260302T074933Z_Connect_to_the_sniffer__start_a_continuo.json +59 -0
  91. bluespy_mcp-1.2.0/tests/e2e/scenario.py +426 -0
  92. bluespy_mcp-1.2.0/tests/e2e/scenarios/__init__.py +1 -0
  93. bluespy_mcp-1.2.0/tests/e2e/scenarios/test_error_handling.py +92 -0
  94. bluespy_mcp-1.2.0/tests/e2e/scenarios/test_file_analysis.py +109 -0
  95. bluespy_mcp-1.2.0/tests/e2e/scenarios/test_hardware_capture.py +110 -0
  96. bluespy_mcp-1.2.0/tests/e2e/scenarios/test_live_analysis.py +115 -0
  97. bluespy_mcp-1.2.0/tests/e2e/test_scenario.py +295 -0
  98. bluespy_mcp-1.2.0/tests/fixtures/.gitkeep +0 -0
  99. bluespy_mcp-1.2.0/tests/fixtures/5_sec_capture.pcapng +0 -0
  100. bluespy_mcp-1.2.0/tests/fixtures/smart_home_capture_10min.pcapng +0 -0
  101. bluespy_mcp-1.2.0/tests/test_analysis_core.py +597 -0
  102. bluespy_mcp-1.2.0/tests/test_analyzer.py +42 -0
  103. bluespy_mcp-1.2.0/tests/test_capture.py +254 -0
  104. bluespy_mcp-1.2.0/tests/test_hardware.py +441 -0
  105. bluespy_mcp-1.2.0/tests/test_integration.py +311 -0
  106. bluespy_mcp-1.2.0/tests/test_loader.py +63 -0
  107. bluespy_mcp-1.2.0/tests/test_packet_cache.py +415 -0
  108. bluespy_mcp-1.2.0/tests/test_server.py +124 -0
  109. bluespy_mcp-1.2.0/tests/test_server_hardware.py +440 -0
  110. bluespy_mcp-1.2.0/tests/test_worker.py +586 -0
  111. bluespy_mcp-1.2.0/uv.lock +1560 -0
@@ -0,0 +1,11 @@
1
+ {
2
+ "enableAllProjectMcpServers": true,
3
+ "enabledMcpjsonServers": [
4
+ "bluespy"
5
+ ],
6
+ "permissions": {
7
+ "allow": [
8
+ "mcp__bluespy__list_captures"
9
+ ]
10
+ }
11
+ }
@@ -0,0 +1,10 @@
1
+ __pycache__/
2
+ *.pyc
3
+ *.egg-info/
4
+ dist/
5
+ build/
6
+ .venv/
7
+ .env
8
+ *.pcapng
9
+ !tests/fixtures/*.pcapng
10
+ .DS_Store
@@ -0,0 +1,12 @@
1
+ {
2
+ "mcpServers": {
3
+ "bluespy": {
4
+ "command": "/Users/mafaneh/Projects/bluespy-mcp/.venv/bin/python",
5
+ "args": ["-m", "bluespy_mcp"],
6
+ "env": {
7
+ "BLUESPY_LIBRARY_PATH": "/Applications/blueSPY.app/Contents/Frameworks/libblueSPY.dylib",
8
+ "BLE_CAPTURES_DIR": "/Users/mafaneh/Projects/bluespy-captures"
9
+ }
10
+ }
11
+ }
12
+ }
@@ -0,0 +1,55 @@
1
+ # CLAUDE.md
2
+
3
+ ## What This Is
4
+
5
+ Open-source MCP server for the BlueSPY Bluetooth LE protocol analyzer. Lets AI assistants load and analyze .pcapng capture files.
6
+
7
+ ## Architecture
8
+
9
+ Flat Python package:
10
+
11
+ - `server.py` — FastMCP server, 16 tools + 2 resources + 3 prompts
12
+ - `capture.py` — CaptureManager wrapping BlueSPY's global file state
13
+ - `analyzer.py` — Packet classification, filtering, summarization
14
+ - `hardware.py` — HardwareManager: subprocess isolation, file lock, state machine
15
+ - `worker.py` — Worker subprocess: executes bluespy hardware commands
16
+ - `loader.py` — Auto-discovers bluespy.py (installed > bundled fallback)
17
+ - `_vendor/bluespy.py` — Bundled fallback BlueSPY API
18
+
19
+ ## Development
20
+
21
+ ```bash
22
+ # Setup
23
+ python -m venv .venv
24
+ source .venv/bin/activate
25
+ pip install -e .
26
+
27
+ # Run tests (no hardware needed)
28
+ python -m pytest tests/ -v
29
+
30
+ # Run integration tests (needs BlueSPY hardware + dylib)
31
+ python -m pytest tests/ -v -m hardware
32
+
33
+ # Run server locally
34
+ bluespy-mcp
35
+ # or
36
+ python -m bluespy_mcp
37
+ ```
38
+
39
+ ## Key Patterns
40
+
41
+ - **Lazy loading**: BlueSPY module loaded on first use, not at import time
42
+ - **One file at a time**: BlueSPY uses global state. CaptureManager wraps this.
43
+ - **Defensive queries**: Every packet attribute access wrapped in try/except
44
+ - **JSON responses**: All tools return JSON. Errors are `{"error": "message"}`.
45
+ - **50K packet cap**: Summary operations cap iteration to prevent hanging on huge captures.
46
+ - **Subprocess isolation**: All hardware calls run in a child process. If ctypes hangs, the subprocess is killed — MCP server stays responsive.
47
+ - **File lock**: `~/.bluespy-mcp.lock` ensures single-client hardware access.
48
+ - **Reboot on connect**: Every connect_hardware() reboots the device first to avoid stale state.
49
+
50
+ ## Adding New Tools
51
+
52
+ 1. Add analysis function to `analyzer.py`
53
+ 2. Add `@mcp.tool` function to `server.py` (follows the pattern of existing tools)
54
+ 3. Add tests to the corresponding test file
55
+ 4. Update README.md tool table
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Novel Bits
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,390 @@
1
+ Metadata-Version: 2.4
2
+ Name: bluespy-mcp
3
+ Version: 1.2.0
4
+ Summary: MCP server for BlueSPY Bluetooth LE protocol analyzer
5
+ Project-URL: Homepage, https://github.com/novelbits/bluespy-mcp
6
+ Project-URL: Repository, https://github.com/novelbits/bluespy-mcp
7
+ Project-URL: Issues, https://github.com/novelbits/bluespy-mcp/issues
8
+ Author-email: Mohammad Afaneh <mafaneh@novelbits.io>
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: ble,bluespy,bluetooth,mcp,protocol-analyzer,sniffer
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Software Development :: Testing
21
+ Requires-Python: >=3.10
22
+ Requires-Dist: fastmcp>=2.0.0
23
+ Provides-Extra: e2e
24
+ Requires-Dist: claude-code-sdk; extra == 'e2e'
25
+ Requires-Dist: pytest-asyncio; extra == 'e2e'
26
+ Description-Content-Type: text/markdown
27
+
28
+ # BlueSPY MCP Server
29
+
30
+ MCP server for the [BlueSPY](https://rfcreations.com) Bluetooth LE protocol analyzer. Gives AI assistants direct access to .pcapng capture analysis — turn packet captures into conversations instead of writing scripts or scrolling through Wireshark.
31
+
32
+ Works with Claude Desktop, Claude Code, and Cursor.
33
+
34
+ ## What It Does
35
+
36
+ Instead of writing Python scripts or manually filtering packets, just ask:
37
+
38
+ > "Load this capture and tell me what's going on."
39
+
40
+ The AI loads your capture file, classifies 54,000+ packets, identifies 219 devices, maps 25 connections, and gives you a structured breakdown — in seconds.
41
+
42
+ ### Example: Analyzing a Restaurant Bluetooth LE Environment
43
+
44
+ ```
45
+ You: Load the First Watch capture and summarize it.
46
+
47
+ Claude: Loaded First Watch Carmel.pcapng — 54,195 packets over 32 seconds.
48
+
49
+ 219 devices detected, 25 active connections.
50
+
51
+ Packet breakdown:
52
+ → 11,671 ADV_IND (connectable advertisements)
53
+ → 4,738 ADV_NONCONN_IND (beacons)
54
+ → 6,554 LE_DATA (active data transfer)
55
+ → 5,642 SCAN_REQ / 5,014 SCAN_RSP
56
+ → 113 CRC errors from RF congestion
57
+ → 33 new connections initiated
58
+
59
+ You: Show me the advertising for the first device.
60
+
61
+ Claude: Device BC:57:29:01:00:42 — 34 advertisements found.
62
+ Channels: 37, 38, 39
63
+ RSSI: min -82, max -48, avg -65.2
64
+
65
+ You: Find all the errors.
66
+
67
+ Claude: 113 errors found — all CRC failures on advertising channels,
68
+ consistent with a congested RF environment (219 devices in range).
69
+ ```
70
+
71
+ No scripts. No manual filtering. Just a conversation.
72
+
73
+ ## Prerequisites
74
+
75
+ - **Python 3.10+**
76
+ - **BlueSPY application** (free) — provides the native `libblueSPY` library required for packet parsing
77
+
78
+ Download BlueSPY for your platform from [rfcreations.com/bluespy-software](https://www.rfcreations.com/bluespy-software):
79
+
80
+ | Platform | Download |
81
+ |----------|----------|
82
+ | macOS 11.0+ | [blueSPY-Darwin.pkg](https://private.rfcreations.com/bin/latest?q=blueSPY-~-Darwin.pkg) |
83
+ | Windows 7+ | [blueSPY-win64.msi](https://private.rfcreations.com/bin/latest?q=blueSPY-~-win64.msi) |
84
+ | Windows 7+ (Portable) | [blueSPY-win64.7z](https://private.rfcreations.com/bin/latest?q=blueSPY-~-win64.7z) |
85
+ | Linux (glibc 2.27+) | [blueSPY-Linux.7z](https://private.rfcreations.com/bin/latest?q=blueSPY-~-Linux.7z) |
86
+ | Linux (arm64) | [blueSPY-Linux-arm64.7z](https://private.rfcreations.com/bin/latest?q=blueSPY-~-Linux-arm64.7z) |
87
+ | Linux (Headless) | [blueSPY-Linux-Headless.7z](https://private.rfcreations.com/bin/latest?q=blueSPY-~-Linux-Headless.7z) |
88
+
89
+ No hardware needed to get started — download the [example captures](https://www.rfcreations.com/bluespy-software) from the same page and use them with the file analysis tools.
90
+
91
+ ## Installation
92
+
93
+ ```bash
94
+ pip install bluespy-mcp
95
+ ```
96
+
97
+ Or install from source:
98
+
99
+ ```bash
100
+ git clone https://github.com/novelbits/bluespy-mcp.git
101
+ cd bluespy-mcp
102
+ pip install .
103
+ ```
104
+
105
+ ## Configuration
106
+
107
+ ### Claude Desktop
108
+
109
+ Add to your `claude_desktop_config.json`:
110
+
111
+ ```json
112
+ {
113
+ "mcpServers": {
114
+ "bluespy": {
115
+ "command": "bluespy-mcp",
116
+ "env": {
117
+ "BLUESPY_LIBRARY_PATH": "/Applications/blueSPY.app/Contents/Frameworks/libblueSPY.dylib"
118
+ }
119
+ }
120
+ }
121
+ }
122
+ ```
123
+
124
+ ### Claude Code
125
+
126
+ ```bash
127
+ claude mcp add bluespy -- bluespy-mcp
128
+ ```
129
+
130
+ Or add to your `.mcp.json`:
131
+
132
+ ```json
133
+ {
134
+ "bluespy": {
135
+ "command": "bluespy-mcp",
136
+ "env": {
137
+ "BLUESPY_LIBRARY_PATH": "/Applications/blueSPY.app/Contents/Frameworks/libblueSPY.dylib"
138
+ }
139
+ }
140
+ }
141
+ ```
142
+
143
+ ### Cursor
144
+
145
+ Add to your MCP server configuration:
146
+
147
+ ```json
148
+ {
149
+ "bluespy": {
150
+ "command": "bluespy-mcp",
151
+ "env": {
152
+ "BLUESPY_LIBRARY_PATH": "/Applications/blueSPY.app/Contents/Frameworks/libblueSPY.dylib"
153
+ }
154
+ }
155
+ }
156
+ ```
157
+
158
+ ## Environment Variables
159
+
160
+ | Variable | Description | Required |
161
+ |----------|-------------|----------|
162
+ | `BLUESPY_LIBRARY_PATH` | Path to `libblueSPY.dylib` / `.so` / `.dll` | Yes |
163
+ | `BLUESPY_API_PATH` | Path to directory containing `bluespy.py` (auto-discovered if not set) | No |
164
+ | `BLE_CAPTURES_DIR` | Default directory for capture files (default: `captures/`) | No |
165
+
166
+ ### Platform-Specific Library Paths
167
+
168
+ | Platform | Path |
169
+ |----------|------|
170
+ | macOS | `/Applications/blueSPY.app/Contents/Frameworks/libblueSPY.dylib` |
171
+ | Windows | `C:\Program Files\blueSPY\libblueSPY.dll` |
172
+ | Linux | `/usr/local/lib/libblueSPY.so` |
173
+
174
+ ## Tools
175
+
176
+ ### File Management
177
+
178
+ | Tool | Description |
179
+ |------|-------------|
180
+ | `load_capture(file_path)` | Load a .pcapng capture file for analysis |
181
+ | `close_capture()` | Close the currently loaded file |
182
+ | `list_captures(directory?)` | List .pcapng files in a directory |
183
+
184
+ ### Discovery
185
+
186
+ | Tool | Description |
187
+ |------|-------------|
188
+ | `capture_summary()` | Packet counts by type, duration, device/connection counts |
189
+ | `list_devices()` | All Bluetooth devices with addresses and names |
190
+ | `list_connections()` | All connections with parameters |
191
+
192
+ ### Analysis
193
+
194
+ | Tool | Description |
195
+ |------|-------------|
196
+ | `search_packets(summary_contains?, packet_type?, channel?, max_results?)` | Filter packets by criteria |
197
+ | `inspect_connection(connection_index)` | Deep-dive connection analysis with packet breakdown |
198
+ | `inspect_advertising(device_index)` | Per-device advertising analysis with RSSI and channel stats |
199
+ | `inspect_all_devices()` | Batch advertising analysis for ALL devices in a single pass (much faster than per-device calls) |
200
+ | `inspect_all_connections()` | Batch connection analysis for ALL connections in a single pass (much faster than per-connection calls) |
201
+ | `find_capture_errors(max_results?)` | Error, failure, disconnect, and timeout packets |
202
+
203
+ ### Live Hardware
204
+
205
+ | Tool | Description |
206
+ |------|-------------|
207
+ | `connect_hardware(serial, force)` | Connect to first available device (or specify serial) |
208
+ | `disconnect_hardware()` | Disconnect from hardware |
209
+ | `start_capture(filename, duration_seconds, LE, CL, QHS, wifi, CS)` | Start live capture |
210
+ | `stop_capture()` | Stop active capture |
211
+ | `hardware_status()` | Get current hardware state |
212
+
213
+ ### Resources
214
+
215
+ | Resource | Description |
216
+ |----------|-------------|
217
+ | `capture://status` | Current loaded file metadata (JSON) |
218
+ | `bluespy://hardware` | Current hardware connection state (JSON) |
219
+ | `bluespy://capture` | Current live capture state (JSON) |
220
+
221
+ ### Prompts
222
+
223
+ | Prompt | Description |
224
+ |--------|-------------|
225
+ | `analyze-capture` | Guided workflow to load and analyze a capture file |
226
+ | `quick-capture` | Quick-start workflow for live hardware capture |
227
+ | `debug-connection` | Troubleshoot hardware connection issues |
228
+
229
+ ## Try It Without Hardware
230
+
231
+ Download the [example captures](https://www.rfcreations.com/bluespy-software) from RFcreations (free) and try these prompts. No sniffer needed — just the BlueSPY application installed.
232
+
233
+ ### Example Captures
234
+
235
+ | File | Packets | Devices | Connections | What's inside |
236
+ |------|---------|---------|-------------|---------------|
237
+ | `LE_Phone_Alert_Status_Profile.pcapng` | 2,359 | 16 | 2 | Simple Bluetooth LE GATT profile with scanning and data exchange |
238
+ | `Encrypted Advertising Data.pcapng` | 1,547 | 13 | 0 | Advertising only — no connections, good for device discovery analysis |
239
+ | `AVDTP_and_eSCO.pcapng` | — | — | — | Bluetooth Classic audio (A2DP streaming + voice calls) |
240
+ | `BIS.pcapng` | — | — | — | LE Audio Broadcast Isochronous Stream |
241
+ | `CIS_and_AVDTP_and_HCI.pcapng` | — | — | — | Mixed: LE Audio CIS + Classic audio + HCI commands |
242
+ | `audiopod_LE_Audio_CIG.pcapng` | 53,887 | 186 | 14 | Dense LE Audio capture — NRF5340 devices, CRC errors, encrypted traffic |
243
+
244
+ ### Start here: simple Bluetooth LE profile
245
+
246
+ ```
247
+ Load LE_Phone_Alert_Status_Profile.pcapng and tell me what's going on.
248
+ ```
249
+
250
+ ```
251
+ Inspect all connections. What protocols are being used?
252
+ ```
253
+
254
+ ### Advertising-only analysis
255
+
256
+ ```
257
+ Load Encrypted Advertising Data.pcapng. How many devices are advertising? Inspect all of them.
258
+ ```
259
+
260
+ ```
261
+ Search for SCAN_REQ packets. Which devices are being actively scanned?
262
+ ```
263
+
264
+ ### Stress test: large LE Audio capture
265
+
266
+ ```
267
+ Load audiopod_LE_Audio_CIG.pcapng and summarize it.
268
+ ```
269
+
270
+ ```
271
+ Inspect all 14 connections at once. Which ones have the most data packets?
272
+ ```
273
+
274
+ ```
275
+ Find all errors. What's causing the CRC failures?
276
+ ```
277
+
278
+ ```
279
+ Look at connection 9 — that's the NRF5340_AUDIO device. What's it doing?
280
+ ```
281
+
282
+ Or use the built-in `analyze-capture` prompt for a guided walkthrough of any file.
283
+
284
+ ## Live Hardware Capture
285
+
286
+ If you have a BlueSPY sniffer connected via USB, the MCP server can control it directly — connect, start/stop captures, and analyze results in real time.
287
+
288
+ ```
289
+ You: Connect to my sniffer and capture Bluetooth LE traffic for 10 seconds.
290
+
291
+ Claude: Connected to Moreph serial 2411001234.
292
+ Capturing Bluetooth LE packets for 10 seconds...
293
+ Capture complete — saved to ble_capture_20260228.pcapng (12,847 packets).
294
+
295
+ You: Summarize what you captured.
296
+
297
+ Claude: 12,847 packets over 10 seconds.
298
+ 87 devices detected, 12 active connections.
299
+ → 4,231 ADV_IND (connectable advertisements)
300
+ → 1,892 ADV_NONCONN_IND (beacons)
301
+ → 3,104 LE_DATA (active data transfer)
302
+ ```
303
+
304
+ Hardware access is subprocess-isolated — if a hardware call hangs, the MCP server kills the worker process and stays responsive. A file lock (`~/.bluespy-mcp.lock`) ensures only one client controls the hardware at a time.
305
+
306
+ ## Model Recommendations
307
+
308
+ This MCP server works with any LLM that supports tool use. The built-in prompt templates (`analyze-capture`, `quick-capture`, `debug-connection`) guide even smaller models through the correct multi-step workflows.
309
+
310
+ | Tier | Models | Best For |
311
+ |------|--------|----------|
312
+ | **Minimum** | Haiku 4.5, GPT-4o mini, Gemini Flash | Loading captures, running guided workflows, basic summaries. Handles the full tool chain reliably. |
313
+ | **Recommended** | Sonnet 4.5, GPT-4o, Gemini Pro | Deeper protocol analysis — correlating error patterns across connections, interpreting RSSI trends, diagnosing RF interference, generating actionable recommendations. |
314
+ | **Advanced** | Opus, o3, Gemini Ultra | Multi-capture comparison, cross-referencing against Bluetooth spec, complex protocol-level debugging. Rarely needed. |
315
+
316
+ **Key factors for good results:**
317
+ - **Use the prompt templates.** They walk any model through load → summarize → analyze → inspect in the right order.
318
+ - **Tool-use reliability matters more than model size.** A mid-tier model that follows tool sequences correctly will outperform a large model that skips steps.
319
+ - **Domain knowledge helps but isn't required.** The server returns structured JSON with classified packet types, so the model doesn't need to know Bluetooth LE internals to report useful findings.
320
+
321
+ ## Development
322
+
323
+ ```bash
324
+ git clone https://github.com/novelbits/bluespy-mcp.git
325
+ cd bluespy-mcp
326
+ pip install -e .
327
+ pytest
328
+ ```
329
+
330
+ To run the full test suite including end-to-end and hardware tests (requires a BlueSPY sniffer connected via USB):
331
+
332
+ ```bash
333
+ pip install -e ".[e2e]"
334
+ bash scripts/test.sh all
335
+ ```
336
+
337
+ ## Troubleshooting
338
+
339
+ ### "BlueSPY library not found"
340
+
341
+ Ensure `BLUESPY_LIBRARY_PATH` points to the correct native library for your platform. The BlueSPY application must be installed from [rfcreations.com](https://rfcreations.com).
342
+
343
+ ### "No capture file loaded"
344
+
345
+ Use `load_capture()` with a path to your .pcapng file before calling analysis tools.
346
+
347
+ ### Claude Desktop crashes on startup
348
+
349
+ If you're using FastMCP v3.0.2+, ensure the server runs with `show_banner=False` (this is the default in bluespy-mcp). The FastMCP banner corrupts the MCP stdio protocol.
350
+
351
+ ### Hardware: "bluespy_init failed"
352
+
353
+ The MCP server retries automatically when this happens, but if it persists:
354
+
355
+ 1. **Connect directly to your computer** — do not use a USB hub. The Moreph sniffer requires a direct USB connection for reliable communication.
356
+ 2. **Close the BlueSPY desktop app** if it's open. Only one application can control the hardware at a time. Check the device LED: green means it's in use, blue means it's available.
357
+ 3. **Unplug and replug** the device, then try again.
358
+
359
+ ### Hardware: "in use by another session"
360
+
361
+ This means the file lock (`~/.bluespy-mcp.lock`) is held by another MCP session. If no other session is actually running (e.g., a previous session crashed), use the `force` parameter:
362
+
363
+ ```
364
+ connect_hardware(force=True)
365
+ ```
366
+
367
+ This removes the stale lock file and proceeds with the connection.
368
+
369
+ ### Hardware: Python crash on exit
370
+
371
+ You may see a macOS crash report (`Python quit unexpectedly`) after using hardware tools. This is a known issue with the BlueSPY native library's cleanup routine (`bluespy_deinit`) and does not affect capture data or MCP server operation. The crash occurs during process exit and can be safely dismissed.
372
+
373
+ ### Module not found errors
374
+
375
+ Ensure you're using Python 3.10+ and have installed the package:
376
+
377
+ ```bash
378
+ python --version # Should be 3.10+
379
+ pip install bluespy-mcp
380
+ ```
381
+
382
+ ## License
383
+
384
+ MIT License. See [LICENSE](LICENSE) for details.
385
+
386
+ ## Links
387
+
388
+ - [BlueSPY by RFcreations](https://rfcreations.com)
389
+ - [MCP Protocol](https://modelcontextprotocol.io)
390
+ - [Issues](https://github.com/novelbits/bluespy-mcp/issues)