Skip to main content
Glama

iaqualink-mcp

MCP server for iAqualink (Zodiac / Jandy / Fluidra) pool and spa equipment. Read temperatures, set points, pumps, heaters, lights, aux circuits and salt chlorinator state, and switch equipment — every change confirmation-gated and verified by a re-read. It signs in with your own iAqualink account (the same email and password as the iAqualink app); no browser, extension or bridge.

Supports the two system types the reference client supports: iAqua (Jandy iAquaLink panels) and eXO (Zodiac salt chlorinators). Other device types on the account (robotic cleaners, …) are listed but not controlled.

Status: partly verified live. Request shapes come from iaqualink-py 0.7.0 (what Home Assistant uses) and a WebTouch capture (see docs/IAQUALINK-API.md).

  • Seen working on a real iAqua (RS) panel, 2026-10-11/12: sign-in, the system list (devices.json), the iAqua status reads (get_home, get_devices) and WebTouch (location lookup, init, button presses and the display stream).

  • Still derived, not yet exercised live: the iAqua set_* commands (pump, heater, aux, light, temperature), every eXO request (shadow read and writes), iaqualink_set_chlorinator's inferred shape, and the iQ20 schedule fallback. Every write still re-reads to confirm what happened.

This project was developed and is maintained by AI (Claude Code). Use at your own discretion.

Tools

Tool

What it does

Annotation

iaqualink_list_systems

Systems on the account and their serials

read

iaqualink_get_status

Temperatures, set points, pumps, heaters, aux/lights, chlorinator, salinity/pH/ORP (view: compact · full · raw)

read

iaqualink_healthcheck

Whether a sign-in is configured and accepted

read

iaqualink_set_equipment

Pool/spa pump, pool/spa/solar heater; eXO filter pump, production, boost, low, heating — on/off

write, not destructive

iaqualink_set_aux

Aux circuit or light on/off (by number, aux_<n> or label)

write, not destructive

iaqualink_set_light

Dimmable brightness (0/25/50/75/100) or color effect (iAqua)

write, not destructive

iaqualink_set_temperature

Pool and/or spa set point (iAqua °F/°C; eXO °C pool)

write, not destructive

iaqualink_set_chlorinator

eXO chlorinator output % — inferred shape, not in iaqualink-py

write, not destructive

iaqualink_list_schedules

RS panel programs via WebTouch (iAqua; iQ20 fallback probe); eXO timers

read

iaqualink_add_schedule

Add an RS panel program (circuit, start, stop, run days) via WebTouch

write, not destructive

iaqualink_edit_schedule

Change an RS panel program's start / stop / run days via WebTouch

write, not destructive

iaqualink_delete_schedule

Delete an RS panel program via WebTouch — no undo

write, destructive

iaqualink_set_schedule

Set / re-time / disable an eXO timer (start–stop, rpm on variable-speed pumps) — eXO only, unverified live

write, not destructive

iaqualink_check_panel

Is an RS system online, and what is its panel showing (screen, open dialog, Edit mode with unsaved changes)? Run it when the app and these tools disagree

read

iaqualink_list_pump_speeds

RS panel variable-speed pump: the named speeds, their RPM and which one runs, via WebTouch

read

iaqualink_set_pump_speed

Run the pump at a named speed via WebTouch

write, not destructive

iaqualink_set_pump_speed_rpm

Change a named speed's RPM (600–3450) via WebTouch; a speed that is not running runs briefly while set, then the running speed is restored

write, not destructive

iaqualink_list_onetouch

RS panel OneTouch scenes (Spa Mode, All OFF, …) and their state, via WebTouch

read

iaqualink_set_onetouch

Turn a OneTouch scene on/off via WebTouch — toggle not exercised live yet

write, not destructive

Systems by name. At start the server lists the account's systems once; when that works, every serial argument advertises the supported systems' names (e.g. "Pool", "Spa") as an enum, and every tool accepts a name or a serial — an unknown name is refused with the list of known systems. If the listing fails (no sign-in yet, iAqualink down, past 5 s), the argument is a plain serial and nothing else changes. Every tool also carries _meta.mint hints (domain home; lookup for reads, turn-on-off for the set_* switches, change for schedule edits) that mcp-host's context groups read and other clients ignore.

Every write has an inverse in this set and reaches no other person — except deleting a panel program, which the panel stores immediately with no undo, so that one is marked destructive.

Schedules and OneTouch on a Jandy AquaLink RS panel go through WebTouch, the panel emulation the iAqualink web app uses: the server opens the panel's display stream, presses the same buttons a person would (checking before every press that the panel is on the expected screen and row, and aborting otherwise), returns to Home, and re-reads to verify. Only one WebTouch session per system runs at a time. The add / edit / delete sequences follow a live capture on an RS panel (2026-10-11) — see docs/IAQUALINK-API.md. If WebTouch is unavailable, iaqualink_list_schedules falls back to iQ20 schedules (adapter firmware 3+), probing r-api.iaqualink.net/v2 (Bearer), p-api.iaqualink.net/v2 (Bearer), p-api.iaqualink.net/v2 (bare token) and prm.iaqualink.net/v1 (bare token) once each and reporting every attempt. iQ20 schedule writes are not built.

How a write works. It reads the current state first and sends nothing if it already matches — iAqua's pump/heater/aux commands are toggles, so a blind send would flip whatever the state happened to be. It then asks you to confirm (an elicitation prompt where the client supports one; otherwise a preview plus a confirmToken, bound to the state it was planned against), sends, and re-reads until the requested value shows up. The result is confirmed, unchanged, or sent-unconfirmed with what was observed (eXO applies changes asynchronously — check again rather than retrying).

Related MCP server: iaqualink-mcp

Setup

npm i -g @chrischall/iaqualink-mcp

Variable

Required

Meaning

IAQUALINK_EMAIL

for every tool

Your iAqualink app sign-in email

IAQUALINK_PASSWORD

for every tool

Your iAqualink password (not trimmed)

IAQUALINK_STATUS_CACHE_SECONDS

no

Seconds a status read is reused (default 5; 0 disables)

IAQUALINK_WEBTOUCH_STREAM_HOSTS

no

Extra hosts (comma-separated) the WebTouch display stream may come from; default only prm.iaqualink.net

IAQUALINK_VERIFY_TIMEOUT_MS

no

Milliseconds a write re-reads to confirm the change before reporting sent-unconfirmed, for iAqua and eXO alike (positive integer; default 15000 iAqua, 30000 eXO)

MCP_CONFIRM_MODE

no

ask-user (default) / auto / refuse

MCP_CONFIRM_TTL_SECONDS

no

confirmToken lifetime (default 600)

MCP_CONFIRM_ELICITATION

no

off skips the prompt even on clients that declare elicitation

The server starts without credentials (so hosts can list its tools) and reports them missing on the first call. A rejected password is never retried: the refusal is remembered until the server restarts, so a typo cannot lock the account.

Claude Code: claude mcp add iaqualink -e IAQUALINK_EMAIL=… -e IAQUALINK_PASSWORD=… -- npx -y @chrischall/iaqualink-mcp

Hosted (claude.ai)

mint.yaml declares per-user children with an email/password form on the connector's authorize page (each person signs in as themselves), egress to exactly prod.zodiac-io.com, r-api.iaqualink.net, p-api.iaqualink.net (iQ20 probe only) and prm.iaqualink.net (WebTouch, and the iQ20 probe); the WebTouch display stream is refused on any other host unless it is added to IAQUALINK_WEBTOUCH_STREAM_HOSTS (and egress). No persistent state — the session lives in memory and one sign-in rebuilds it after a cold start.

Skills

  • skills/iaqualink — how to use these tools.

  • skills/iaqualink-curl — the same reads straight from a shell with curl, credentials from the macOS Keychain (the sign-in, system list and iAqua reads are seen live; the eXO shadow read is still derived from iaqualink-py).

Development

npm install
npm run build
npm run test:coverage   # typecheck (src + tests) + vitest, 100% coverage — what CI runs

Tests run the real client against an in-memory iAqualink (tests/support/fake-server.ts) built from iaqualink-py's fixtures.

Available Tools

19 tools

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A4/5.0

    Scored across 19 tools

    Disambiguation4/5

    Most tools target a distinct resource+action (systems, status, schedules, OneTouch, pump speeds), and descriptions explicitly scope iAqua vs eXO behavior. Two real overlap points remain: iaqualink_set_equipment covers eXO 'production'/'boost' chlorine control while iaqualink_set_chlorinator sets output percentage, and iaqualink_set_aux turns light circuits on/off while iaqualink_set_light sets brightness/color. iaqualink_check_panel vs iaqualink_healthcheck is also a fine distinction an agent must reason about.

    Naming Consistency4/5

    All names share the iaqualink_ prefix and snake_case verb_noun structure (list_systems, get_status, set_equipment, add/edit/delete_schedule). The only deviation is iaqualink_healthcheck, which omits a verb, and the schedule tools split awkwardly across set_schedule (eXO) versus add/edit/delete_schedule (iAqua) for what is nominally one concept.

    Tool Count4/5

    Nineteen tools is on the heavy side, but the server genuinely spans two hardware families plus schedules, OneTouch scenes, VSP speeds, and diagnostics, so most tools earn their place. A few could be collapsed — set_chlorinator largely duplicates the eXO branch of set_equipment, and check_panel/healthcheck are adjacent diagnostics.

    Completeness4/5

    Coverage is strong for a control server: discovery, status, equipment/aux/light/temperature writes, full schedule CRUD for iAqua panels, OneTouch and pump-speed management, plus healthcheck and panel diagnostics. Gaps exist on the eXO side, which has list+set schedule but no add or true delete (only a disable-to-00:00), and there is no history/energy/log surface.

    Maintenance

    ActivityMaintained
    ResponsivenessLimited data

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      C
      maintenance
      Enables AI assistants to monitor and control Jandy iAqualink pool and spa equipment, including device status, power toggling, temperature settings, and lighting effects.
      MIT
    • A
      license
      Not graded
      quality
      B
      maintenance
      Enables MCP-compatible assistants to retrieve live data from an iopool pool account, including water temperature, pH, ORP, running mode, and filtration recommendations, and to list the account's pools. Access is authenticated with the user's own iopool API key, via OAuth or a direct bearer header.
      MIT
    • A
      license
      Not graded
      quality
      C
      maintenance
      Reads thermostat status and controls HVAC modes, holds, schedules, comfort sensors, settings, vacations, fan runtime, and on-screen messages for a single thermostat account, hosted remotely on Cloudflare Workers with bearer-protected, stateless access.
      96 npm
      MIT