hapbeat-helper
Local daemon that bridges Hapbeat Studio (Web SPA at https://devtools.hapbeat.com)
to Hapbeat hardware on the local network.
The browser cannot do mDNS, UDP broadcast, or raw TCP sockets directly.
hapbeat-helper runs in the background, exposes a WebSocket on
ws://localhost:7703, and relays Studio requests to the devices using
UDP (port 7700) and TCP (port 7701).
Studio (https://devtools.hapbeat.com)
│ ws://localhost:7703 (JSON)
▼
hapbeat-helper (this daemon)
│ UDP 7700 (PLAY / STOP / PING / streaming)
│ TCP 7701 (config / kit deploy)
│ mDNS (_hapbeat._udp.local.)
▼
Hapbeat devices
Install
hapbeat-helper is distributed as a Python CLI that runs in its own
isolated environment. The recommended installer is pipx — it puts
each Python tool in a separate venv and exposes the entry point on your
PATH, so you don't need to think about Python versions or dependency
conflicts.
Step 1 — Install pipx (once per machine)
macOS
brew install pipx
pipx ensurepath
Windows
py -m pip install --user pipx
py -m pipx ensurepath
# Close and reopen your terminal so the new PATH takes effect.
pipx --version # should print the version
Windows tip: if
pipxis still "not recognized" after reopening the shell, usepy -m pipx ...for everything below (it works identically). The barepipxcommand becomes available once%APPDATA%\Python\Python3xx\Scriptsis on yourPath.OneDrive / cloud-synced home directory: if your
C:\Users\<you>\is synced by OneDrive, pipx may fail withWinError 448 — untrusted mount point. Move pipx out of the synced tree by setting these environment variables (User scope) and reopening the shell:[Environment]::SetEnvironmentVariable('PIPX_HOME', 'C:\pipx\home', 'User') [Environment]::SetEnvironmentVariable('PIPX_BIN_DIR', 'C:\pipx\bin', 'User')
Step 2 — Install hapbeat-helper
Once pipx is on your PATH:
pipx install hapbeat-helper
That's it. hapbeat-helper will be available in any new terminal.
Local development (from a clone of this repo)
# editable install via pipx (changes in src/ are picked up live)
pipx install -e .
# or — preferred during active dev — a plain venv:
python -m venv .venv
# macOS:
.venv/bin/pip install -e ".[dev]"
.venv/bin/hapbeat-helper start
# Windows:
.venv\Scripts\pip install -e ".[dev]"
.venv\Scripts\hapbeat-helper start
Updating
pipx upgrade hapbeat-helper
If you installed editable from a clone (pipx install -e . or
pip install -e ".[dev]"), updates are automatic — just git pull and
restart the daemon. The Python package picks up changes in src/ on
the next process start.
WS protocol mismatch? When Studio reports
ERROR: unknown type: <message>in the log drawer, your helper is older than the Studio build.git pull && restart(orpipx upgrade).
Update notices
When a newer release exists on PyPI, hapbeat-helper start prints a single
line about it, and hapbeat-helper version always reports it. It is one
scrolling line with nothing to dismiss, so it simply prints on every start
rather than being suppressed after the first sighting.
The lookup reads a static release feed
(https://devtools.hapbeat.com/releases.json), times out after 3 seconds and
stays completely silent on failure, so it is a no-op on isolated networks.
Disable it with --no-update-check or HAPBEAT_NO_UPDATE_CHECK=1.
Uninstalling
pipx uninstall hapbeat-helper
Run
Option A — Auto-start service (recommended)
Register hapbeat-helper as an OS-level service so it starts automatically every time you log in. After this one-time setup you never need to open a terminal again:
hapbeat-helper install-service
To check the registration state:
hapbeat-helper service-status
To remove the auto-start registration:
hapbeat-helper uninstall-service
Platform notes:
- macOS — creates
~/Library/LaunchAgents/com.hapbeat.helper.plist(launchd) - Windows — drops a hidden VBS shim into the Startup folder (
%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\hapbeat-helper.vbs) that launches the daemon at login with no console window. stdout/stderr →%LOCALAPPDATA%\hapbeat-helper\hapbeat-helper.log
Option B — Foreground (dev / debug)
hapbeat-helper start
Then open https://devtools.hapbeat.com — Studio will connect automatically.
Press Ctrl+C to stop.
Other commands
hapbeat-helper status # check whether a daemon is reachable on 7703
hapbeat-helper version # print version (and any newer release)
hapbeat-helper config show # show config path
Firmware OTA from the CLI
Push a firmware app image to one device over Wi-Fi, without opening Studio:
hapbeat-helper ota 192.168.0.48 dist/necklace_v3/firmware_app_ota.bin
hapbeat-helper ota duo-01 dist/necklace_v3/firmware_app_ota.bin # by device name
The target is an IP address or a device name; names are resolved through the
running daemon, so an IP is required when no daemon is up. Pass the app-only
image (firmware_app_ota.bin) — a merged serial image is rejected before
anything is sent. Exit code is 0 on success, 1 on OTA failure, 2 on a bad
argument or an unresolvable target.
AI agent integration (MCP)
hapbeat-helper mcp is a local MCP server
(stdio) that lets an AI agent on your PC — Claude Code, Codex — drive Studio's
AI trials directly: read the guide and knowledge base, submit a trial, open it
for you to try, and wait for your rating. It is a thin relay: every tool call
goes through the running daemon to the Studio tab, and Studio does the work
(validation, rendering, knowledge-base writes). Only you rate candidates; the
agent never writes ratings. File-based exchange (inbox/) keeps working.
Claude Code / Codex ──stdio──▶ hapbeat-helper mcp
│ ws://127.0.0.1:7703
▼
hapbeat-helper daemon ──relay──▶ Studio tab (Waveform editor, folder open)
Install
The MCP SDK is an optional extra:
pipx install --force "hapbeat-helper[mcp]"
# from a clone of this repo:
pipx install --force --editable ".[mcp]"
Without the extra, hapbeat-helper mcp prints these commands and exits with
code 2.
Register with your agent
Claude Code:
claude mcp add hapbeat -s user -- hapbeat-helper mcp
Codex — add to ~/.codex/config.toml:
[mcp_servers.hapbeat]
command = "hapbeat-helper"
args = ["mcp"]
Add --port N to the args if your daemon is not on 7703.
Prerequisites while using it
- The daemon is running (
hapbeat-helper startorinstall-service). - Hapbeat Studio is open with the Waveform editor showing a folder. That tab registers itself as the agent endpoint; if several tabs qualify, the one that registered last receives the requests.
Otherwise tools fail with HELPER_NOT_RUNNING: ... (no daemon) or
STUDIO_NOT_READY: ... (no Studio tab ready). Requests time out after 30 s
(submit_trial: 120 s) with TIMEOUT: ....
Tools
| Tool | Arguments | Purpose |
|---|---|---|
status |
— | Studio version, open folder, clip / trial / unrated counts, dimensions |
get_guide |
— | Agent guide (read first): trial format and workflow |
get_catalog |
— | Existing clips in the folder |
get_knowledge |
term? |
Term index + dimensions + insights, or one term's document |
submit_trial |
trial (hapbeat-trial@1 object) |
Validate, render and store a trial; returns trialId and per-candidate features |
get_trial |
trialId |
Request, candidates and rating (null while unrated) |
list_trials |
limit? (1–100, default 20), unratedOnly? |
Recent trials |
audition |
trialId, candidateId, play? |
Open the trial in the editor's AI-trials tab; play: true also starts playback |
adopt |
trialId, candidateId |
Adopt a candidate as a clip (same as the Adopt button) |
propose_insight |
statement (≤ 1000 chars), evidence (1–20 × trialId/candidateId) |
Append to the Proposed section of insights.md |
wait_for_rating |
trialId, timeoutSec? (10–1800, default 300) |
Poll every 2 s; {status: "rated", rating} or {status: "waiting"} (call again) |
Relay messages
Same {"type": ..., "payload": {...}} envelope as the rest of the WS API.
These messages are routed only between the MCP client and the endpoint tab —
never broadcast, never passed to the device handlers.
| type | Direction | payload | Helper behaviour |
|---|---|---|---|
agent_endpoint_register |
Studio → helper | { studioVersion, folderName } |
Make the sender the agent endpoint (last wins); reply agent_endpoint_registered {} |
agent_endpoint_unregister |
Studio → helper | {} |
Clear the endpoint if the sender is it |
agent_request |
MCP → helper → Studio | { requestId, method, params } |
Forward unchanged to the endpoint and remember the requester. No endpoint → agent_response { requestId, ok: false, error: "STUDIO_NOT_READY: ..." } |
agent_response |
Studio → helper → MCP | { requestId, ok, result?, error? } |
Forward to the requester and forget the request. Accepted only from the tab the request went to; dropped if the requester is gone |
- Endpoint disconnects → every request still pending on it is answered with
ok: false, error: "STUDIO_DISCONNECTED". Requester disconnects → its pending requests are dropped. requestIdmust match^[A-Za-z0-9_-]{1,64}$(otherwise anerrormessage is returned and nothing is forwarded). A duplicate pending id is answered withDUPLICATE_REQUEST_ID; a payload over 4 MB (JSON, UTF-8) withPAYLOAD_TOO_LARGE.
素材台帳(materials)
Free sound-effect sites get downloaded from in bulk, and a month later nobody
remembers where a file came from. The material ledger records it at download
time — site, source page, license — and ties it to the SHA-256 of the file
content, so renaming or moving a file never loses it. Studio registers the
files it exports from those originals, and a Kit's CREDITS.md is generated
from the ledger.
Commands
hapbeat-helper materials ingest --dry-run # what would be taken from ~/Downloads
hapbeat-helper materials ingest # copy new audio / zip downloads into the ledger
hapbeat-helper materials ingest --dir D:\sfx --since 90
hapbeat-helper materials list --needs-review # materials whose license is unknown
hapbeat-helper materials list --site maou.audio
hapbeat-helper materials show path\to\gunshot.wav # or a sha256
hapbeat-helper materials set-license --site otologic.jp --license-id CC-BY-4.0 \
--name "OtoLogic (CC BY 4.0)" --url https://otologic.jp/free/license.html \
--credit OtoLogic --verified
hapbeat-helper materials set-license --sha <sha256> --license-id CC0-1.0
hapbeat-helper materials credits MyKit/ # CREDITS.md to stdout
hapbeat-helper materials where # print the materials dir
ingestscans the top level of the folder (default~/Downloads) for.wav .mp3 .ogg .flac .m4a .aif .aiff .opusand.zip(audio members are extracted). Files are copied; the downloads stay where they are. Content already in the ledger is reported as a duplicate and skipped.--since DAYSdefaults to "since the last ingest" (30 days on the first run).- The source is read from the Windows download mark (
Zone.Identifier:ReferrerUrl/HostUrl). Without it (other OS, file not from a browser) the site is_unknown. set-license --siteeditssites.json;--shaappends a per-file override.--attribution / --no-attributiondefaults to yes forCC-BY*ids.
Materials dir
Default ~/HapbeatMaterials/; change it with materials_dir in the config
file (hapbeat-helper config show prints its path):
materials_dir = "D:/HapbeatMaterials"
materials_watch_downloads = false # true: the daemon ingests new downloads by itself
HapbeatMaterials/
store/<site>/<YYYY-MM>/<name> read-only copy of each original (name collisions get " (2)")
ledger.jsonl append-only records: hapbeat-material@1 / hapbeat-derived@1 / hapbeat-license-override@1
sites.json domain → license rules (yours to edit; seeded on first use)
MATERIALS.md overview by site and month, regenerated on every change
state.json internal (last ingest time)
Licenses are resolved on every lookup — per-file override, then the site rule
in sites.json (a perAsset site such as freesound.org resolves to unknown
and needs review), then unknown — so fixing a rule later applies to every
material already ingested. CREDITS.md in a Kit is described in
hapbeat-contracts specs/kit-format.md.
With materials_watch_downloads = true the daemon polls ~/Downloads every
10 s and ingests a new file once its size is unchanged across two polls
(download finished). Off by default.
WS messages
Answered to the sender only; requestId in the payload is echoed back when
present. Invalid payloads get the same reply type with an error string.
| type | payload | reply |
|---|---|---|
material_lookup |
{ requestId?, sha256s: string[] (≤ 500) } |
material_lookup_result { requestId?, results: { <sha>: { kind: "material" | "derived" | "unknown", originals: [{ sha256, site, referrerUrl, hostUrl, originalName, storePath, license, needsReview }] } } } |
material_register_derived |
{ requestId?, sha256, parents: string[] (1–64), tool, name?, note? } |
material_register_derived_result { requestId?, ok } — an identical sha256 + parents record is not appended twice |
material_credits |
{ requestId?, sha256s: string[] (≤ 500), toolName } |
material_credits_result { requestId?, markdown } |
A derived file resolves to the originals reached through its parents (cycles and chains deeper than 32 are cut).
Verify
Quick smoke test using websocat:
echo '{"type":"ping","payload":{}}' | websocat ws://localhost:7703
echo '{"type":"list_devices","payload":{}}' | websocat ws://localhost:7703
Troubleshooting
-
Studio reports "Helper 未接続" — run
hapbeat-helper install-service(once) or start manually withhapbeat-helper start. -
Browser cannot connect to
ws://localhost:7703from HTTPS Studio — Chrome and Edge allow this by default. Firefox requiresnetwork.websocket.allowInsecureFromHTTPS = trueinabout:config. -
No devices found — confirm the Hapbeat devices and this PC are on the same Wi-Fi network. Some hotspot/AP modes block UDP broadcast and mDNS.
-
Port 7700 / 7703 already in use — stop any running
hapbeat-manager(it owns the same ports). The two cannot run at the same time. -
Windows:
pipx installfails withWinError 448 — untrusted mount point— your home directory is under OneDrive (or another reparse point). pipx finished installing the package but cannot finalize the shim under~/.local/bin/. Either runhapbeat-helper.exefrom that path directly, or relocate pipx outside the synced tree:[Environment]::SetEnvironmentVariable('PIPX_HOME', 'C:\pipx\home', 'User') [Environment]::SetEnvironmentVariable('PIPX_BIN_DIR', 'C:\pipx\bin', 'User') # Reopen the shell, then: py -m pipx ensurepath py -m pipx install hapbeat-helper
-
pipxnot recognized afterpip install --user pipx— the user Scripts dir is not onPathyet. Runpy -m pipx ensurepathand open a new terminal. As a fallback, everypipx Xcall also works aspy -m pipx X.
ドキュメント
公式ドキュメントは https://devtools.hapbeat.com/docs/helper/ を参照してください。 インストール手順 / CLI リファレンス / セキュリティ解説などをまとめています。
License
MIT
Metadata
Release files for hapbeat-helper 0.5.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| hapbeat_helper-0.5.0.tar.gz | 138.9 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| hapbeat_helper-0.5.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 253.2 kB
Release files / hapbeat_helper-0.5.0.tar.gz
| Download URL | hapbeat_helper-0.5.0.tar.gz |
|---|---|
| Size | 138.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
662bee2b59db1e8ca7a4393a50e16c2853a5e840e32565f59ee47c99014f0421
|
|
BLAKE2b-256 checksum How to use checksums |
71c2bd7711358a803ff14a790c6d30da5283e23c4b1e1a010a52d267f20f3a10
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 7, 2026.
Transparency logRelease files / hapbeat_helper-0.5.0-py3-none-any.whl
| Download URL | hapbeat_helper-0.5.0-py3-none-any.whl |
|---|---|
| Size | 114.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
f4c98122804dd7c01c8d89074d7aa32b2ef3db8c27c2a09cd041bcb5341f54c8
|
|
BLAKE2b-256 checksum How to use checksums |
90ef4117b3b77c1eb11444a418f8674fce98a220e653bf887644e852c829477e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 7, 2026.
Transparency logSecurity for hapbeat-helper 0.5.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for hapbeat_helper-0.5.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 7, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.