mcpField
B
Bluetooth Low Energy (BLE) MCP server -- scan, connect and interact with BLE peripherals

Bluetooth Low Energy (BLE) MCP server -- scan, connect and interact with BLE peripherals

ローカル
github.com/stass開発ツール2522 週間前に更新

Bluetooth Low Energy (BLE) MCP server -- scan, connect and interact with BLE peripherals について

blew は macOS 向けの Bluetooth Low Energy(BLE)コマンドラインツールで、デバイスのスキャン、GATT 構造の確認、キャラクタリスティックの読み書き、通知の購読を行えます。GUI を使わずに BLE デバイスをデバッグしたい開発者や組み込みエンジニア向けです。

主な機能

  1. 01信号強度付きで近くの BLE デバイスをスキャン
  2. 02GATT サービスとキャラクタリスティックのツリー表示
  3. 03キャラクタリスティックの読み書きと通知の購読
  4. 04スクリプト処理向けのキーバリュー出力
  5. 05Mac を仮想 BLE ペリフェラルとして動作させる機能

活用シーン

  1. 01BLE ペリフェラルのサービスとキャラクタリスティックのデバッグ
  2. 02センサー通知の長時間のログ記録
  3. 03スクリプトや CI での複数ステップの BLE テスト実行

MCP 対応

本プロジェクトには mcp サブコマンドがあり、BLE 操作を MCP ツールとして AI エージェントに公開します。

設定

ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。

インストール方法は未掲載です

このサーバーのパッケージやリモートエンドポイントはまだ取得できていません。プロジェクトの README の手順に従ってください。

リポジトリ

ツール

ツール一覧はまだ取得されていません

ローカルサーバーのツールはマシン上で起動して初めて取得できます。メンテナーはまだツール一覧を提供していません。

詳細

blew -- Mac OS X 向け BLE スキャナー兼 CLI ツール

Bluetooth Low Energy デバイスのデバッグに GUI アプリを使うのはもうやめましょう。blew を使えば、macOS のコマンドラインから BLE を自在に操作できます。電波をスキャンし、任意のデバイスの GATT ツリーを詳しく調べ、キャラクタリスティックの読み取りや書き込み、通知のリアルタイム受信ができるほか、ほかのデバイスが接続できる仮想ペリフェラルも起動できます。

BLE スキャン

GATT ツリー UART

blew を選ぶ理由
  • ツールは 1 つ、面倒な手順は不要です。 スキャン、接続、調査、読み取り、書き込み、購読は、それぞれ 1 つのコマンドで実行できます。自動接続により、本格的な作業を始める前に手動でペアリングする必要はありません。
  • 必要に応じて対話的に使えます。 REPL を起動すれば、タブ補完、永続的な履歴、入力を続けながら表示されるバックグラウンド購読を備えた探索セッションを実行できます。
  • 必要に応じてスクリプト化できます。 exec でコマンドを連結し、機械可読な kv 出力を awk やログファイルにパイプし、CI や監視スクリプトで決定的な終了コードを利用できます。
  • GATT を詳細に確認できます。 任意のデバイスのサービス/キャラクタリスティックツリーを一度に表示できます。すべての値をインラインで読み取れます。接続しなくても、Bluetooth SIG の任意のキャラクタリスティックのフィールドレベル仕様を参照できます。
  • ペリフェラルモード。 Mac を仮想 BLE デバイスにできます。JSON 設定から GATT サーバーを定義するか、実際のデバイスのサービスツリー全体を複製して再現できます。
  • デフォルトで人が読みやすい表示です。 標準的な Bluetooth SIG UUID は、スキャン結果、GATT ツリー、通知など、あらゆる箇所で名前に変換されます。変換には公式の Bluetooth SIG データベースを使用しています。
動作モード
  • コマンドモード -- 単一のコマンドを実行して終了します:blew [global-options] <command> [command-options]
  • 対話型 REPL -- コマンドを指定せずに blew を実行すると、履歴とタブ補完に対応した readline 形式のシェルが起動します
  • スクリプトモード -- セミコロン区切りのコマンド列を、1 つの接続を共有して実行します:blew exec "connect -n Sensor; gatt tree; read -f uint8 2A19"
  • MCP サーバーモード -- すべての BLE 操作を AI エージェント向けの MCP ツールとして公開します:blew mcp

macOS 13 以降と Bluetooth の使用許可が必要です。

できること

実際のデバイスを複製して、そのデバイスになりすまします。 心拍計に近づき、GATT ツリー全体を複製すると、Mac がそのデバイスとしてアドバタイズを開始します。ほかのアプリは、実際のデバイスと同じようにクローンへ接続できます:

% blew periph clone -n "Heart Rate Monitor" --save hr.json

BLE の電波をリアルタイムで監視します。 周囲のすべてのデバイスを、リアルタイムで更新される電波強度バーとともに表示します。名前、サービス、電波強度の下限で絞り込み、混雑した場所でも必要なデバイスを見つけられます:

% blew scan -w -R -70

デバイスの全容を一度に調べます。 接続してすべてのサービスを検出し、読み取り可能なキャラクタリスティックをすべて読み取り、名前、プロパティ、ディスクリプター、リアルタイム値を含む全体をツリー形式で表示します:

% blew -n "Thingy" gatt tree -dr

デバイスがなくても、Bluetooth SIG の任意のキャラクタリスティックを参照できます。 バイト配置、型、条件付きフィールドなど、標準キャラクタリスティックのフィールドレベルの構造をすぐに確認できます:

% blew gatt info 2A37    # Heart Rate Measurement spec

センサーデータをログに直接ストリーミングします。 キャラクタリスティックを購読し、値をキーと値のペアとしてフォーマットして、ファイルや別のツールにパイプします。ヘッドレスで動作し、タイムアウト時には正常に終了します:

% blew -o kv sub -n "Sensor" -f uint16le -d 3600 fff1 >> hourly.log

JSON ファイルから仮想 BLE デバイスを起動します。 設定ファイルでサービス、キャラクタリスティック、プロパティ、初期値を定義し、1 つのコマンドでアドバタイズを開始します:

% blew periph adv --config health-thermometer.json

複数ステップのテストシーケンスを 1 行で実行します。 接続、調査、読み取り、書き込み、待機、再読み取り、または通知の購読を、単一のコマンドとして実行できます:

% blew exec -k "connect -n Sensor; gatt tree; write -f uint8 fff2 01; sleep 2; read -f uint16le fff1"

インストール

Homebrew
brew install stass/tap/blew
要件
  • macOS 14(Sonoma)以降
  • Xcode 16 以降(Swift 6.0 以降のツールチェーン)

MCP サーバー(blew mcp サブコマンド)は MCP swift-sdk に依存しており、Swift 6.1 のツールチェーン(Xcode 16.3 以降)が必要です。古いツールチェーンでも blew をビルドできるよう、MCP のサポートは条件付きでコンパイルされます。Swift 6.1 以降でビルドすると mcp サブコマンドが利用可能になり、Swift 6.0.x(Xcode 16.0〜16.2)でビルドすると MCP サーバーは省略され、その他のコマンドはすべて変更なく動作します。フラグは不要です。判定は Package.swift で自動的に行われます。

ソースからビルド
git clone --recurse-submodules https://github.com/stass/blew.git
swift build -c release
cp .build/release/blew /usr/local/bin/blew

Bluetooth SIG の名前データベースと特性データベースは、Vendor/ 配下の Git サブモジュールとして含まれているため、--recurse-submodules が必要です。Swift ビルドでは、サブモジュールのデータからすべてのデータファイルが自動的に生成されます。

初回起動時に、macOS から Bluetooth の使用許可を求められます。システム設定 → プライバシーとセキュリティ → Bluetooth で許可してください。


クイックスタート

# Scan for nearby devices
% blew scan

# Print the GATT tree of a device (connects automatically)
% blew -n "Thingy" gatt tree

# Read a characteristic (battery level)
% blew -n "Thingy" read -f uint8 2A19

# Subscribe to notifications for 10 seconds
% blew -n "Thingy" sub -d 10 fff1

# Run a multi-step procedure in one invocation
% blew -n "Thingy" -x "gatt tree; read -f uint8 2A19"

# Start the interactive REPL
% blew

グローバルオプション

これらのオプションはすべてのコマンドに適用され、サブコマンド名より前に指定する必要があります。

% blew [global-options] <command> [command-options]
フラグ説明
-v, --verboseログの詳細度を上げます。繰り返し指定できます。-vv でデバッグレベルの詳細を表示します。
-o, --out <format>結果の出力形式です。text(デフォルト。人が読みやすい形式)または kv(key=value。1 行につき 1 レコードで、解析が容易)を指定します。
-t, --timeout <sec>BLE 操作のタイムアウト(秒)です。
-h, --helpヘルプを表示して終了します。
--versionバージョンを表示して終了します。

デバイス指定オプション

デバイスに接続するコマンド(scan、connect、gatt、read、write、sub、periph clone)では、対象を特定するためにこれらのオプションを使用できます。サブコマンド名の後に指定してください。

特定のデバイスを対象にするには --id を使用するか、複数のセレクターを組み合わせて blew に一致するデバイスを自動的に検索させます。

フラグ説明
-i, --id <device-id>識別子で特定のデバイスを指定します。
-n, --name <substring>デバイス名で絞り込みます(部分一致)。
-S, --service <uuid>アドバタイズされたサービス UUID で絞り込みます。繰り返し指定できます。
-m, --manufacturer <id>メーカー ID で絞り込みます。
-R, --rssi-min <dBm>RSSI の最小しきい値です(例:-R -70)。
-p, --pick <strategy>複数のデバイスが一致した場合の選択方法です。strongest(デフォルト。RSSI が最も高いデバイス)、first(最初に検出されたデバイス)、only(複数一致した場合はエラー)を指定します。

コマンド

scan — BLE デバイスをスキャンする
blew scan [-n <name>] [-S <uuid>] [-R <dBm>] [-i <id>] [-m <id>] [-p <strategy>] [-w]

アドバタイズ中の BLE 周辺機器をスキャンし、検出されたデバイスの一覧を表示します。タイムアウトのデフォルトは 5 秒です。

フラグ:

フラグ説明
-n, --nameデバイス名の部分一致で結果を絞り込みます。
-S, --serviceアドバタイズされたサービス UUID で絞り込みます。繰り返し指定できます。
-R, --rssi-minRSSI の最小しきい値です(例:-R -70)。
-i, --idこの識別子を持つデバイスのみを表示します。
-m, --manufacturerメーカー ID で絞り込みます。
-p, --pick選択方法です。strongest、first、only を指定できます。
-w, --watch継続的にスキャンし、デバイス一覧をリアルタイムで更新します。Ctrl-C を押すか、-t の時間が経過するまで実行します。テキストモードでは TTY が必要です。パイプ出力には -o kv を使用してください。

出力列(テキスト): ID、名前、RSSI、信号(視覚的なバー)、サービス

サービス列では、標準の Bluetooth SIG サービス UUID は人が読みやすい名前で表示されます。例:180F (Battery Service), 180A (Device Information)。

例:

% blew scan                          # Scan for 5 seconds
% blew -t 10 scan                    # Scan for 10 seconds
% blew scan -w                       # Continuously scan until Ctrl-C
% blew -t 30 scan -w                 # Watch for 30 seconds then stop
% blew scan -n "Heart" -w            # Watch for Heart Rate devices
% blew scan -S 180D -R -65           # Heart Rate service, RSSI ≥ -65 dBm
% blew -o kv scan                    # Machine-readable output
% blew -o kv scan -w                 # Stream device updates (piping-friendly)

connect — デバイスに接続する
blew connect [-n <name>] [-S <uuid>] [-i <id>] [<device-id>]

BLE デバイスに明示的に接続して終了します。接続のテストや、exec スクリプトで事前に接続を確立しておく場合に便利です。デバイスは位置引数、--id、またはデバイス指定セレクターで指定できます(セレクターの場合、ツールは短時間スキャンして対象を特定します)。

gatt、read、write、sub では、先に connect を実行する必要はありません。これらのコマンドは同じデバイス指定オプションを使用して自動的に接続します。

終了時に接続は自動的に閉じられます。

例:

% blew connect F3C2A1B0-...          # Test connectivity to an explicit ID
% blew connect -n "Thingy"           # Test connectivity by name
% blew connect -S 180F               # Connect to device advertising Battery Service

gatt — GATT 構造を調べる

アクティブな接続がない場合、gatt は自動的に接続します。--id、--name、またはその他のデバイス指定オプションで対象デバイスを指定してください。

すべての UUID 出力には、Bluetooth SIG 標準 UUID の人間が読みやすい名前が含まれます。カスタム UUID またはベンダー UUID はそのまま表示されます。

gatt svcs — サービスを一覧表示する
blew gatt svcs [-n <name>] [-i <id>]

検出されたすべてのサービスを一覧表示します。出力列:UUID、名前、プライマリ。

UUID   Name                  Primary
------------------------------------
180F   Battery Service       yes
180A   Device Information    yes
FFF0                         yes
gatt tree — GATT ツリー全体を表示する
blew gatt tree [-n <name>] [-i <id>] [-d] [-r]

サービスと、そのプロパティ(読み取り/書き込み/通知/表示)を含むキャラクタリスティックを出力します。標準 UUID には、人間が読みやすい名前も併記されます。

Service 180F  Battery Service
├── 2A19  Battery Level  [read, notify]
│   └── 2902  Client Characteristic Configuration
└── FFF1  [read, write-without-response, notify]

Service FFF0
└── FFF1  [read, write-without-response, notify]
フラグ説明
-d, --descriptors各キャラクタリスティックのディスクリプターも表示します。
-r, --read読み取り可能なすべてのキャラクタリスティックの値を読み取り、インラインで表示します。既知の Bluetooth SIG キャラクタリスティックは自然な型(例:バッテリーレベルは整数、メーカー名は文字列)にデコードされ、それ以外は 16 進数で表示されます。

-r を指定した場合:

Service 180F  Battery Service
└── 2A19  Battery Level  [read, notify]  = 85

Service 180A  Device Information
├── 2A29  Manufacturer Name String  [read]  = Apple Inc.
└── 2A24  Model Number String  [read]  = MacBookPro18,3
gatt chars — サービスのキャラクタリスティックを一覧表示する
blew gatt chars [-n <name>] [-i <id>] [-r] <service-uuid>

出力列:UUID、名前、プロパティ。

フラグ説明
-r, --read読み取り可能なすべてのキャラクタリスティックの値を読み取り、表示します。テーブルに値列が追加され、読み取り不可のキャラクタリスティックは空欄になります。
gatt desc — キャラクタリスティックのディスクリプターを一覧表示する
blew gatt desc [-n <name>] [-i <id>] <char-uuid>

出力列:UUID、名前。

gatt info — Bluetooth SIG 仕様のキャラクタリスティック情報を表示する
blew gatt info <char-uuid>

標準キャラクタリスティック UUID の Bluetooth SIG 名、説明、フィールド単位の構造を表示します。接続済みデバイスは必要ありません。生成されたキャラクタリスティックデータベースから直接読み取ります。

$ blew gatt info 2A37
Heart Rate Measurement (2A37)

The Heart Rate Measurement characteristic is used to represent data related to a heart rate measurement.

Structure:
  Flags                                             boolean[8]  1 byte
  Heart Rate Measurement Value (8 bit resolution)   uint8       1 byte  [present if bit 0 of Flags is 0]
  Heart Rate Measurement Value (16 bit resolution)  uint16      2 bytes  [present if bit 0 of Flags is 1]
  Energy Expended                                   uint16      2 bytes  [present if bit 3 of Flags is 1]
  RR-interval                                       opaque      variable (array)  [present if bit 4 of Flags is 1]

埋め込みの Date Time など、構造体型のフィールドは、ドット区切りの名前で再帰的にインライン表示されます。

$ blew gatt info 2A0A
Day Date Time (2A0A)

The Day Date Time characteristic is used to represent weekday, date, and time.

Structure:
  Date Time.Year           uint16  2 bytes
  Date Time.Month          uint8   1 byte
  Date Time.Day            uint8   1 byte
  Date Time.Hours          uint8   1 byte
  Date Time.Minutes        uint8   1 byte
  Date Time.Seconds        uint8   1 byte
  Day of Week.Day of Week  uint8   1 byte

-o kv を指定すると、スクリプトで使用できるように、フィールドごとに 1 つのレコードが出力されます。

例:

% blew gatt tree -n "Thingy"
% blew gatt tree -n "Thingy" -d        # Include descriptors
% blew gatt tree -n "Thingy" -r        # Read values for readable characteristics
% blew gatt tree -n "Thingy" -dr       # Descriptors + values
% blew gatt chars -n "Thingy" 180F
% blew gatt chars -n "Thingy" -r 180A  # Read values for Device Information
% blew gatt desc -n "Thingy" 2A19

read — キャラクタリスティックの値を読み取る
blew read [-n <name>] [-i <id>] [-f <format>] <char-uuid>

キャラクタリスティックの値を読み取り、指定された形式で出力します。アクティブな接続がない場合は自動的に接続します。

フラグ説明
-f, --format <fmt>出力形式(以下の表を参照)。デフォルト:hex。

形式:

形式説明
hex16 進数文字列(デフォルト)
utf8UTF-8 文字列
base64Base64 エンコード文字列
uint8符号なし 8 ビット整数
uint16le符号なし 16 ビット整数、リトルエンディアン
uint32le符号なし 32 ビット整数、リトルエンディアン
float32le32 ビット浮動小数点数、リトルエンディアン
raw生バイト列

例:

% blew read -n "Thingy" -f uint8 2A19     # Battery level as integer
% blew read -n "Thingy" -f utf8 2A29      # Manufacturer name string
% blew read -n "Thingy" fff1              # Raw characteristic as hex

write — キャラクタリスティックに書き込む
blew write [-n <name>] [-i <id>] [-f <format>] [-r|-w] <char-uuid> <data>

キャラクタリスティックにデータを書き込みます。アクティブな接続がない場合は自動的に接続します。上書きされない限り、キャラクタリスティックのプロパティに基づいて、レスポンスあり/なしの書き込みモードが自動的に選択されます。

フラグ説明
-f, --format <fmt>データ形式(read と同じ値)。デフォルト:hex。
-r, --with-responseレスポンスありの書き込みを強制します。
-w, --without-responseレスポンスなしの書き込みを強制します。

例:

% blew write -n "Thingy" fff1 "deadbeef"           # Write hex bytes
% blew write -n "Thingy" -f uint8 -r 2A06 1        # Write uint8 with response
% blew write -n "Thingy" -f utf8 fff2 "hello"      # Write a UTF-8 string

sub — 通知またはインディケーションを購読する
blew sub [-n <name>] [-i <id>] [-f <format>] [-d <sec>] [-c <count>] [-b] <char-uuid>

キャラクタリスティックを購読し、受信した値をイベントごとに1行で標準出力へストリーミングします。接続がアクティブでない場合は自動的に接続します。Ctrl-C を押すか、時間または件数の上限に達すると停止します。

フラグ説明
-f, --format <fmt>値の形式(read と同じ値)。デフォルト:hex。
-d, --duration <sec>指定した秒数後に停止します。
-c, --count <n>指定した件数の通知を受信した後に停止します。
-b, --bgバックグラウンドで実行します(REPL と exec モードのみ)。すぐにプロンプトへ戻り、通知は到着時に出力されます。キャンセルするには sub stop を使います。

--out kv を指定すると、各行に ts=、char=、value= フィールドが含まれます。

バックグラウンドモード(-b)は REPL と exec スクリプトで利用できます。複数のキャラクタリスティックを同時に購読できます。バックグラウンド通知は、プロンプト上に見やすく表示されるよう、ANSI カーソル制御を使って stderr に出力されます。

blew> sub -b -f uint8 2A19
Subscribing to 2A19 (Battery Level) in background. Use 'sub stop 2A19' to stop.
blew> sub -b fff1
Subscribing to fff1 in background. Use 'sub stop fff1' to stop.
blew> sub status
Active background subscriptions:
  2A19 (Battery Level)
  fff1
blew> sub stop 2A19
Stopped subscription for 2A19 (Battery Level).
blew> sub stop
Stopped all background subscriptions.

例:

% blew sub -n "Thingy" fff1                        # Stream indefinitely (Ctrl-C to stop)
% blew sub -n "Thingy" -f uint16le -d 30 fff1      # 30-second capture as uint16
% blew -o kv sub -n "Thingy" -c 100 2A37           # Capture 100 events, kv output
% blew -o kv sub -n "Thingy" fff1 >> data.log      # Append to a log file

対話型 REPL

引数なし(またはサブコマンドなしでグローバルオプションを指定して)blew を実行すると、対話型 REPL が起動します。

% blew
% blew -v           # Start verbose REPL

REPL では次の操作ができます。

  • 行編集 — カーソル移動、Ctrl-A/E、Ctrl-W など
  • 履歴の保存 — セッション間で ~/.config/blew/history に保存
  • タブ補完 — コマンド、GATT 検出後の既知の UUID、形式名
REPL コマンド
コマンド説明
scan [options]デバイスをスキャンします
connect [<id>]デバイスに接続します
disconnect切断します
status接続状態を表示します
gatt svcs|tree|chars|desc|infoGATT を検査します
read [-f <fmt>] <uuid>キャラクタリスティックを読み取ります
write [-f <fmt>] [-r|-w] <uuid> <data>キャラクタリスティックに書き込みます
sub [-f <fmt>] [-d <s>] [-c <n>] <uuid>通知を購読します(停止するには Ctrl-C)
sub [-b] [-f <fmt>] [-d <s>] [-c <n>] <uuid>バックグラウンドで購読します。プロンプトは引き続き使用できます
sub stop [<uuid>]1つまたはすべてのバックグラウンド購読を停止します
sub statusアクティブなバックグラウンド購読を一覧表示します
periph adv [-n <n>] [-S <uuid>] [--config <f>]仮想ペリフェラルとしてアドバタイズを開始します
periph clone [--save <f>]実際のデバイスの GATT を複製します
periph stopアドバタイズを停止します
periph set [-f <fmt>] <uuid> <val>キャラクタリスティックの値を更新します
periph notify [-f <fmt>] <uuid> <val>購読者に通知を送信します
periph statusペリフェラルのアドバタイズ状態を表示します
help使用可能なコマンドを表示します
quit / exitREPL を終了します

セッションの例:

blew> scan -t 3 -n Thingy
ID                                    NAME    RSSI  Signal    Services
-------------------------------------------------------------------------------------------
F3C2A1B0-1234-5678-ABCD-000000000001  Thingy  -58   ████████  180F (Battery Service), 180A (Device Information)

blew> connect F3C2A1B0-1234-5678-ABCD-000000000001

blew> gatt tree
Service 180F  Battery Service
└── 2A19  Battery Level  [read, notify]

Service 180A  Device Information
└── 2A29  Manufacturer Name String  [read]

blew> read -f uint8 2A19
87

blew> sub -f uint8 -c 5 2A19
87
86
86
85
85

blew> quit

スクリプト実行(exec)

exec サブコマンドは、セミコロンで区切られた一連のコマンドを1つのプロセスで実行し、接続のライフサイクルを共有します。コマンドは REPL と同じ方法で解析されます。接続が必要な最初のコマンドで自動接続が行われ、それ以降のコマンドではその接続が再利用されます。

% blew exec "connect -n Thingy; gatt tree; read -f uint8 2A19"

フラグ:

フラグ説明
-k, --keep-goingコマンドエラー後も続行し、最初に発生したゼロ以外のコードで終了します。
--dry-run実行せずに解析済みのステップを表示します。

exec と REPL で使用できる追加コマンド:

コマンド説明
sleep <seconds>指定した秒数だけ一時停止します。0 を指定すると、Ctrl-C を押すまで無期限に一時停止します。

例:

# Connect by name, read a value
% blew exec "connect -n Thingy; read -f uint8 2A19"

# Wait 2 seconds between operations
% blew exec "connect -n Thingy; sleep 2; read -f uint8 2A19"

# Keep going after errors
% blew exec -k "read fff1; read fff9"

# Preview what would run
% blew exec --dry-run "connect -n Thingy; gatt tree; read -f uint8 2A19"

出力とログ

stdout と stderr
ストリーム内容
stdoutコマンドの結果 — テーブル、値、通知行
stderr運用メッセージ(エラー、詳細情報、デバッグ情報)
ログ形式(stderr)

エラーは常に出力されます。

Error: timeout waiting for connection

-v を指定すると、情報メッセージも表示されます。

connecting to F3C2A1B0-...
connected to F3C2A1B0-...

-vv を指定すると、デバッグレベルのメッセージが追加されます。

[debug] discovered service 180F
[debug] discovered characteristic 2A19
キーと値形式の出力(--out kv)

機械で解析できる出力を得るには、-o kvを渡します。各レコードは、スペース区切りのkey=valueペアで1行に出力されます(スペースを含む値は引用符で囲まれます)。awk、grep、または任意のログパーサーで簡単に処理できます。

id=F3C2A1B0-... name=Thingy rssi=-58 services=180F,180A
char=2A19 name="Battery Level" value=57 fmt=uint8
ts=2026-02-21T12:34:56.789Z char=fff1 value=deadbeef

UUIDが既知のBluetooth SIG UUIDの場合、name=フィールドが含まれます。カスタムUUIDまたはベンダーUUIDの場合は省略されます。

% blew -n "Thingy" -o kv sub -d 60 fff1 | awk -F'value=' '{print $2}'

終了コード

コード意味
0成功
2見つからない/一致するデバイスがない
3Bluetoothが利用できない、またはアクセス許可が拒否された
4タイムアウト
5操作に失敗(接続/GATT/読み取り/書き込み/購読)
6引数が無効

periph — ペリフェラル(GATT サーバー)モード

periphを使うと、Macを仮想BLEペリフェラルとして動作させ、近くのデバイスから接続して、読み取り、書き込み、購読できるようになります。

CoreBluetoothペリフェラルの制限事項: アドバタイズできるのは、ローカルデバイス名とサービスUUIDのみです。ペリフェラルAPIからメーカーのデータやその他のADVペイロードフィールドは設定できません。ADV間隔、TX電力、接続パラメーターはOSによって制御されます。

アドバタイズ名とホスト名: macOSは常に、コンピューターのホスト名をGAP(Generic Access Profile)のデバイス名として使用します。以前にMacに接続したことのあるiOSスキャナーでは、デバイスリストに-nで設定したアドバタイズ名ではなく、キャッシュされたGAP名(ホスト名)が表示されます。アドバタイズ名は生のアドバタイズデータ(kCBAdvDataLocalName)には引き続き含まれており、デバイスをタップした後、LightBlueなどのツールの「Advertisement Data」で確認できます。

多くの標準Bluetooth SIGサービスUUIDは短縮形(例:180D、1809、181A)で動作します。ただし、macOS自体がペリフェラルとして公開しているサービスは短縮形ではブロックされます。CBPeripheralManagerは1800(Generic Access)、1801(Generic Attribute)、1805(Current Time)、180A(Device Information)、180F(Battery)、1812(HID)、181E(Bond Management)を「The specified UUID is not allowed for this operation.」というエラーで拒否します。完全な128ビットのBluetooth Base UUID形式(例:0000180F-0000-1000-8000-00805F9B34FB)を使うとチェックを回避できますが、生の128ビットUUIDとして登録されるため、セントラルは標準サービスとして認識できません。

periph adv — GATTサーバーのアドバタイズとホスト
blew periph adv [-n <name>] [-S <uuid>] [--config <file>]

アドバタイズを開始し、中断されるまで(Ctrl-C)実行します。イベント(読み取り、書き込み、購読)は標準出力に記録されます。

フラグ説明
-n, --name <name>アドバタイズするデバイス名。デフォルトはblewです。
-S, --service <uuid>アドバタイズするサービスUUID。複数回指定できます。
-c, --config <file>サービスと特性を定義するJSON設定ファイル。

設定ファイルの形式:

{
  "name": "My Device",
  "services": [
    {
      "uuid": "180F",
      "primary": true,
      "characteristics": [
        {
          "uuid": "2A19",
          "properties": ["read", "notify"],
          "value": "55",
          "format": "uint8"
        }
      ]
    }
  ]
}

propertiesには、read、write、writeWithoutResponse、notify、indicateを指定できます。 formatでは、read/writeの--formatと同じ値(hex(デフォルト)、utf8、uint8、uint16le、uint32le、float32le、base64、raw)を指定できます。

例:

# Advertise a name + service UUID (scanner-visible, no GATT characteristics)
% blew periph adv -n "My Sensor" -S 180F

# Multiple service UUIDs
% blew periph adv -n "My Sensor" -S 180F -S 180A

# Full GATT server from a config file
% blew periph adv --config device.json

# Config file with name override
% blew periph adv -n "Override Name" --config device.json

設定ファイルの例はExamples/ディレクトリにあります。

ファイル説明
health-thermometer.jsonHealth Thermometer(1809)— 温度測定値(2A1C)を通知、温度の種類(2A1D)を読み取り、測定間隔(2A21)を読み取り/書き込み/通知
environmental-sensing.jsonEnvironmental Sensing(181A)— 温度(2A6E)、湿度(2A6F)、気圧(2A6D)を読み取り/通知
blood-pressure.jsonBlood Pressure(1810)— 測定値(2A35)を通知し、機能フラグ(2A49)を読み取り
custom-sensor.json128ビットUUIDを使った完全なカスタムベンダーサービス — 値の読み取り/通知、設定レジスターの読み取り/書き込み、コマンドエンドポイントへの書き込み専用アクセス

出力(テキストモード):

Advertising "My Sensor" [180F (Battery Service)]
  Service 180F (Battery Service)
  +-- 2A19 (Battery Level) [read, notify]

[12:34:56] central A1B2C3D4 connected
[12:34:56] read 2A19 (Battery Level) by A1B2C3D4
[12:34:57] subscribe 2A19 (Battery Level) by A1B2C3D4
[12:35:01] write 2A19 (Battery Level) by A1B2C3D4 <- 2a
^C
Stopped advertising.

出力(kvモード):

event=connected ts=12:34:56 central=A1B2C3D4-...
event=read ts=12:34:56 central=A1B2C3D4-... char=2A19
event=subscribe ts=12:34:57 central=A1B2C3D4-... char=2A19
event=write ts=12:35:01 central=A1B2C3D4-... char=2A19 value=2a
periph clone — 実機をクローンする
blew periph clone [-n <name>] [-i <id>] [--save <file>]

対象デバイスに接続して、GATT ツリー全体を検出し、読み取り可能な特性値をすべて読み取ってから切断し、クローンとしてアドバタイズを開始します。

フラグ説明
-n, --name対象デバイス名のフィルターです。
-i, --id対象デバイスの識別子です。
-o, --save <file>クローンした GATT 構造を、後で再利用できるよう JSON 設定ファイルに保存します。

クローンの範囲:

  • クローン対象:アドバタイズ名、サービス UUID、GATT 構造全体、初期特性値(読み取り可能な特性の場合)。
  • クローン対象外:メーカー データ、ADV ペイロード内のサービスデータ、ADV のタイミング。

例:

% blew periph clone -n "Heart Rate Monitor"
% blew periph clone -i F3C2A1B0-... --save hr-monitor.json
REPL 専用の periph コマンド

REPL モードと exec モードでは、periph adv と periph clone は 2 つのフェーズで実行されます。起動フェーズ(設定とアドバタイズの開始)は同期的に実行され、ペリフェラルが正常に起動したことを確認します。アドバタイズが確認されると、イベントループはバックグラウンドタスクとして実行され、プロンプトがすぐに戻ります。これにより、同じセッションで periph stop、periph set、periph notify を使用できます。

コマンド説明
periph stopアドバタイズを停止し、バックグラウンドのイベントタスクをキャンセルします。
periph set [-f <fmt>] <char> <val>特性の保存値を更新します。
periph notify [-f <fmt>] <char> <val>値を更新し、すべてのサブスクライバーに通知を送信します。
periph statusアドバタイズの状態、サービス/特性の数、サブスクライバーの数を表示します。
blew> periph adv --config device.json
Advertising "My Device" [180F]
Advertising in background. Use 'periph stop' to stop.
blew> periph notify -f uint8 2A19 42
blew> periph stop
Stopped advertising.

exec モードでも同じ非ブロッキング動作が適用されるため、次のようなスクリプトを実行できます。

% blew exec "periph adv -n 'My Device' --config device.json; periph set 2A19 ff; periph notify 2A19 ff"

MCP サーバーモード

blew mcp は、stdio 経由で Model Context Protocol サーバーを起動し、すべての BLE 操作を個別の MCP ツールとして公開します。AI エージェント(Cursor、Claude Desktop など)は、これらのツールを検出して呼び出し、BLE デバイスをプログラムから操作できます。

サーバーは状態を保持します。REPL と同様に、BLE 接続とペリフェラルの状態はツール呼び出しの間も維持されます。ツールの結果は構造化 JSON(structuredContent)として返されるため、エージェントはスキャン結果、GATT データ、読み取り値などに型付きでアクセスできます。

エージェントの設定

Cursor(.cursor/mcp.json):

{
  "mcpServers": {
    "blew": {
      "command": "/path/to/blew",
      "args": ["mcp"]
    }
  }
}

Claude Desktop(claude_desktop_config.json):

{
  "mcpServers": {
    "blew": {
      "command": "/path/to/blew",
      "args": ["mcp"]
    }
  }
}
利用可能なツール
ツール説明
ble_scan近くの BLE デバイスをスキャンします
ble_connectID、名前、またはフィルターでデバイスに接続します
ble_disconnect現在のデバイスから切断します
ble_status接続状態を表示します
ble_gatt_servicesGATT サービスの一覧を表示します
ble_gatt_treeオプションの値を含む GATT ツリー全体を表示します
ble_gatt_charsサービスの特性を一覧表示します
ble_gatt_descriptors特性のディスクリプターを一覧表示します
ble_gatt_infoBluetooth SIG の特性仕様を検索します(デバイスは不要です)
ble_read特性値を読み取ります
ble_write特性に書き込みます
ble_subscribe通知を収集します(バッチで返します)
ble_periph_advertiseペリフェラルとしてアドバタイズを開始します
ble_periph_clone実機の GATT 構造をクローンします
ble_periph_stopアドバタイズを停止します
ble_periph_setペリフェラルの特性値を更新します
ble_periph_notify値を更新し、サブスクライバーに通知します
ble_periph_statusペリフェラルの状態を表示します

リモートデバイスとやり取りするすべてのツールは、オプションのターゲット指定パラメーター(name、device_id、service、manufacturer、rssi_min、pick)を受け付け、未接続の場合は自動的に接続します。

構造化出力の例

ble_scan の呼び出し結果:

{
  "type": "devices",
  "devices": [
    {
      "id": "A1B2C3D4-...",
      "name": "Heart Rate Sensor",
      "rssi": -55,
      "serviceUUIDs": ["180D"],
      "serviceDisplayNames": ["Heart Rate"]
    }
  ]
}

ble_read の呼び出し結果:

{
  "type": "readValue",
  "readValue": {
    "char": "2A19",
    "name": "Battery Level",
    "value": "85",
    "format": "uint8"
  }
}

レシピ

デバイスが密集した環境でデバイスを見つける
% blew scan -n "Sensor" -S 180F -R -65 -t 10
センサーデータをファイルに記録する
% blew -o kv sub -n "Sensor" -f uint16le -d 300 fff1 >> sensor.log
1 行で GATT をすばやく監査する
% blew gatt tree -n "Thingy" -d
デバイス情報の特性とその値を確認する
% blew gatt chars -n "Thingy" -r 180A
スクリプトを使って読み取る
% value=$(blew read -n "Thingy" -f uint8 2A19)
% echo "Battery: ${value}%"
意図しない複数一致を防ぐために --pick only を使用する
% blew read -n "Thingy" -p only -f uint8 2A19
# Errors out if more than one "Thingy" is nearby

コメント

MCP は初めてですか?

プロトコルの概要から最初のサーバーの導入まで、よくある疑問にまとめて答えます。

AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。