PiShock MCP server
Project description
rlaif
This is a single-user MCP server that provides a shock tool.
There are three tools:
| Tool | Function |
|---|---|
rlaif_info |
Read-only device and server state |
rlaif_log |
Read-only log |
rlaif |
Fire a shock |
There is no tool to change the config, it is set at launch. There is also no tool for beep or vibrate. The only purpose of this project is for your agent to be able to zap you.
Install
The PyPI distribution name is rlaif-mcp (the bare rlaif name is taken; transfer pending). The import and CLI command are still just rlaif.
uv tool install rlaif-mcp
rlaif init # interactive: credentials, config, doctor, MCP snippet
rlaif init will ask you for your credentials; get them from pishock.com/#/account. It then writes ~/.config/rlaif/config.toml with allow_shock = false, runs rlaif doctor to probe the device, and offers to emit an MCP client snippet for you. It does not fire the device on startup.
From a source checkout:
git clone <repo> rlaif
cd rlaif
uv sync # creates .venv, installs deps
uv run rlaif init # same wizard, running from the checkout
Connect MCP client
There are two ways to register rlaif: auto-install (7 clients with dedicated config files) or copy-paste snippet (all 10 clients).
Auto-install
rlaif install claude-desktop # JSON: ~/Library/.../claude_desktop_config.json
rlaif install claude-code # JSON: ~/.claude.json
rlaif install cursor # JSON: ~/.cursor/mcp.json
rlaif install windsurf # JSON: ~/.codeium/windsurf/mcp_config.json
rlaif install antigravity # JSON: ~/.gemini/antigravity/mcp_config.json
rlaif install codex # TOML: ~/.codex/config.toml (tomlkit round-trip)
rlaif install hermes # YAML: ~/.hermes/config.yaml (ruamel.yaml round-trip)
Install atomically merges in an rlaif entry. A single <file>.rlaif.bak is kept.
If a different rlaif entry already exists, install refuses unless you pass
--force. Pass --dry-run to preview without writing. rlaif uninstall <client> removes the entry the same way.
Snippet (paste manually)
The remaining 3 clients use JSONC (opencode, vscode) or share a config file
with unrelated user state (zed). For those, use snippet:
rlaif snippet claude-desktop # JSON for ~/Library/.../claude_desktop_config.json
rlaif snippet claude-code # JSON for ~/.claude.json or project .claude.json
rlaif snippet codex # TOML for ~/.codex/config.toml
rlaif snippet hermes # YAML for ~/.hermes/config.yaml
rlaif snippet antigravity # JSON for ~/.gemini/antigravity/mcp_config.json
rlaif snippet opencode # JSON for opencode.json or ~/.config/opencode/opencode.json
rlaif snippet cursor # JSON for ~/.cursor/mcp.json or .cursor/mcp.json
rlaif snippet windsurf # JSON for ~/.codeium/windsurf/mcp_config.json
rlaif snippet vscode # JSON for .vscode/mcp.json or user mcp.json
rlaif snippet zed # JSON fragment for ~/.config/zed/settings.json
After uv tool install rlaif-mcp the snippet is a one-liner: "command": "rlaif", "args": ["serve"]. For dev mode, please pass --dev-path /absolute/path/to/rlaif to get a uv run --directory … variant. The same flag works on install.
Before use
Please do these in order, this is for safety.
-
rlaif doctor: Confirms PiShock credentials load and the collar is reachable. -
With
allow_shock = false, please ask your agent to callrlaif_infoandrlaif(intensity=1, duration_s=1). The first one should reportdevice.online: true; the second should refuse with anallow_shockerror. -
Set
allow_shock = truein~/.config/rlaif/config.toml, then runrlaif live-smoke. It fires a real minimum-intensity shock (1 at 1 second) to the collar, gated by a confirmation. -
Ask the agent to fire four consecutive
rlaif(intensity=1, duration_s=1)calls. You should confirm the fourth is refused withrate_limited: true. Wait for the cooldown, then confirm the next call succeeds. -
Only then you should raise
max_intensity,max_duration_s,bucket_capacity, orrefill_secondsfor normal use. If you want to setmax_intensity > 25orbucket_capacity > 3you have to enablei_understand_and_consent = true.
Configure
rlaif init writes a default config:
[auth]
username = "..." # your pishock.com username
api_key = "..." # from https://pishock.com/#/account
sharecode = "..." # per-device share code
[device]
label = "left-thigh" # free-form, appears in the ops log only
[safety]
allow_shock = false # leave false for the first-run check
max_intensity = 25 # code ceiling 50 (gated by consent flag)
max_duration_s = 2 # code ceiling 5
warn_threshold_intensity = 15 # surfaces `high_intensity: true`
i_understand_and_consent = false # required to raise caps past defaults
[rate_limit]
bucket_capacity = 3 # code ceiling 10 (gated by consent flag)
refill_seconds = 600 # code floor 60
You can also override the secrets via environment variables: RLAIF_USERNAME, RLAIF_API_KEY, and RLAIF_SHARECODE.
Safety gate
Rlaif refuses to start if either of these is set without i_understand_and_consent = true in the config:
max_intensity > 25bucket_capacity > 3
The defaults are meant to stay conservative. The flag makes sure that raising them is an explicit step. Please read this section before you flip it.
| Setting | Default | Gated | Code ceiling |
|---|---|---|---|
max_intensity |
25 | yes (above 25) | 50 |
max_duration_s |
2 | no | 5 |
bucket_capacity |
3 | yes (above 3) | 10 |
refill_seconds |
600 | no | 60 (floor, not ceiling) |
The code ceilings apply no matter what the config says. You cannot raise max_intensity above 50 or bucket_capacity above 10 by editing the config, and refill_seconds cannot go below 60; the server will refuse to start.
At default settings the worst case is a burst of 3 shocks at intensity 25 for 2 seconds each, with a 10-minute cooldown per additional shock after the bucket empties. At fully raised settings the worst case is 10 shocks at intensity 50 for 5 seconds each, with a 1-minute cooldown per additional shock. Please keep the caps at what you are comfortable.
CLI
rlaif init interactive first-run setup (writes config, runs doctor, offers snippet)
rlaif doctor read-only health check (config and device probe)
rlaif snippet X emit MCP client config snippet (X is claude-desktop, claude-code, codex, hermes, antigravity, opencode, cursor, windsurf, vscode, or zed)
rlaif install X auto-write rlaif into a supported MCP client config (X is claude-desktop, claude-code, cursor, windsurf, antigravity, codex, or hermes)
rlaif uninstall X remove rlaif from one of the same seven supported configs
rlaif serve start the MCP server over stdio
rlaif log tail the on-disk ops log (default: last 10 entries, --tail N to change)
rlaif dry-run exercise every tool against a mock device; nonzero on violation
rlaif live-smoke fire one real minimum-intensity shock (interactive confirm)
python -m rlaif <subcommand> does the same thing.
Safety
There is no built in stop button, so an agent may shock you too much. If you need to stop it immediately:
- Ctrl-C or kill the MCP server process.
- Pause the device on pishock.com.
- Unplug the device.
Restarting the server clears the cooldowns. I would strongly recommend against deliberately restarting the server to skip the cooldown.
Troubleshooting
-
rlaif_info.device.online == falsebut the device is on. Please check these potential issues: (a) your share code is correct, (b) the device is online at pishock.com, and (c) the device is not paused there.rlaif doctorwill display these issues if they are present. -
403 from upstream. Your
api_keyorusernameis wrong. The error message should mentionNotAuthorizedError. -
rlaifrefuses every call withdevice_offline. The PiShock API returnedDeviceNotConnectedError. Info calls can succeed when the physical device isn't online, because.info()returns server-side metadata. Please wait for the device to reconnect, or pause and unpause it on pishock.com. -
Upstream rate limit (separate from rlaif's bucket). PiShock itself rate-limits API traffic. If you see
UnknownErrorwith a message about throttling, that's from PiShock and rlaif can do nothing about it. -
Safety gate fires at startup. If
max_intensity > 25orbucket_capacity > 3andi_understand_and_consent = false, the server refuses to start. This is intentional. Please reduce the caps or enable the consent flag. -
Config path on Windows. The default is
%USERPROFILE%\.config\rlaif\config.toml, not%APPDATA%. If you setXDG_CONFIG_HOME, rlaif uses that instead.rlaif initwrites to whichever path resolves, so please run it rather than creating the file by hand. -
Config file permissions.
rlaif initwrites the config with mode0600so other users on the same machine cannot read your PiShock API key. Rlaif does not re-check permissions on load, so if you edit the file and widen the mode, please chmod it back:chmod 600 ~/.config/rlaif/config.toml. -
Env vars override the config file.
RLAIF_USERNAME,RLAIF_API_KEY, andRLAIF_SHARECODEtake precedence over the values in[auth]. If the credentials in the config file look right but rlaif seems to be using different ones, please check whether one of these env vars is set in your shell or in your MCP client's launch environment.
Architecture
src/rlaif/
safety.py # pure Python core: caps, token bucket, ops log, safety gate
config.py # TOML loader, env overrides, validation
server.py # FastMCP wiring (thin) + on-disk ops log sink
cli.py # `rlaif` entry point and subcommand dispatcher
init.py # `rlaif init`
doctor.py # `rlaif doctor`
snippet.py # `rlaif snippet`
log.py # `rlaif log`
dry_run.py # `rlaif dry-run`
live_smoke.py # `rlaif live-smoke`
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file rlaif_mcp-1.0.3.tar.gz.
File metadata
- Download URL: rlaif_mcp-1.0.3.tar.gz
- Upload date:
- Size: 52.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b737dad74d763d883486a165e232b14d9f7aef4e044c1613ec24d03ee0a32b08
|
|
| MD5 |
799ff1c85ba76be091e5bf29913ce545
|
|
| BLAKE2b-256 |
a56c2c513b0643e09ee63fe9836dc50a77f29cc6fe007255819e8c76286a0958
|
Provenance
The following attestation bundles were made for rlaif_mcp-1.0.3.tar.gz:
Publisher:
release.yml on a9lim/rlaif
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rlaif_mcp-1.0.3.tar.gz -
Subject digest:
b737dad74d763d883486a165e232b14d9f7aef4e044c1613ec24d03ee0a32b08 - Sigstore transparency entry: 1404019268
- Sigstore integration time:
-
Permalink:
a9lim/rlaif@6a4328dca1c758b657a1cd980e261e61d9bfdb48 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/a9lim
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@6a4328dca1c758b657a1cd980e261e61d9bfdb48 -
Trigger Event:
push
-
Statement type:
File details
Details for the file rlaif_mcp-1.0.3-py3-none-any.whl.
File metadata
- Download URL: rlaif_mcp-1.0.3-py3-none-any.whl
- Upload date:
- Size: 44.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ea96c34cd5f08b82869810a05b029bbaf32092c3afc1ca57fa1a09a66f136508
|
|
| MD5 |
652fd0a9ace94c048950ca56e71cf250
|
|
| BLAKE2b-256 |
aad48713f30162979dc6cadc7d17c87efb5572f7d96f222d791a8eef1248de7f
|
Provenance
The following attestation bundles were made for rlaif_mcp-1.0.3-py3-none-any.whl:
Publisher:
release.yml on a9lim/rlaif
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rlaif_mcp-1.0.3-py3-none-any.whl -
Subject digest:
ea96c34cd5f08b82869810a05b029bbaf32092c3afc1ca57fa1a09a66f136508 - Sigstore transparency entry: 1404019361
- Sigstore integration time:
-
Permalink:
a9lim/rlaif@6a4328dca1c758b657a1cd980e261e61d9bfdb48 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/a9lim
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@6a4328dca1c758b657a1cd980e261e61d9bfdb48 -
Trigger Event:
push
-
Statement type: