otolawn-mcp
# otolawn-mcp
A Model Context Protocol (MCP) server for [OtO Lawn](https://otolawn.com) smart sprinkler
devices. Control your OtO zones from any MCP client (Claude, Cursor, etc.):
- List devices, zones, and schedules
- Start / stop watering on demand, with a water depth or runtime you choose
- Enable/disable zones, rename them, change their scheduled water amounts
- Browse irrigation event history (what ran, when, how much water, errors)
- Enable/disable watering schedules
- Read the weather data OtO uses for rain/wind skipping, device diagnostics, battery
history and notification messages
The protocol was reverse-engineered from the OtO mobile app (v4.0.8, Android build
`com.oto`). See [docs/API.md](docs/API.md) for the full API reference. This is an
independent personal-use interoperability project - it is not affiliated with or
endorsed by OtO Inc. Trademarks belong to their owners.
```text
This project talks to YOUR devices with YOUR credentials.
```
## How it works
```text
MCP client (Claude, etc.)
| (stdio JSON-RPC)
v
otolawn-mcp server ──► Firebase Auth (email/password sign-in) ─► ID token
| https://oto-cloud-service-ems-prod-*.run.app API
v (Bearer ID-token auth)
OtO sprinkler device(s), polled by OtO's cloud
```
- Authentication uses the same Firebase auth endpoint the app uses, with an embedded
app API key (public information, shipped inside the distributed app).
- The ID token is cached in memory and refreshed automatically (tokens last ~1 hour).
- Manual start/stop are executed via the same schedule-execution endpoints the app
uses; the physical device picks up the change within ~1-2 minutes.
## Install
Requires Python 3.11+.
```bash
git clone https://github.com/DmitriyAlergant/otolawn-mcp
cd otolawn-mcp
python -m venv .venv
uv pip install -e . # or: .venv/bin/pip install -e .
```
## Configure credentials
```bash
export OTO_EMAIL="you@example.com"
export OTO_PASSWORD="your-oto-app-password"
```
These are the same email/password you use in the OtO mobile app.
## Tool call contract
All tools return flat JSON blobs:
```json
{"ok": true , "...": "payload"}
{"ok": false, "error": "description on failure"}
```
Water depths are millimetres (`wateringQuantity` at the API level). Runtimes and
water depth are related by a per-zone calibration curve (`timeToWater_form`);
`water_zone` accepts either.
## Available tools
| Tool | Description |
|---|---|
| `list_devices` | Devices with name + winter mode |
| `get_device_status` | Live hardware status: battery mA/V, charge state, comm info |
| `get_battery_history` | Daily battery level history |
| `get_device_logs` | Device event log (watering starts/stops, errors) |
| `list_zones` / `get_zone` | Zones with water amount, enabled, days, rain/wind skips |
| `set_zone_enabled` | Enable / disable a zone |
| `rename_zone` | Rename a zone |
| `set_zone_watering` | Change a zone's scheduled water depth or runtime |
| `water_zone` | **Start watering now** (returns `scheduleId`) |
| `stop_watering` | Stop a manual run by `scheduleId` |
| `list_events` | Irrigation events (scheduled run instances) with statuses |
| `cancel_event` | Cancel an event, or un-cancel a rain-skipped one |
| `list_routines` / `set_routine_enabled` | Watering schedules (routines) |
| `get_weather` | Weather used by the rain/wind intelligence |
| `list_messages` | Account messages |
| `add_zone` | Create an unconfigured zone (path calibration still needs the app) |
| `delete_zone` | Delete a zone |
## Example
```text
You: "Water the Front Lawn Far zone about 3 mm, then tell me how much it ran."
Claude: calls list_zones() ─► finds zone "Front Lawn Far"
calls water_zone(device_id, zone_id, water_depth_mm=3)
calls list_events(only_active=true) ...
calls stop_watering(scheduleId) when done (or lets it finish)
```
## Registering with Claude Code / other clients
`claude mcp add otolawn -- <repo>/venv/bin/otolawn-mcp` (with creds in env), or
in `mcp.json`:
```json
{
"mcpServers": {
"otolawn": {
"command": "/path/to/otolawn-mcp/.venv/bin/otolawn-mcp",
"env": { "OTO_EMAIL": "you@example.com", "OTO_PASSWORD": "..." }
}
}
}
```
## Safety notes
- `water_zone` triggers real watering within a minute or so.
- A stopped `water_zone` run may still have briefly spawned a run - the device
applies commands on its next poll, so stopping immediately after starting is
also handled (event ends up `USER_STOPPED`).
- Rain/wind intelligence is not bypassed by manual starts; use large-enough depth
if you expect the forecast to interfere.
- Deleting zones or devices cannot easily be undone.
## Repo contents
- `src/otolawn_mcp/client.py` - OtO cloud API client (auth, zones, events, control)
- `src/otolawn_mcp/server.py` - MCP tool surface
- `docs/API.md` - the full reverse-engineered API reference
## Disclaimer
This is an unofficial, independent interoperability implementation, not affiliated
with OtO Inc. Endpoints may change without notice. Use at your own risk. Be mindful
of local water-use rules and of what you commit to a public repo :).
MIT licensed. See [LICENSE](LICENSE).
TDQS
Scored across 19 tools
Most tools target clearly distinct resources and actions (zones vs routines vs events vs devices). A few pairs could be momentarily confused—cancel_event vs stop_watering, and water_zone (manual) vs set_zone_watering (scheduled amount)—but the descriptions explicitly clarify the manual/scheduled distinction.
Every tool follows a consistent snake_case verb_noun pattern (list_zones, get_zone, set_zone_enabled, water_zone, cancel_event, etc.). No mixed conventions or vague verbs appear.
19 tools is on the heavier side but the domain is genuinely broad (zones, routines, events, devices, weather, messages), and each tool maps to a distinct operation. Slightly heavy but not bloated.
Zones have full lifecycle coverage (add/get/list/rename/enable/delete/set-watering) and devices/events are well covered. The main gap is routines: they can only be listed and enabled/disabled, with no create/edit, though zone-level scheduling partially compensates.