Skip to main content

PiShock MCP server

Project description

rlaif

CI PyPI Downloads License: AGPL v3 Python 3.11+

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.

  1. rlaif doctor: Confirms PiShock credentials load and the collar is reachable.

  2. With allow_shock = false, please ask your agent to call rlaif_info and rlaif(intensity=1, duration_s=1). The first one should report device.online: true; the second should refuse with an allow_shock error.

  3. Set allow_shock = true in ~/.config/rlaif/config.toml, then run rlaif live-smoke. It fires a real minimum-intensity shock (1 at 1 second) to the collar, gated by a confirmation.

  4. Ask the agent to fire four consecutive rlaif(intensity=1, duration_s=1) calls. You should confirm the fourth is refused with rate_limited: true. Wait for the cooldown, then confirm the next call succeeds.

  5. Only then you should raise max_intensity, max_duration_s, bucket_capacity, or refill_seconds for normal use. If you want to set max_intensity > 25 or bucket_capacity > 3 you have to enable i_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 > 25
  • bucket_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:

  1. Ctrl-C or kill the MCP server process.
  2. Pause the device on pishock.com.
  3. 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 == false but 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 doctor will display these issues if they are present.

  • 403 from upstream. Your api_key or username is wrong. The error message should mention NotAuthorizedError.

  • rlaif refuses every call with device_offline. The PiShock API returned DeviceNotConnectedError. 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 UnknownError with a message about throttling, that's from PiShock and rlaif can do nothing about it.

  • Safety gate fires at startup. If max_intensity > 25 or bucket_capacity > 3 and i_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 set XDG_CONFIG_HOME, rlaif uses that instead. rlaif init writes to whichever path resolves, so please run it rather than creating the file by hand.

  • Config file permissions. rlaif init writes the config with mode 0600 so 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, and RLAIF_SHARECODE take 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

rlaif_mcp-1.0.3.tar.gz (52.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

rlaif_mcp-1.0.3-py3-none-any.whl (44.4 kB view details)

Uploaded Python 3

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

Hashes for rlaif_mcp-1.0.3.tar.gz
Algorithm Hash digest
SHA256 b737dad74d763d883486a165e232b14d9f7aef4e044c1613ec24d03ee0a32b08
MD5 799ff1c85ba76be091e5bf29913ce545
BLAKE2b-256 a56c2c513b0643e09ee63fe9836dc50a77f29cc6fe007255819e8c76286a0958

See more details on using hashes here.

Provenance

The following attestation bundles were made for rlaif_mcp-1.0.3.tar.gz:

Publisher: release.yml on a9lim/rlaif

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for rlaif_mcp-1.0.3-py3-none-any.whl
Algorithm Hash digest
SHA256 ea96c34cd5f08b82869810a05b029bbaf32092c3afc1ca57fa1a09a66f136508
MD5 652fd0a9ace94c048950ca56e71cf250
BLAKE2b-256 aad48713f30162979dc6cadc7d17c87efb5572f7d96f222d791a8eef1248de7f

See more details on using hashes here.

Provenance

The following attestation bundles were made for rlaif_mcp-1.0.3-py3-none-any.whl:

Publisher: release.yml on a9lim/rlaif

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page