docs: OAuth connector setup, governor config, feature_index; add changelog

- claude.ai connector section now documents the OAuth login flow as the
  primary path, with the authless single-user fallback as Option C.
- Project structure, requirements, and .env docs updated for the OAuth +
  governor modules; pointer to .env.example for the governor knobs.
- Tools table covers the extended output types, the escalate hold
  contract, and feature_index for multi-motor devices.
- Safety Features section documents the governor.
- CHANGELOG.md records v1.1 and the v1.0 baseline.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Aletheia
2026-07-07 20:20:57 +02:00
parent 7b69479e80
commit 9dafd12104
2 changed files with 107 additions and 11 deletions

77
CHANGELOG.md Normal file
View File

@@ -0,0 +1,77 @@
# Changelog
## v1.1 — 2026-07-07
This release brings the remote server up to date with everything that
shipped in the [Android edition](https://github.com/AletheiaVox/signal_bridge_android)
since March, plus fixes found while porting. If you run a server from an
older clone, rebuild the Docker image (`docker-compose build --no-cache`)
and update your relay client — both sides changed.
### Added
- **OAuth 2.0 support.** Full flow for claude.ai custom connectors:
discovery metadata, dynamic client registration, a login page at
`/oauth/authorize`, and a token endpoint. Each user signs in with their
own account — no more sharing one token or relying on the single-phone
fallback. The fallback still works for single-user servers and can be
disabled with `SB_REQUIRE_MCP_AUTH=true`.
- **Safety governor.** Server-side heat model (intensity × time) that
forces a cooldown when a session runs hot for too long. Server defaults
via `SB_GOVERNOR_*` env vars, per-user overrides via
`GET`/`POST /safety/config`, current state surfaced in `list_devices`
and piggybacked on heartbeat pings.
- **`feature_index` on every output and pattern tool.** Multi-motor
devices (Lovense Edge, Dolce, …) can now have each motor driven
independently. Implemented in both relay clients; a bad index returns a
helpful error listing the valid ones.
- **`CHANGELOG.md`, `.env.example`, `.gitignore`, `.gitattributes`.**
### Changed
- **Neutral engineering terminology** in tool schemas and
`devices.json`, matching the Android edition. Content filters on some
LLM platforms refused tools whose schemas contained explicit anatomical
language; the reworded schemas work across providers. `list_devices`
now also shows what each output channel does.
- Duplicate same-model devices get suffixed short names (`lush`,
`lush_2`) instead of silently replacing each other.
- Relay clients no longer push an unsolicited device list after
authenticating; the server requests a scan on connect and the scan
response covers it.
### Fixed
- **MCP protocol compliance:** JSON-RPC notifications (e.g.
`notifications/initialized`) now get the bare `202` the spec requires
instead of a malformed error, and `initialize` echoes the client's
`protocolVersion` when supported. Fixes connection failures with strict
clients.
- **OAuth token endpoint** 500'd when a client POSTed the token request
as `multipart/form-data` (missing `python-multipart` dependency).
- **`governor_enabled`** now round-trips as a real JSON boolean; strict
clients (the Android app) previously couldn't re-enable the governor
after disabling it.
- **Patterns with `duration=0` silently did nothing.** They now run
until an explicit stop, matching plain commands.
- **`escalate` hold contract:** `hold_seconds` ≤ 0 holds at peak until
stopped, > 0 auto-stops after the hold — and an error mid-ramp can no
longer leave the device running at the last intensity it reached.
- **Pattern collisions:** starting a pattern cancels the previous
pattern on that device (they used to fight over the actuator), and a
direct command now supersedes a running pattern instead of being
overwritten by it.
- **Stale auto-stops:** a `duration` auto-stop from an earlier command
could fire minutes later and silently kill output a newer command had
started. Auto-stops are now tracked per channel and cancelled when
something newer takes over.
- Pattern timing now uses a monotonic clock; an NTP wall-clock jump
could stretch, truncate, or instantly end a pattern.
- Repo hygiene: removed accidentally committed compiled bytecode
(`__pycache__`) and a stray deploy tarball.
## v1.0 — 2026-03-14
Initial public release: FastAPI relay server (MCP over HTTPS, JWT auth,
rate limiting, dead man's switch), Windows/desktop relay client, and the
Termux relay for Android.

View File

@@ -90,15 +90,19 @@ On your local machine, create the project directory and prepare files. The proje
signal-bridge-remote/
├── Dockerfile
├── docker-compose.yml
├── .env
├── .env (copy .env.example and fill in)
├── requirements-server.txt
├── requirements-phone.txt
├── server/
│ ├── __init__.py
│ ├── app.py
│ ├── auth.py
│ ├── config.py
│ ├── governor.py
│ ├── models.py
│ ├── mcp_tools.py
│ ├── oauth.py
│ ├── oauth_routes.py
│ ├── relay_hub.py
│ ├── safety.py
│ └── session_registry.py
@@ -147,6 +151,7 @@ websockets>=12.0
bcrypt>=4.1.0
PyJWT>=2.8.0
python-dotenv>=1.0.0
python-multipart>=0.0.9
```
**.env:**
@@ -158,12 +163,20 @@ SB_HOST=0.0.0.0
SB_PORT=8420
SB_REGISTRATION_OPEN=true
SB_TOKEN_EXPIRY_HOURS=720
# true = every MCP request must be authenticated (multi-user servers).
# false = single-user convenience: unauthenticated MCP requests go to the
# sole connected phone.
SB_REQUIRE_MCP_AUTH=false
SB_HEARTBEAT_INTERVAL=2.0
SB_HEARTBEAT_TIMEOUT=6.0
SB_BAN_THRESHOLD=20
SB_BAN_DURATION_MINUTES=30
```
See [.env.example](.env.example) for the full list, including the safety
governor's tuning knobs (`SB_GOVERNOR_*` — heat/cooldown rates for the
session intensity limiter).
Upload to your VPS:
```bash
@@ -290,17 +303,21 @@ Add this MCP server config:
Restart Claude Desktop. You should see Signal Bridge in your available tools.
### Option B: claude.ai Custom Connector (authless)
### Option B: claude.ai Custom Connector (OAuth)
Claude.ai supports custom MCP connectors without authentication, using a fallback mechanism: when only one phone is connected, all MCP requests are routed to that phone automatically.
The server implements the full OAuth 2.0 flow that claude.ai custom connectors expect (discovery metadata, dynamic client registration, authorize + token endpoints). Each user logs in with their own Signal Bridge account, so this works properly on multi-user servers.
1. Go to claude.ai Settings (or click the connector icon in the chat)
2. Choose "Add custom connector" (or "Add MCP server")
3. Enter your server URL: `https://signal-bridge.duckdns.org/mcp`
4. Leave authentication as "None"
5. Save
4. Save — claude.ai will open your server's login page
5. Sign in with the username and password you registered in step 1.4
**Important**: The authless fallback only works when exactly one phone/relay client is connected to the server. If no phones are connected, claude.ai will show a connection error. Start your relay client first, then connect from claude.ai.
### Option C: claude.ai without login (single-user fallback)
If you skip the OAuth login, the server falls back to routing unauthenticated MCP requests to the sole connected phone — convenient for a private single-user server.
**Important**: The authless fallback only works when exactly one phone/relay client is connected to the server (and is disabled entirely when `SB_REQUIRE_MCP_AUTH=true`). If no phones are connected, claude.ai will show a connection error. Start your relay client first, then connect from claude.ai.
---
@@ -466,18 +483,19 @@ Once connected, Claude has access to these tools:
| Tool | Description |
|------|-------------|
| `list_devices` | Show connected devices and their capabilities |
| `list_devices` | Show connected devices, their output channels, and governor state |
| `scan_devices` | Rescan for new or reconnected Bluetooth devices |
| `vibrate` | Send vibration (intensity 0.0–1.0, optional duration in seconds) |
| `rotate` | Rotation or sonic output (device-dependent) |
| `oscillate` | Thrusting/oscillation output |
| `rotate` | Rotational/high-frequency actuator output (device-dependent) |
| `oscillate` | Linear reciprocating output |
| `constrict` / `temperature` / `led` / `position` / `spray` | Extended outputs for devices that support them |
| `pulse` | Rhythmic on/off pattern |
| `wave` | Smooth sine-wave intensity modulation |
| `escalate` | Gradual ramp from 0 to peak, with optional hold |
| `escalate` | Gradual ramp to peak; `hold_seconds` > 0 auto-stops after holding, 0 holds until stopped |
| `stop` | Immediately stop all output (also cancels patterns) |
| `read_battery` | Read device battery level |
All output tools accept `device` (name or "all"), `intensity` (0.0–1.0), and `duration` (seconds, 0 = until stopped). Pattern tools also accept `output_type` to modulate rotation or oscillation instead of vibration.
All output tools accept `device` (name or "all"), `intensity` (0.0–1.0), and `duration` (seconds, 0 = until stopped). Pattern tools also accept `output_type` to modulate rotation or oscillation instead of vibration. Multi-motor devices (e.g. Lovense Edge, Dolce) additionally take `feature_index` to drive one motor independently — omit it to drive all matching motors together.
---
@@ -486,6 +504,7 @@ All output tools accept `device` (name or "all"), `intensity` (0.0–1.0), and `
Signal Bridge has several safety mechanisms built in:
- **Dead Man's Switch**: The server pings the relay client every 2 seconds. If 3 pings go unanswered (6 seconds), the server sends an emergency stop to all devices and disconnects the session. Your devices will never be left running if the connection drops.
- **Safety Governor**: A server-side heat model (intensity × time) that forces a cooldown when a session runs too hot for too long. Tunable server-wide via `SB_GOVERNOR_*` env vars and per-user via `GET`/`POST /safety/config`; current heat is shown in `list_devices` and can be disabled per user.
- **Auto-stop on Duration**: Commands with a `duration` parameter automatically stop after the specified time.
- **Fallback Stop**: If a stop command references a device name that doesn't exist, ALL devices are stopped as a safety fallback.
- **Rate Limiting**: Prevents command flooding (120 commands/minute default).